Angularjs: understanding a recursive directive

The Ng site has some great documentation (some of the best around in my opinion). The overview of the Startup and Runtime loops are very helpful:
http://docs.angularjs.org/guide/concepts

At a high level, when Ng first starts it compiles the DOM starting at where ng-app is located (treated like just another directive by Ng). This means it goes through the elements and looks directives and expressions it needs to link up to the $rootScope (the root of all scopes that are part of the prototypical inheritance chain setup by the compiling/linking process). If it is a directive, the compile process is done on it as well. The compiling process takes all of the Ng directives it finds in the HTML and prioritizes them based on there assigned priority or assumes the priority is zero. When it has them all ordered it executes the compile function for the directive which returns the link function. In the above example there are two show link functions which I will annotate below along with other notes linking it to this explanation. the link function also is given the HTML that was in the element the directive was a attribute, class, or element on in the form of the transclude object.

The link functions are executed which links the scope and the directive along with producing a view. This may include the HTML/transclude so it can be added where the directive ng-transclude is in the template of the directive (which will have the same process applied to it with it’s template being the transclude).

So here are my notes for the slightly corrected custom directive above:

module.directive("tree", function($compile) {
    //Here is the Directive Definition Object being returned 
    //which is one of the two options for creating a custom directive
    //http://docs.angularjs.org/guide/directive
    return {
        restrict: "E",
        //We are stating here the HTML in the element the directive is applied to is going to be given to
        //the template with a ng-transclude directive to be compiled when processing the directive
        transclude: true,
        scope: {family: '='},
        template:       
            '<ul>' + 
                //Here we have one of the ng-transclude directives that will be give the HTML in the 
                //element the directive is applied to
                '<li ng-transclude></li>' +
                '<li ng-repeat="child in family.children">' +
                    //Here is another ng-transclude directive which will be given the same transclude HTML as
                    //above instance
                    //Notice that there is also another directive, 'tree', which is same type of directive this 
                    //template belongs to.  So the directive in the template will handle the ng-transclude 
                    //applied to the div as the transclude for the recursive compile call to the tree 
                    //directive.  The recursion will end when the ng-repeat above has no children to 
                    //walkthrough.  In other words, when we hit a leaf.
                    '<tree family="child"><div ng-transclude></div></tree>' +
                '</li>' +
            '</ul>',
        compile: function(tElement, tAttr, transclude) {
            //We are removing the contents/innerHTML from the element we are going to be applying the 
            //directive to and saving it to adding it below to the $compile call as the template
            var contents = tElement.contents().remove();
            var compiledContents;
            return function(scope, iElement, iAttr) {

                if(!compiledContents) {
                    //Get the link function with the contents frome top level template with 
                    //the transclude
                    compiledContents = $compile(contents, transclude);
                }
                //Call the link function to link the given scope and
                //a Clone Attach Function, http://docs.angularjs.org/api/ng.$compile :
                // "Calling the linking function returns the element of the template. 
                //    It is either the original element passed in, 
                //    or the clone of the element if the cloneAttachFn is provided."
                compiledContents(scope, function(clone, scope) {
                        //Appending the cloned template to the instance element, "iElement", 
                        //on which the directive is to used.
                         iElement.append(clone); 
                });
            };
        }
    };
});

Whole thing working:
http://jsfiddle.net/DsvX6/7/

Leave a Comment