[英]angularJS: wait for template to be evaluated before directive loads
情況
假設我有一個指令,它必須通過ID在定義指令的元素內訪問某些元素。 可能出現的問題是,在評估指令時,子元素還沒有。 結果是,我無法通過其ID訪問這些元素。
例
<div ng-controller="MyCtrl">
<div color="elementId">
<div ng-repeat="item in items" id="{{ item.id }}">
{{ item.name }}
</div>
</div>
</div>
<script>
var myApp = angular.module('myApp',[]);
myApp.directive("color", function () {
return {
restrict: "A",
link: function (scope, element, attributes) {
var name = attributes.color,
el = element[0];
scope.$watch(name, function () {
var id = scope[name];
console.log(id); //id1
console.log(element.children().eq(0).attr("id")); //{{ item.id }}
element.find("#"+id).css("background-color","red");
});
}
};
});
function MyCtrl($scope) {
$scope.items = [
{ id:"id1", name:"item1" },
{ id:"id2", name:"item2" }
];
$scope.elementId="id1";
}
</script>
因此,我的指令應該只使用$scope.elementId
的id $scope.elementId
元素的背景顏色。 (順便說一句。我知道我可以更輕松地處理這個簡單的例子,它應該只是說明一般問題)。 問題是,ng-repeat中的元素的id還沒有。 正如代碼中的注釋所指出的那樣,id仍然是“{{item.id}}”。 因此,角度尚未評估此部分。
題
我現在明顯的問題是:如何讓我的指令等待后代元素被完全評估?
進一步解釋
在我的實際應用程序中,我希望有一個指令,使我能夠滾動到頁面上的某些元素。 我還使用分頁指令來分割我想要顯示的元素。 由於分頁,只有真正可見的元素在DOM中,因此隱形元素已經在我的控制器中被過濾掉了。
我還有一個側邊欄,其中有所有元素的小鏈接(不僅是可見元素)。 當有人點擊側邊欄中的元素時,應該發生兩個事件:
當我跳到頁面時,我基本上都有這種情況,我在上面描述過。 我有一個完整的新元素列表,必須由ng-repeat處理。 但是在那之后,我嘗試告訴我的scroll-directive,它應該使用ID“xy”滾動元素,但是這個ID還沒有分配。
用$ timeout包裝$ scope.elementId =“Id1”以通知angular調用偵聽器。 (也可以使用$ scope。$ apply()來完成,但這會導致另一個問題)
代碼是 -
var myApp = angular.module('myApp',[]);
myApp.directive("color", ['$timeout', function ($timeout) {
return {
restrict: "A",
link: function (scope, element, attributes) {
console.log(element)
var name = attributes.color,
el = element[0];
scope.$watch(name, function () {
var id = scope[name];
console.log(id); //id1
console.log(element.find("#"+id)); //{{ item.id }}
element.find("#"+id).css("background-color","red");
});
}
};
}]);
myApp.controller("MyCtrl", function($scope, $timeout) {
$scope.items = [
{ id:"id1", name:"item1" },
{ id:"id2", name:"item2" }
];
$timeout(function() {
$scope.elementId="id1";
});
});
如果最終編寫了一個getElementById
輔助函數,它返回一個promise並有一個內部間隔,如果該元素存在與否則每100ms檢查一次:
function getElementById(elementId) {
var deferred = $q.defer(),
intervalKey,
counter = 0,
maxIterations = 50;
intervalKey = setInterval(function () {
var element = document.getElementById(elementId);
if (element) {
deferred.resolve(element);
clearInterval(intervalKey);
} else if (counter >= maxIterations) {
deferred.reject("no element found");
clearInterval(intervalKey);
}
counter++;
}, 100);
return deferred.promise;
}
在我給出的例子中,我會像這樣使用它:
getElementById(id).then(function (element) {
$(element).css("background-color","red");
}, function (message) {
console.log(message);
});
它仍然不是我的首選解決方案,但它現在可以解決我的問題。 但我仍然很好奇,如果有更好的方法。
根據Jim Hoskins的文章,以下代碼段可以幫助您。
scope.$watch(name, function () {
setTimeout(function () {
scope.$apply(function () {
var id = scope[name];
console.log(id); //id1
console.log(element.find("#"+id)); //{{ item.id }}
element.find("#"+id).css("background-color","red");
}
}, 200))
});
發布此答案可幫助人們節省一些時間(當然,閱讀完整文章會很有幫助)
您應該完成指令,包括controller
選項。
controller: function ($scope){
$scope.items = [
{ id:"id1", name:"item1" },
{ id:"id2", name:"item2" }
];
}
這將在控制器范圍內創建所有內容,然后您可以從使用此指令的視圖的控制器訪問它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.