从指令更新指令上的ngModel

时间:2014-07-03 00:53:10

标签: javascript angularjs angularjs-directive angularjs-scope

我无法理解为什么对ngModel的更改不会从一个指令传播到另一个指令。 Here's a plunker that shows a simplified version of what we're trying to do

基本上,我已经声明了一个使用ngModel和一个隔离范围的指令:

.directive('echo', function() {
    var link = function(scope, element, attrs, ngModel) {
        // --- 8< ---
        scope.$watch(attrs['ngModel'], function() {
            scope.model = ngModel.$modelValue;
            console.log("***** Echo model updated: ", scope.model);
        });
    };

    return {
        restrict: 'E',
        require: 'ngModel',
        link: link,
        scope: {
            id: "="
        }
    }
})

然后该指令被另一个指令包装,同时依赖于ngModel和一个隔离范围:

.directive('wrapper', function() {
    var link = function(scope, element, attrs, ngModel) {
        scope.$watch(attrs['ngModel'], function() {
            var model = ngModel.$modelValue;
            console.log("----- Wrapper model updated", model);

            scope.model = model;
        })
    };

    return {
        restrict: 'E',
        require: 'ngModel',
        link: link,
        scope: {
        },

        template: "<div><h2>Echo:</h2> <echo id='myEcho' ng-model='model'></echo></div><div><h2>Model text:</h2>{{ model.text }}</div>"
    }
})

您可以看到“包装器”指令需要ngModelecho指令也是如此。

当我在HTML中使用“wrapper”指令,然后我将值推入ngModel时,“wrapper”指令正确识别模型已更改(console.log显示了这一点)。此时,包装器指令然后更新其范围的模型,我将期望将该模型更新传播到“echo”指令。

但是,观看控制台时,“echo”指令永远不会看到模型得到更新。

问:为什么“echo”指令不能从“wrapper”指令中看到更新的模型?

注意:由于“echo”不是从“wrapper”指令消耗的事实,这可能会稍微复杂一些 - 有时会直接使用它。

1 个答案:

答案 0 :(得分:2)

更新回答:

不,这个问题与时间无关 - 无论是在观看值设定之前还是之后添加,手表仍然会发光。

我建议在你的echo指令中添加一些断点,然后逐步查看观察者的设置方式。

这是一个正在运行的更新的plunker:http://plnkr.co/edit/bbv2vpZ7KaDiblVcoaNX?p=preview

.directive('echo', function() {
    var link = function(scope, element, attrs, ngModel) {
        console.log("***** Linking echo");

        var render = function (val) {
            var htmlText = val || 'n/t';
            element.html(htmlText);
        };
        scope.$watch("model.text", render);
    };

    return {
        restrict: 'E',
        link: link,
        scope: {
            id: "=",
            model: '=echoModel'
        }
    }
})

.directive('wrapper', function() {
    var link = function(scope, element, attrs, ngModel) {
        console.log("---- Linking Wrapper");
    };

    return {
        restrict: 'E',
        require: 'ngModel',
        link: link,
        scope: {
          wrapperModel: '=ngModel'
        },

        template: "<div><h2>Echo:</h2> <echo id='myEcho' echo-model='wrapperModel'></echo></div><div><h2>Model text:</h2>{{ wrapperModel.text }}</div>"
    }
})

它不起作用的原因是因为attrs和观察者的工作方式可能有点出乎意料。

基本上,您尝试在示波器上查看scope.model属性,而不是您所期望的ngModel属性的评估值:

.directive('echo', function() {
    var link = function(scope, element, attrs, ngModel) {
        // Your HTML is `<echo ng-model='model'></echo>`
        // which means this `scopePropertyToWatch` will have the value 'model'.
        var scopePropertyToWatch = attrs['ngModel'];

        // This means it will try to watch the value
        // of `scope.model`, which isn't right because
        // it hasn't been set.
        scope.$watch(scopePropertyToWatch, function() {
            scope.model = ngModel.$modelValue;
            console.log("***** Echo model updated: ", scope.model);
        });
    };

    // ...
})

有两个简单的解决方案。

<强> 1。在ngModel属性上设置双向绑定:

.directive('echo', function() {
    var link = function(scope, element, attrs, ngModelCtrl) {
        // Watch the `scope.ngModel` property on the scope.
        // NOT the attr['ngModel'] value which will still
        // be 'model'.
        scope.$watch('ngModel', function() {
            scope.model = ngModelCtrl.$modelValue;
            console.log("***** Echo model updated: ", scope.model);
        });
    };

    return {
        restrict: 'E',
        require: 'ngModel',
        link: link,
        scope: {
            id: "=",
            ngModel: "=" // This will make the `ngModel` property available on the scope.
        }
    }
});

使用ngModel方式有点复杂 - 我建议您查看有关如何在自定义组件中使用ngModel的视频:Jason Aden - Using ngModelController to Make Sexy Custom Components

<强> 2。在$ parent范围内观察该属性:

.directive('echo', function() {
    var link = function(scope, element, attrs, ngModelCtrl) {
        // Add a watch on the **parent** scope for the attribute value.
        // NOTE that we use the attrs['ngModel'] value because the property
        // on the parent scope **is**: `scope.$parent.model`
        scope.$parent.$watch(attrs['ngModel'], function() {
            scope.model = ngModelCtrl.$modelValue;
            console.log("***** Echo model updated: ", scope.model);
        });
    };

    return {
        restrict: 'E',
        require: 'ngModel',
        link: link,
        scope: {
            id: "="
        }
    }
});

同样,使用ngModelCtrl.$modelValue可能会有点复杂,但至少会让你的观察者开火。