14

據我注意到,Angular以先到先得的方式編譯了一些東西,這很棘手。我做了一個包裝一些元素的指令,我想要一個鏈接屬性來查找內容中的東西。AngularJS編譯指令的更改順序

對於一個具體的用例:我想提出一個輸入標籤指令,它查找第一個輸入裏面的內容,並增加了一個隨機生成的idinputfor屬性爲label

下面的代碼:

// Find the first element with the attribute ng-label-target or the first input and links a label to it 
app.directive('ngLabel', function() { 
    return { 
    restrict: 'E', 
    replace: true, 
    transclude: true, 
    scope: { 
     label: '@', 
    }, 
    template: '<span class="ng-label">' + 
       '<label class="ng-label-text">{{label}}</label>' + 
       '<span class="ng-label-content" ng-transclude></span>' + 
       '</span>', 
    link: function (scope, element, attrs) { 
     scope.id = scope.id || 'random-id-' + Math.floor(Math.random() * 90000000); 
     angular.element(element[0].querySelector('.ng-label-text')). 
     attr({for:scope.id}); 

     var target = angular.element(element[0].querySelector('[ng-label-target]')); 
     if (!target || target.length == 0) { 
     target = angular.element(element[0].querySelectorAll('input,select,textarea')[0]); 
     } 
     target.attr({id:scope.id}); 
    }, 
    }; 
}); 

實例應用:

<ng-label label="Text:"> 
    <input type="text" ng-model="page.textColor" size="5" maxlength="7" placeholder="e.g. #000" required /> 
    <input ng-label-target type="color" ng-model="page.textColor" required /> 
</ng-label> 

這本身就像一種魅力。

但是現在我想自動生成幾個輸入並將標籤指向第一個。問題是,當我在ng-label內部執行ng-repeat時,ng-repeat代碼在處理我的指令後生成,因此沒有找到任何實際的代碼。

因此,我的問題是:有沒有一種方法可以指定角來評估嵌套指令而不是其他方式?

或者,有沒有比我目前這樣做的連擊方式?

我做了一個小提琴來舉例說明評估東西的順序。你看到外指令有一個小於或等於價值超過了它的內容(不能再低於微秒,所以我不得不做了一堆重複的):

http://jsfiddle.net/YLM9P/

+0

肯定看起來比需要複雜得多...演示提供了太多的遞歸,使用您的指令和標記提供演示。你可能會發現更容易創建角度的演示,而不是小提琴 – charlietfl

+0

爲什麼'id'需要是隨機的?你不能只使用'id =「name _ {{$ index}}」'和標籤相同嗎? –

+0

我不想指定ID,因爲我通常不需要它,這就是爲什麼它是隨機的,如果它沒有指定 – Stefan

回答

18

從角文檔:

PRIORITY 當存在單個DOM元素上定義的多個指示,有時有必要指定其中應用所述指令的順序。優先級用於在調用編譯函數之前對指令進行排序。優先級被定義爲一個數字。首先編譯具有更高數字優先級的指令。前鏈接功能也按優先順序運行,但後鏈接功能按相反順序運行。具有相同優先級的指令的順序未定義。默認優先級爲0。

TERMINAL 如果設置爲true,那麼當前的首要任務將是最後一組將執行(在當前的優先級任何指示仍將執行作爲執行對相同的優先級順序指令未定義)。

因此,對於您的問題,我相信將終端屬性設置爲true將解決您的問題。

app.directive('ngLabel', function() { 
    return { 
    restrict: 'E', 
    replace: true, 
    transclude: true, 
    .... 
    .... 
    terminal: true 
}); 
+1

看起來像優先級較低的指令先執行。 – Ghigo

0

我想你可以大大簡化通過在父元素上添加一個函數表達式(可能傳入一個參數),然後從ng-repeat內的子元素中調用該函數,從而消除querySelectorAll(不是非常的Angular-esque)。該函數將簡單地設置一個值,你會利用它,一旦它被設置,那麼你知道你有第一個,其餘的應該被忽略。如果你展示了ng-repeat輸入字段的最終HTML應該是什麼樣子,我可以更具體些。

0

您可以嘗試註冊偵聽添加到DOM的元素的MutationObserver。但是,這可能是一個小的開銷,以你的問題 - 你決定;)

var observer = new MutationObserver(function(mutations) { 
    mutations.forEach(function(mutation) { 
    // you get notified about added DOM elements here 
    // check mutation.type if it was an insertion 
    // then you handle the first node specifically. 
    console.log(mutation.target.nodeName, mutation.type, mutation); 
    }); 
}); 

var config = {childList: true, attributes: false, characterData: false, subtree: false, attributeOldValue: false, characterDataOldValue: false}; 
observer.observe(element[0], config); 
 
Demo    http://plnkr.co/h6kTtq 
Documentation  https://developer.mozilla.org/en/docs/Web/API/MutationObserver 
Browser support http://caniuse.com/mutationobserver 
1

這個問題,許多人一樣的角度,可以得到額外的指令來解決:

app.directive('myLabelTarget', function() { 
return { 
    require: '^myLabel', 
    link: function(scope, element, attrs, myLabelCtrl) { 
    myLabelCtrl.doIfFirst(function(id) { 
     attrs.$set('id', id); 
    }); 
    } 
}; 
}); 

使用require屬性,您可以訪問DOM中較高層的myLabel指令的控制器。

查看此plnkr作爲一個工作示例。