为什么 ngModel.$setViewValue(...) 不工作 [英] Why is ngModel.$setViewValue(...) not working from

查看:34
本文介绍了为什么 ngModel.$setViewValue(...) 不工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个需要隔离作用域的指令,但我想通过 ngModel.

I'm writing an directive which needs an isolated scope, but I want to bind it to the parent scope via ngModel.

这里的问题是父级的范围值没有改变.

Here the problem is that the parent's scope value is not getting changed.

标记

<form name="myForm" ng-app="customControl">
    <div ng-init="form.userContent"></div>
    <div contenteditable name="myWidget" ng-model="form.userContent" required>Change me!</div>
    <span ng-show="myForm.myWidget.$error.required">Required!</span>
    <hr />
    <textarea ng-model="form.userContent"></textarea>
</form>

JS

angular.module('customControl', []).directive('contenteditable', function() {
    return {
        restrict : 'A', // only activate on element attribute
        require : '?ngModel', // get a hold of NgModelController
        scope: {},
        link : function(scope, element, attrs, ngModel) {
            if (!ngModel)
                return; // do nothing if no ng-model

            // Specify how UI should be updated
            ngModel.$render = function() {
                element.html(ngModel.$viewValue || '');
            };

            // Listen for change events to enable binding
            element.bind('blur keyup change', function() {
                        scope.$apply(read);
                    });
            read(); // initialize

            // Write data to the model
            function read() {
                ngModel.$setViewValue(element.html());
            }
        }
    };
});

演示:小提琴.

如果我不为指令使用隔离范围,这可以正常工作

This works fine if I don't use a isolated scope for the directive

演示:小提琴.

推荐答案

原因是因为您正在为 contenteditable 指令创建一个隔离的范围,ng-model 指令也获得该隔离范围.这意味着您有两个不相互连接的不同作用域,它们都有一个分别更改的 form.userContent 属性.我想你可以用这段代码来举例说明:

The reason is that since you're creating an isolated scope for your contenteditable directive, the ng-model directive on the same element gets that isolated scope as well. Which means that you have two different scopes that aren't connected to each other, which both have a form.userContent property that changes separately. I guess you could exemplify it by this code:

<!doctype html>
<html ng-app="myApp">
<head>
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="http://code.angularjs.org/1.0.5/angular.min.js"></script>
    <script>
    angular.module('myApp', []).controller('Ctrl', function($scope) {

    })
    .directive('contenteditable', function() {
        return {
            restrict : 'A', // only activate on element attribute
            require : '?ngModel', // get a hold of NgModelController
            scope: {},
            link : function(scope, element, attrs, ngModel) {
                if (!ngModel)
                    return; // do nothing if no ng-model

                setInterval(function() {
                    if (angular.element('#contenteditable').scope().form)
                        console.log(angular.element('#contenteditable').scope().form.userContent);

                    if (angular.element('#textarea').scope().form)
                        console.log(angular.element('#textarea').scope().form.userContent);
                }, 1000);

                // Specify how UI should be updated
                ngModel.$render = function() {
                    element.html(ngModel.$viewValue || '');
                };

                // Listen for change events to enable binding
                element.bind('blur keyup change', function() {
                            scope.$apply(read);
                        });
                read(); // initialize

                // Write data to the model
                function read() {
                    ngModel.$setViewValue(element.html());
                }
            }
        };
    });
    </script>
</head>
<body ng-controller="Ctrl">
    <form name="myForm">
        <div ng-init="form.userContent"></div>
        <div contenteditable name="myWidget" ng-model="form.userContent" id="contenteditable" required>Change me!</div>
        <span ng-show="myForm.myWidget.$error.required">Required!</span>
        <hr />
        <textarea ng-model="form.userContent" id="textarea"></textarea>
    </form>
</body>
</html>

正如您将在控制台中看到的,如果您更改 textarea 中的文本或更改 contenteditable div 中的文本,则有两个不同的范围和 form.userContent 分别更改.

As you'll see in your console, there are two different scopes and form.userContent on them change separately if you change the text in the textarea or if you change the text in your contenteditable div.

所以我敢打赌你在想解释够了,给我一个解决方案!".好吧,(据我所知)没有一个很好的解决方案,但是有一个有效的解决方案.您想要做的是将模型的引用带入您的隔离范围,并确保它在您的隔离范围中与父范围中具有相同的名称.

So I bet you're thinking "enough with the explaining and show me a solution!". Well, there aren't (to my knowledge) a pretty solution for this, but there is one that works. What you want to do is bring a reference of the model into your isolated scope, and make sure that it has the same name in your isolated scope as in the parent scope.

这是你要做的,而不是像这样创建一个空的范围:

Here's what you do, instead of creating an empty scope like this:

...
scope: {}
...

你像这样绑定模型:

...
scope: {
    model: '=ngModel'
}
....

现在您的独立作用域上有一个 model 属性,它是对父作用域上的 form.userContent 的引用.但是 ng-model 不是在寻找 model 属性,它在寻找一个 form.userProperty 在我们的隔离中仍然不存在范围.所以为了解决这个问题,我们在我们的链接函数中添加了这个:

Now you have a model property on your isolated scope that is a reference to form.userContent on your parent scope. But ng-model isn't looking for a model property, it's looking for a form.userProperty which still doesn't exist in our isolated scope. So to fix this, we add this inside our linking function:

scope.$watch('model', function() {
    scope.$eval(attrs.ngModel + ' = model');
});

scope.$watch(attrs.ngModel, function(val) {
    scope.model = val;
});

第一个监视将来自我们指令之外的 form.userContent 上的更改同步到我们隔离的 form.userContent,第二个监视确保我们传播任何将我们隔离的 form.userContent 更改到父作用域.

The first watch syncs changes on form.userContent that comes from outside of our directive to our isolated form.userContent, and the second watch makes sure that we propagate any changes on our isolated form.userContent up to the parent scope.

我意识到这是一个冗长的答案,可能不太容易理解.所以我很乐意澄清您认为模糊的任何内容.

I realize that this is a lengthy answer, and perhaps not very easy to follow. So I'd be happy to clearify anything that you feel is blurry.

这篇关于为什么 ngModel.$setViewValue(...) 不工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆