【问题标题】:Update ngModel on a directive, from a directive从指令更新指令上的 ngModel
【发布时间】:2014-08-23 23:12:49
【问题描述】:

我无法理解为什么对 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”不是从“包装器”指令中消耗的 - 有时它是直接消耗的。

【问题讨论】:

标签: javascript angularjs angularjs-directive angularjs-scope


【解决方案1】:

更新答案:

不,此问题与时间无关 - 无论是在设置监视值之前还是之后添加手表,手表仍然会触发。

我建议在您的 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 可能会变得有点复杂,但至少这会让你的观察者开火。

【讨论】:

  • 嗯,你的解释对我来说没有意义 :-(。包装指令和 echo 指令本质上是相同的——但是 wrapper 指令正确更新,但 echo 指令没有。为什么?我认为@ben-priebe 的答案可能“更正确”——这似乎与指令执行的顺序有关。我不明白为什么顺序很重要,因为我认为设置一个表达式上的手表不会依赖于时间。他的 plunk 有一个错误,但这是一个有效的:plnkr.co/edit/dsybDpmKvjQfE2BsDbev?p=preview
  • 顺便说一句,你更新的 plunker 太棒了——它让我意识到我让事情变得过于复杂(通过观察 ngModel 中的变化,然后将其作为“模型”推送到范围)。你的方法要简单得多。再次感谢。
  • 不客气 :) 这是一个很好的过程,可以了解指令的细微差别和范围。
猜你喜欢
  • 1970-01-01
  • 2015-01-01
  • 2023-03-11
  • 2016-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-25
  • 2015-01-24
相关资源
最近更新 更多