这个fiddle应该会让事情变得更清楚,但本质上我在它的指令中分配了元素的一些属性(比如它的id)参数:
myApp.directive('myDiv', function () {
return {
restrict: 'E',
replace: true,
scope: {
'elementId': '@',
'displayName': '@',
},
transclude: true,
template: '<div class="my-div" id="{{elementId}}">{{displayName}}<div ng-transclude></div></div>'
}
})
问题是,如果我在启动时立即执行一些操作,比如初始化其他指令,那么这些值(例如elementId)还没有插值。
换句话说,如果我获得对myDiv元素的引用并打印其id,则在启动时立即打印"{{elementId}}"。但是,如果我等待很短的时间(比如一秒钟),那么将打印作为值传递给元素id属性的值(正如我所期望的)。
如果你在观看Fiddle时打开控制台,你会看到的。
我在这里做错了什么?我该如何避免这种情况(除了在启动时出现很多非常糟糕的超时)?
您有几点不正确。这里有一个新的fiddle,您可以在其中看到显示正确值的"最初"日志:http://jsfiddle.net/0mq2xv8m/
1) 您应该在第二个的模板中包含内部元素。您已将transclude设置为true,因此它将替换您的节点。这也确保了第二个指令在第一个指令准备好之前不会绑定。也就是说,由于它与outter指令一起在DOM中,因此它可能与包装指令实例化不同步。
template: '<div class="my-div" id="elementId">{{displayName}}<div my-field></div></div>'
2) 执行id="elementId"
而不是id="{{elementId}}"
以通过引用而不是值
3) 通常,在父对象上查找属性是不好的做法,最好通过双向绑定将其传入。这在我使用过的任何面向显示列表的编程中都适用。
良好实践:
您应该为任何"init"步骤使用控制器或链接函数。在指令链接了所有属性/作用域之前,这些函数不会运行。按照您的方式,它在$scope创建期间的评估步骤中执行(目前还没有作用域)。链接函数和控制器等待$scope可用。可以使用控制器来代替链接功能(我认为它更清晰,更容易进行单元测试)。
angular.module('App').controller('someController',[], function() {
var controller = {
init:function(){
console.log(elementId);
}
}
controller.init();
return controller;
});
myApp.directive('myDiv', function () {
return {
restrict: 'E',
replace: true,
controller:'someController',
scope: {
'elementId': '@',
'displayName': '@',
},
transclude: true,
template: '<div class="my-div" id="{{elementId}}">{{displayName}}<div ng-transclude></div></div>'
}