0

I have a custom directive myItems that is rendering a list of items using another directive myItem via ng-repeat:

app.directive('myItems', function() {
    return {
        scope:{items:"="},
        restrict:"E",
        template:"<div my-item ng-repeat='item in items'></div>"
    };
});

Each item contains information (such as a color) that has be used for styling the elements. This works fine so far if I use the templating approach:

app.directive('myItem', function() {
    return {
        restrict:"A",
        template:"<div ng-style='{color:item.color}'>myItem: {{item.name}}</div>",
        replace:true
    };
});

To support more involved features, I now want to rebuild the same functionality if my-item by manually creating all the needed stuff including the ng-style:

app.directive('myItem', function($compile) {
    return {
        restrict:"A",
        compile: function(element) {
            element.removeAttr('my-item');
            element.text('myItem: {{item.name}}');
            element.attr('ng-style', '{color:item.color}');    // Why does it have no effect?

            var compileElement = $compile(element);
            return function link(scope, elem, attr) {
                compileElement(scope);    // Why does binding with {{item.name}} even work without this statement?
            };
        }
    };
});

Interestingly enough databinding using {{item.name}} works while ng-style does not.

Here is a fiddle showing both approaches described above.

I am aware of the similar question. But I think it does not apply here since I have no explicit statements about the scope of the my-item directive.

Community
  • 1
  • 1
paulroho
  • 1,234
  • 1
  • 11
  • 27

2 Answers2

2

There is a catch with the compile function: it is called for the directives of an element, after Angular has "parsed" that element. This means that any directive added from the code of the compile() function to the element containing the directive being compiled will not be parsed by Angular! This is why element.attr('ng-style', '{color:item.color}'); is disregarded.

On the other hand, the compileElement(scope); is superfluous; any DOM manipulation done inside the element containing the directive being compiled, will be parsed by Angular.

What can you do? Your situation is easy, it is a trivial DOM manipulation to add the style with a watch (even without the watch, if you do not expect the color to change):

return function link(scope, elem, attr) {
    scope.$watch('item.color', function(newval) {
        elem.css('color', newval);
    });
};
Nikos Paraskevopoulos
  • 39,514
  • 12
  • 85
  • 90
  • Thank you. Although I have hoped to be able to stick to an imperative way of databinding, I understand the reasoning and will go down that path. – paulroho Mar 07 '15 at 11:32
0

Calling compileElement does compile the ng-style element, but because $compile was called before the element was fully constructed, calling compileElement worked on a invalid version of the element and ng-style didn't changed the color.

So you can move the $compile inside the link function:

var compileElement = $compile(elem);
compileElement(scope);

But then it also compiles again the ng-repeat directive which is no good. So you need to remove that attribute as well:

element.removeAttr('ng-repeat');

Check this plunker.

eladcon
  • 5,815
  • 1
  • 16
  • 19