Grant
Grant

Reputation: 11376

How to transclude ng-repeat from directive

I am trying to dynamically create a table in an angular directive. Following table creation i wish to populate the table with unknown data from my scope model. The tricky part of this is that i am not able to create my ng-repeat until after the linking function has built my table structure. I was hoping i could use the transclude function to accomplish this but it is not working and nothing is happening. all i see in my table is {{row.field0}} instead of the actual data value.

PS: the code sample below is in typescript.

Markup:

<table table-maker="" column-count="model.getColumnCount()" row-data="model.rowData">
</table>

Directive:

/// <reference path="../_references.ts" />

module Directives {

    export interface ITableMakerScope extends ng.IScope {
        columnCount: number;
        rowData: any[];
    }

    export class TableMakerDirective {

        public link: Function;
        public transclude: boolean = true;
        public scope: any = {
            columnCount: '=',
            rowData: '='
        }

        private _scope: ITableMakerScope;
        private _element: JQuery;

        constructor() {
            this.link = this.internalLink.bind(this);
        }

        public internalLink(scope: ITableMakerScope, element: JQuery, attrs, ctrl): void {

            this._scope = scope;
            this._element = element;

            var stopWatch = scope.$watch('columnCount', (newValue) => {
                if (newValue) {
                    this.buildTable(newValue);
                    stopWatch();
                }
            });

        }

        private buildTable(columnCount: number): void {

            var headerRow = $('<tr>');
            var templateRow = $('<tr ng-repeat="row in rowData" ng-transclude="">'); // <-- angular

            for (var i = 0; i < columnCount; i++) {

                var th = $('<th>');
                th.text('Column {0}'.format((i + 1).padLeft(1)));
                headerRow.append(th);

                var td = $('<td>');
                td.text('{{row.field{0}}}'.format(i)); // <-- angular
                templateRow.append(td);

            }

            var thead = $('<thead>');
            thead.append(headerRow);
            this._element.append(thead);

            var tbody = $('<tbody>');
            tbody.append(templateRow);
            this._element.append(tbody);

        }

    }

}

Is this the right way to do this?

Thanks.

Upvotes: 0

Views: 1322

Answers (1)

Sly_cardinal
Sly_cardinal

Reputation: 13043

Use the template or templateUrl property on the directive to hold your template information.

See the Angular documentation on directives

To pre-process the data I would do something like this (based on your example):

angular.module('moduleName').directive('tableMaker', function(){
    return {
        replace: true,
        scope: {
            columnCount: '=',
            rowData: '='
        },
        link: function(scope, element, attr){
            var columnCount = null;
            var rowData = null;

            // Set up watchers to detect changes to both properties.
            scope.$watch('columnCount', function(value){
                columnCount = value;
                updateTable()
            });

            scope.$watchCollection('rowData', function(value){
                rowData = value;
                updateTable()
            });


            // This makes sure that we have both the columnCount and data before
            // generating the table data.
            function updateTable(){
                var tableData = null;
                if (columnCount && data){
                    tableData = buildTable(columnCount, data);
                }
                scope.tableData = tableData;
            }


            // Generate the data that will be used to create the table.
            // Adjust as needed.
            function buildTable(columnCount, rowData){
                var tableData = {
                    headers: [],
                    rowData: rowData,
                    cellData: []
                };

                var headers = tableData.headers;
                var cellData = tableData.cellData;

                for (var i = 0; i < columnCount; i++){
                    headers.push('Column {0}'.format((i + 1).padLeft(1)));
                    cellData.push('field'+i);
                }

                return tableData;
            }
        },

        template:
        '<table>'+
            '<thead>'+
                '<tr>'+
                    '<th ng-repeat="header in tableData.headers">{{header}}</th>'+
                '</tr>'+
            '</thead>'+
            '<tbody>'+
                '<tr ng-repeat="row in tableData.rowData">'+
                    '<td ng-repeat="cell in tableData.cellData">{{row[cell]}}</td>'+
                '</tr>'+
            '</tbody>'+
        '</table>'
    };
});

Upvotes: 1

Related Questions