Search code examples
angularjsangularjs-directivejquery-ui-layout

directive link function execution order


I'm kind of new in all this angular...

I have a custom directive, lets call it myDar. Inside this directive I define a link function. In my html I want to to use multiple nested tags of this directive as follows:

<myDar id="outer"><myDar id="inner"></myDar></myDar>

I want the link function of "outer" to be executed first. How do I do this?

That's the general question. If it helps then what I'm really trying to do is to create directives that wrap jquery ui layout (link to the website). So I have a directive for "ui-layout" and directives for "center", "west" etc. In the "ui-layout" directive I call to $(tElm).layout(options). I have a problem when creating a nested layout:

<ui-layout class="layout-container">
    <ui-layout-center>
        <ui-layout>
            <ui-layout-center>inner center</ui-layout-center>
            <ui-layout-west>inner west</ui-layout-west>
        </ui-layout>        
    </ui-layout-center>
    <ui-layout-west>west</ui-layout-west>
</ui-layout>

Angular executes first the link function of the inner "ui-layout" directive but for the jquery ui layout plugin to work it requires to call $(tElm).layout(options) of the outer first, otherwise the layout is not rendered correctly.


Solution

  • For this you will take advantage of the directive's controller. It will be a class defining a method to register nested controllers and another one for executing the desired command (here $(...).layout(...)) on this element and then on all of its children. This means that the outer directive is responsible for coordinating the creation of the layouts.

    The full example code is:

    app.directive("y", function() {
        function Controller($element) {
            this.$element = $element;
            this.children = [];
        }
    
        Controller.prototype.register = function(child) {
            this.children.push(child);
        };
    
        Controller.prototype.execute = function() {
            console.log("PAYLOAD: " + this.$element.attr("id"));
            for( var i=0; i < this.children.length; i++ ) {
                this.children[i].execute();
            }
        };
    
        return {
            require: "y",
            controller: ["$element", Controller],
            link: function(scope, element, attrs, ctrl) {
                var e = element.parent(), nested = false;
                while( e != null ) {
                    if( e.controller("y") != null ) {
                        e.controller("y").register(ctrl);
                        nested = true;
                        break;
                    }
                    e = e.parent();
                    if( typeof(e.tagName) === "undefined" ) break; //XXX Needed, at least for fiddle
                }
                if( !nested ) ctrl.execute();
            }
        };
    });
    

    Replace the line console.log("PAYLOAD: " + this.$element.attr("id")); with the actual code to run. See relevant fiddle: http://jsfiddle.net/8xSjZ/

    If the outer directive was different than the current, getting the parent controller would be as easy as requiring "?^y". In this case, it gives us the current controller, therefore we have to loop (e.parent()) ourselfes.