порядок выполнения функции ссылки директивы

Я новичок во всем этом угловатом...

У меня есть пользовательская директива, назовем ее myDar. Внутри этой директивы я определяю функцию ссылки. В моем html я хочу использовать несколько вложенных тегов этой директивы следующим образом:

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

Я хочу, чтобы функция ссылки «внешнего» выполнялась первой. Как мне это сделать?

Это общий вопрос. Если это поможет, то я действительно пытаюсь создать директивы, которые обертывают макет пользовательского интерфейса jquery (ссылка на веб-сайт) . Итак, у меня есть директива для «ui-layout» и директивы для «центра», «запада» и т. д. В директиве «ui-layout» я вызываю $(tElm).layout(options). У меня проблема при создании вложенного макета:

<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 сначала выполняет функцию ссылки внутренней директивы «ui-layout», но для работы плагина jquery ui layout требуется сначала вызвать $(tElm).layout(options) внешнего, иначе макет не будет отображаться правильно.


person julius_am    schedule 15.01.2014    source источник
comment
Не могли бы вы подробно объяснить, что вам нужно запустить, в какой функции связывания?   -  person Ilan Frumer    schedule 15.01.2014
comment
Кстати, angular.module('app').directive('myDar' ... зарегистрирует myDar как змеиный регистр в html <my-dar>...</my-dar>   -  person Nate-Wilkins    schedule 27.02.2014


Ответы (2)


Для этого вы воспользуетесь контроллером директивы. Это будет класс, определяющий метод для регистрации вложенных контроллеров и еще один для выполнения нужной команды (здесь $(...).layout(...)) для этого элемента и затем для всех его дочерних элементов. Это означает, что внешняя директива отвечает за координацию создания макетов.

Полный код примера:

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();
        }
    };
});

Замените строку console.log("PAYLOAD: " + this.$element.attr("id")); фактическим кодом для запуска. См. соответствующую скрипку: http://jsfiddle.net/8xSjZ/

Если бы внешняя директива отличалась от текущей, получить родительский контроллер было бы так же просто, как потребовать "?^y". В этом случае он дает нам текущий контроллер, поэтому мы должны сами зациклиться (e.parent()).

person Nikos Paraskevopoulos    schedule 15.01.2014
comment
Отличный ответ. Один вопрос: как работает e.controller(y)? Это напрямую вызывает конструктор класса Controller? - person Faredoon; 25.08.2014
comment
Привет спасибо. На самом деле e.controller() является расширением Angular для jqLite (или jQuery). Это задокументировано здесь - ищите jQuery/jqLite Extras. Вкратце, он находит существующий контроллер с заданным именем, прикрепленный к элементу. Таким образом, он не вызывает конструктор, он ожидает, что контроллер уже создан и присоединен к элементу. - person Nikos Paraskevopoulos; 26.08.2014
comment
Спасибо. Очень признателен. - person Faredoon; 26.08.2014
comment
Еще один вопрос: учтите, что у меня нет вложенной директивы, поэтому у меня есть что-то вроде ‹div level=1 id=a y=›, за которым следует другой ‹div id=other level=2 y=›. При обработке первого div в функции ссылки я хотел бы получить доступ ко второму div, используя angular.element(div[level='2']), а затем к контроллеру, используя .controller(y). См. этот plunkr: plnkr.co/edit/oIpvQP. Я получаю исключение JQLite Поиск элементов с помощью селекторов не поддерживается jqLite! См.: docs.angularjs.org/api/angular.element. - person Faredoon; 27.08.2014
comment
Для этого вам нужно будет включить jQuery, чтобы Angular использовал jQuery вместо jQlite. jQlite — это реализация урезанной версии API jQuery. Он не поддерживает такой селектор (например, "div[level='2']"). Я полагаю, должен быть какой-то другой способ сделать это, реорганизовав вашу логику. - person Nikos Paraskevopoulos; 27.08.2014

В зависимости от конкретного варианта использования может подойти функция compile. compile функции выполняются перед link функциями и, что более важно, в обратном порядке. Значение родителя в первую очередь.

app.directive("ui-layout", function() {
  return {
    restrict: 'E',
    compile: function(element, attrs) {
               //do layout stuff here
               //return a link function or nothing

Другим решением может быть выполнение соответствующего кода ваших дочерних директив с помощью $timeout.

app.directive("ui-layout-center", function($timeout) {
  return {
    restrict: 'E',
    link: function(scope, element, attrs) {
            $timeout(function() {
              //do layout stuff later
             }, 0);  
person a better oliver    schedule 15.01.2014
comment
Нет-секундный $timeout помог мне, поскольку он откладывается до следующего цикла дайджеста. - person Soviut; 19.03.2014