Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why AngularJS directive compiling not support nested directives

Tags:

angularjs

In my understanding, $compile should be able to support nested directive compilation/link, but we came across an issue that the compilation/link is incomplete - only the outermost directive got rendered into DOM, and the issue only reproduced when both below conditions are true:

  1. The inner directive template is loaded via templateUrl (e.g. async manner)
  2. The compilation is triggered outside Angular context.

I wrote a jsfiddler to demo it, part code listed below, for complete case http://jsfiddle.net/pattern/7KjWP/

myApp.directive('plTest', function($compile){
return {
    restrict :'A',
    scope: {},
    replace: true,
    template: '<div>plTest rendered </div>',
    link: function (scope, element){
        $('#button1').on('click', function(){
            var ele;
            ele = $compile('<div pl-shared />')(scope); 
            console.log('plTest compile got : '+ ele[0].outerHTML);
           // scope.$apply();
            element.append(ele);
        });
     }
};
});

myApp.directive('plShared', function($compile, $timeout){
return {
    restrict: 'A',
    scope: {},
    replace: true,
    link: function (scope, element){
        // comment out below line to make render success
        //$timeout(function(){});
        var el = $compile('<div pl-item></div>')(scope);
        console.log('plShared compile got:' + el[0].outerHTML);
        element.append(el);
    }
};
});

myApp.directive('plItem', function($timeout){
return {
    restrict: 'A',
    scope:{},
    template:'<div>plItem rendered <div pl-avatar/></div>',
    link: function(scope){            

    }
};
});

myApp.directive('plAvatar', function(){
return {
    restrict: 'A',
    scope: {}
   , templateUrl: 'avatar.html'
  //  ,template: 'content of avatar.html <div pl-image></div>'
};
});

Interestingly, i can workaround the issue by either calling scope.$apply() somewhere after compile() call (line 27) or adding $timeout(function(){}) call into the link func of one of inner directive (line 41). Is this a defect or by design?

like image 210
Roger Jin Avatar asked Jan 31 '26 10:01

Roger Jin


1 Answers

$(foo).on(bar, handler) is a jQuery event, which means AngularJS does not know the specifics of it, and will not (can not) run an apply-digest cycle after it to process all the bindings.

scope.$apply was made for this, and as you rightly say, fixes it. The rule of thumb is: if you implement UI functionality in an AngularJS application using other libraries (specifically: outside of apply-digest cycles), you must call scope.$apply yourself.

HTH!

like image 85
Steve Klösters Avatar answered Feb 02 '26 03:02

Steve Klösters



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!