Search code examples
javascriptangularjsangular-directive

Custom Directive for Table with different object properties


Suppose I would like to write a reusable + universal directive that will be used throughout my whole project. This directive will be displaying a table, as follows:

angular
    .module('demo')
    .directive('indexTable', function() {
        return {
            restrict: 'A',
            template: '<tr ng-repeat=" component in components>' +
                        '<td ng-repeat="obj in component">' +
                          '{{obj}}' + 
                        '</td>' + 
                       '</tr>',
            scope: {
                 components: '=',
            },
            link: function (scope, attrs) {
            }
        }
    });

Supposing in my controller I have two different arrays with each two sets of different objects with different properties:

//some mockup data
angular.module('demo')
   .controller('demoCtrl',['$scope',function($scope){
    //array 1
    $scope.userArray = [
      {Id:1 , Name: 'Chuck Norris', Age:'21'},
      {Id:2 , Name: 'George Bush' , Age: '42'}
    ];
    //array 2 
    $scope.itemArray = [
      {Id:1, ProductNo: '001', BasePrice: 20.20, Value: 50.50} 
      {Id:2, ProductNo: '002', BasePrice: 20.20, Value: 50.50} 
    ];
}]);

So basically the question is: How do I select (in controller) what are the properties to be displayed in the table?

Question In-Depth : Now I have two different arrays with each of their own properties. How I would use it in my HTML will be

<div index-table components="userArray"></div>

Take the itemArray for example. Each object will have 4 properties, namely Id, ProductNo, etc. But in my table, I would only want to display 2 of which, say only ProductNo and BasePrice. How do I discard the other two properties that I do not want? As you can see from my partial template there, I am using double ng-repeats.

Things I have considered / tried so far: Tried Mapping objects to array, but I believe ng-repeat is far more intelligent. Do I need to add in more scope attributes? How do I write my link function? Any ideas?


Solution

  • You can pass an attribute defining the property names that will populate each column. E.g.:

    <table index-table
        components="itemArray"
        columns="ProductNo,BasePrice"
    ></table>
    

    Your directive has to be slightly modified:

    app.directive('indexTable', function() {
        function parseProps(str) {
            // implement parse logic, return an array of strings - the property names
        }
    
        return {
            restrict: 'A',
            template:
                '<tr ng-repeat="component in components">' +
                    '<td ng-repeat="name in props">' +
                        '{{ component[name] }}' + 
                    '</td>' + 
                '</tr>',
            scope: {
                components: '='
            },
            link: function (scope, elem, attrs) {
                scope.props = parseProps(attrs.columns);
            }
        };
    });
    

    You can see a sample implementation here: http://jsfiddle.net/y54x0hcd/

    This has a lot of rough edges (e.g.: column titles?); maybe it would be better to use a grid library.