0

i have some kind of legacy angularjs code which creates a dynamic table using a directive where the controller can overwrite the behavior of the table (on how to display the data)

It consists of the following setup (simplified):

Directive's controller

.directive('datatable', [function () {
    return {
        scope: {
            items: '=',
            tablemetadata: '=',
            processors: '=?'
        },
        controller: ...
        $scope.processField = function processField(item, data){
        if($scope.processors === undefined){return;}
            for(var i = 0; i < $scope.processors.length; i++){
                if($scope.processors[i].field===field){
                    var newData = $scope.processors[i].processor(item, data);
                    return $sce.trustAsHtml(newData);
                }
            }
            return data;
        };
    ...

Directive's Template

<tr ng-repeat="item in items">
    <td ng-repeat="column in tableMetadata.columns" ng-bind-html="processField(column.field, $eval('item.'+column.field))"></td>
</tr>

Controller

$scope.myItems = [{id: 2, otherProperty: "text"}];

$scope.tableMetadata = {
    columns: [
        {field: 'id', headerKey: 'object id'},
        {field: 'otherProperty', headerKey: 'some data'},
    ]
};

$scope.tableProcessors = [
    {field: 'id', processor: function(entry, data){ //data = content of object.id
        var retVal = "<a ng-click='alert(" + data + ");'>click me</a>";
        return retVal;
    }}
];

Controller's view

<datatable items="myItems" tablemetadata="tableMetadata" processors="tableProcessors"></datatable>

I need to generate buttons (or other html-elements) for some specific properties, like a link (like shown above).

The Button is displayed but the ng-click handler is not working. This makes sense since it wasn't compiled to the scope.

How do I correctly compile the new element and add it to the table?

2 Answers 2

1

In your link method in the directive you have to use

elem.append( $compile(html)(scope) );

As for separating the concerns cleanly, I would make each <td> its own directive that inherits what you are currently concatenating as a string in its isolated scope properties. Instead of

var retVal = "<a ng-click='alert(" + data + ");'>click me</a>";

<tr ng-repeat="item in items">
    <td ng-repeat="column in tableMetadata.columns" ng-bind-html="processField(column.field, $eval('item.'+column.field))"></td>
</tr>

use something like:

<tr ng-repeat="item in items">
  <table-item ng-repeat="..." process-field="item"></table-item>
</tr>

/** directive compiles dynamically */

scope: {
  processField: '='
},
link: function(scope, elem, attr, ctrl) {
  var template = `<a ng-click="${ctrl.processField}"></a>`;
  elem.append( $compile(template)(scope) );
}
Sign up to request clarification or add additional context in comments.

Comments

1

A simple solution can be to not use an isolated scope. Change your scope from scope: { ... } to scope: true and use $scope.$eval to evaluate your attributes.

Another solution (most elegant) can be to use angularjs transclusion (see here). But this solution ask to modify your dom representation of your directive.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.