当插入指令属性时,如何在启动时避免竞争条件?

时间:2015-03-06 22:31:38

标签: angularjs angularjs-directive race-condition

This 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}}”。但是如果我等待一小段时间(比如说,一秒钟),则会打印作为element-id属性的值传递的值(正如我所期望的那样)。

如果您在查看小提琴时打开控制台,您会看到。

我在这里做错了什么?我怎样才能避免这种情况(除了启动时很多非常丑陋的超时)?

1 个答案:

答案 0 :(得分:2)

你有几件事不正确。这是一个新的小提琴,你可以看到'at first'日志显示正确的值:http://jsfiddle.net/0mq2xv8m/

1)您应该在第二个模板中包含内部元素。您已将transclude设置为true,因此它将替换您的节点。这也确保第二个指令在第一个指令准备好之前不会绑定。即因为它与DOM指令一起位于DOM中,所以它可能与包装指令不一致地实例化。

template: '<div class="my-div" id="elementId">{{displayName}}<div my-field></div></div>'

2)执行id="elementId"代替id="{{elementId}}"传递而不是值

3)通常在父母身上捕捉属性是不好的做法,最好通过双向绑定传递它。在我使用过的任何面向显示列表的编程中都是如此。

良好做法:

您应该使用控制器或链接功能进行任何“初始化”步骤。在指令将其所有属性/范围链接起来之前,这些都不会运行。你拥有它的方式,它是在$ 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>'
}