javascript 如何观看指令的指令 ng-model

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22819494/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 23:50:00  来源:igfitidea点击:

How to Watch Directive's Directive ng-model

javascriptangularjsangularjs-directiveangularjs-scopeangular-ngmodel

提问by Neel

I have a directive that uses the parent scope in that view. This directive has a child directive that uses an isolated scope. I am trying to get the parent directive to watch any changes done to the ngModel of the child directive and update its own modal if changes were made. Here is a jsfiddle that probably explains better: http://jsfiddle.net/Alien_time/CnDKN/

我有一个在该视图中使用父作用域的指令。该指令有一个使用隔离作用域的子指令。我试图让父指令观察对子指令的 ngModel 所做的任何更改,并在进行更改时更新其自己的模式。这是一个 jsfiddle,可能解释得更好:http: //jsfiddle.net/Alien_time/CnDKN/

Here is the code:

这是代码:

   <div ng-app="app">
        <div ng-controller="MyController">

            <form name="someForm">
                <div this-directive ng-model="theModel"></div>
            </form>

         </div>
    </div>

Javascript:

Javascript:

    var app = angular.module('app', []);
app.controller('MyController', function() {

});

app.directive('thisDirective', function($compile, $timeout) {

    return {
        scope: false,

        link: function(scope, element, attrs) {
            var ngModel = attrs.ngModel;
            var htmlText = '<input type="text" ng-model="'+ ngModel + '" />' +
                           '<div child-directive ng-model="'+ ngModel + '"></div>';

            $compile(htmlText)(scope, function(_element, _scope) {
                element.replaceWith(_element);                
            });

            // Not sure how to watch changes in childDirective's ngModel ???????

        }, // end link
    } // end return

});

app.directive('childDirective', function($compile, $timeout) {
    return {
            scope: {
                ngModel: '='            
        },  

        link: function(scope, element, attrs, ngModel) {
            var htmlText = '<input type="text" ng-model="ngModel" />';

            $compile(htmlText)(scope, function(_element, _scope) {
                element.replaceWith(_element);                
            });   

            // Here the directive text field updates after some server side process
            scope.ngModel = scope.dbInsertId;

            scope.$watch('dbInsertId', function(newValue, oldValue) {
                if (newValue)
                    console.log("I see a data change!");  // Delete this later
                    scope.ngModel = scope.imageId;
            }, true);

        },

    } // end return
});

In the example, you can see that there is a text input inside a parent directive as well as its child directive. If you type inside each of them, the other model gets updated since they are binded by ngmodel. However, the child directive's text input gets updated after a server connection. When that happens, the text input in the parent directive doesnt get updated. So I think I need to watch the ngModel inside the child directive for any changes. How can I do that? Does it make sense?

在示例中,您可以看到在父指令及其子指令中都有一个文本输入。如果您在每个模型中键入,则另一个模型会更新,因为它们由ngmodel. 但是,子指令的文本输入在服务器连接后更新。发生这种情况时,父指令中的文本输入不会更新。所以我想我需要观察子指令中的 ngModel 是否有任何更改。我怎样才能做到这一点?是否有意义?

回答by JoseM

As @shaunhusain mentioned you have to use the ngModelController to interact with ngModel. On the ngModelController you can set up a watch on the $modelValueand you can change the value in the model by calling $setViewValue. Remeber that to use the ngModelController you need to add a require: "ngModel"to your directive definition object.

正如@shaunhusain 提到的,您必须使用 ngModelController 与 ngModel 进行交互。在 ngModelController 上,您可以在 上设置监视,$modelValue并且可以通过调用$setViewValue. 请记住,要使用 ngModelController,您需要将 a 添加require: "ngModel"到指令定义对象中。

When you get a value from outside of angular (like from your database) and you in turn want to use that value to change the model value, you need to wrap that code in a scope.$apply()

当您从 angular 外部(例如从您的数据库中)获得一个值并且您又想使用该值来更改模型值时,您需要将该代码包装在一个 scope.$apply()

app.directive('thisDirective', function($compile, $timeout, $log) {

    return {
        scope: false,
        require: 'ngModel',

        link: function(scope, element, attrs, ngModel) {
            ...

            scope.$watch(
                function(){
                    return ngModel.$modelValue;
                }, function(newValue, oldValue){
                    $log.info('in *thisDirective* model value changed...', newValue, oldValue);
                }, true);

        }, // end link
    } // end return

});

app.directive('childDirective', function($compile, $timeout, $log) {
    return {
        scope: {
            ngModel: '='
        },
        require: 'ngModel',

        link: function(scope, element, attrs, ngModel) {
            ...

            scope.$watch(
                function(){
                    return ngModel.$modelValue;
                }, function(newValue, oldValue){
                    $log.info('in *childDirective* model value changed...', newValue, oldValue);
                }, true);

            // make believe change by server
            setTimeout(function() {
                scope.$apply(function() {
                    ngModel.$setViewValue('set from the server...');
                };
            },5000);


        },

    } // end return
});

relevant jsfiddle http://jsfiddle.net/CnDKN/2/

相关 jsfiddle http://jsfiddle.net/CnDKN/2/

BUTI don't think this is not really right usage of $setViewValue. According to the docs, that is supposed to be used to update the value that is shown on the UI, not necessarily the model value.

我认为这不是$setViewValue. 根据文档,这应该用于更新 UI 上显示的值,不一定是模型值。

There is actually another way of making this work which I think is more straightforward and easier to use. Just use =attrto set up bi-directional binding of the property you want to use in both thisDirectiveand in childDirective. And then you can just set up the the ng-model attribute setting in your directive and when you first use it you don't even need to know that it is using ng-model underneath.

实际上还有另一种方法可以使这项工作更加直接和易于使用。只需用于=attr设置要在thisDirective和 in 中使用的属性的双向绑定childDirective。然后你可以在你的指令中设置 ng-model 属性设置,当你第一次使用它时,你甚至不需要知道它在下面使用 ng-model 。

Here is the code that shows you what I mean:

这是向您展示我的意思的代码:

app.directive('thisDirective', function($compile, $timeout, $log) {

    return {
        scope: {
            thisval: '='
        },

        link: function(scope, element, attrs) {

            var htmlText = '<input type="text" ng-model="thisval" />' +
                           '<div child-directive childval="thisval"></div>';

            $compile(htmlText)(scope, function(_element, _scope) {
                element.replaceWith(_element);                
            });

            scope.$watch('thisval',function(newVal,oldVal){
                $log.info('in *thisDirective* thisval changed...',
                          newVal, oldVal);
            });


        }, // end link
    } // end return

});

app.directive('childDirective', function($compile, $timeout, $log) {
    return {
        scope: {
            childval: '='
        },

        link: function(scope, element, attrs) {
            var htmlText = '<input type="text" ng-model="childval" />';

            $compile(htmlText)(scope, function(_element, _scope) {
                element.replaceWith(_element);                
            });

            scope.$watch('childval',function(newVal,oldVal){
                $log.info('in *childDirective* childval changed...',
                          newVal, oldVal);
            });

            // make believe change that gets called outside of angular
            setTimeout(function() {
                // need to wrap the setting of values in the scope 
                // inside of an $apply so that a digest cycle will be 
                // started and have all of the watches on the value called
                scope.$apply(function(){
                    scope.childval = "set outside of angular...";
                });
            },5000);

        },

    } // end return
});

Updated jsfiddle example: http://jsfiddle.net/CnDKN/3/

更新 jsfiddle 示例:http: //jsfiddle.net/CnDKN/3/