What's the best practice to add ngIf to a directive programmatically?

2,831 views
Skip to first unread message

Volker Tietz

unread,
Dec 2, 2013, 5:04:42 AM12/2/13
to ang...@googlegroups.com
I want to create a directive that checks if an element should be present in the dom based on a value coming from a service (e.g. check for a user role).

The corresponding directive looks like this:

    angular.module('app', []).directive('addCondition', function($rootScope) {
        return {
            restrict: 'A',
            compile: function (element, attr) {
              var ngIf = attr.ngIf,
                  value = $rootScope.$eval(attr.addCondition);
    
              /**
               * Make sure to combine with existing ngIf!
               */
              if (ngIf) {
                value += ' && ' + ngIf;
              }
    
              attr.$set('ng-if', value);
            }
        };
    });

At the end the element has the ng-if attribute attached but somehow it doesn't apply to the element and it is still existing in the dom. So this is obviously a wrong approach. 

This fiddle shows the problem: http://jsfiddle.net/L37tZ/2/

Who can explain why this happens? Is there any other way a similar behaviour could be achieved? Existing ngIfs should be considered.

Daniel Tabuenca

unread,
Dec 2, 2013, 1:22:10 PM12/2/13
to ang...@googlegroups.com

There are quite a few problems with this code. The biggest is that there is no link function.

On the compile function there is no scope available. You are using $rootScope there, but this is usually incorrect behavior since your directive will not be able to use any data in any other scope other than $rootScope.

The purpose of the link function is to combine a scope’s data with your directive’s logic. So anything that accesses scope should be in the link function.

In fact it’s almost always wrong to put things anywhere other than the link function. People read that you can modify the dom within the compile, but that’s only partially true. You cannot modify or add directives to it like you are doing, because by this time it is too late.

The way angular works is that it takes a given piece of dom and scans it for directives, building a list of all the directives on that dom. It then takes this list and compiles each directive. Compile just returns the link function. Angular then goes through all the link functions and calls them with the propper scope so that they can setup the directive’s watches, or do whatever they need to do.

If you add an ng-if in your directive’s compile it will not get compiled since by this time the dom has already been scanned for any directives and the list of directives created. It will not scan again and so it won’t ever find your ng-if.

You would have to make sure to compile the new ng-if yourself, but this can be tricky.

I would suggest you reconsider why you need this behavior? If you need to add a condition why not just add it to the existing ng-if?

Rafael Nami

unread,
Dec 2, 2013, 3:02:01 PM12/2/13
to ang...@googlegroups.com
Excellent explanation Daniel, thanks a lot!

Cheers, Rafa


2013/12/3 Daniel Tabuenca <dtab...@gmail.com>

--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular+u...@googlegroups.com.
To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/groups/opt_out.

Volker Tietz

unread,
Dec 3, 2013, 7:28:12 AM12/3/13
to ang...@googlegroups.com
Thanks for the explanation! 

I managed to achieve this behaviour by using the same implementation as for ngIf. See my post on SO for the solution.

Reply all
Reply to author
Forward
0 new messages