为什么ngModel。$ setViewValue(...)不工作(Why is ngModel.$

2019-07-21 03:49发布

我写这需要一个孤立的作用域的指示,但我想将其绑定到通过父范围ngModel 。

这里的问题是,父母的范围值没有得到改变。

标记

<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());
            }
        }
    };
});

演示: 小提琴 。

如果我不使用分离范围的指令也能正常工作

演示: 小提琴 。

Answer 1:

其原因是因为你创建一个孤立的范围内为您contenteditable指令后, ng-model在相同的元素指令获取孤立的范围为好。 这意味着你有没有相互连接两个不同的领域,它都具有form.userContent是单独改变属性。 我想你可以通过这个代码举例说明吧:

<!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>

正如你将在你的控制台中看到,有两个不同的范围和form.userContent他们分开,如果你在textarea的修改文本或更改,如果您更改CONTENTEDITABLE格中的文本。

所以,我敢打赌,你会想“足以与解释,并告诉我一个解决方案!”。 那么,有没有(据我所知)这一个漂亮的解决方案,但有一个工程。 你想要做的就是把模型的参考到您的隔离范围,并确保它在你的孤立范围相同的名称,在父范围。

这里是你做什么,而不是创建一个空的范围是这样的:

...
scope: {}
...

您绑定这样的模式:

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

现在你有一个model上的孤立范围属性,它是一个参考form.userContent上你的父母范围。 但是ng-model是不是找一个model属性,它在寻找一个form.userProperty仍然没有在我们的孤立范围存在。 因此,要解决这个问题,我们我们的逻辑函数里面添加这个:

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

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

第一块手表同步的变化form.userContent是来自于我们的指令以外的我们孤立form.userContent ,而第二块手表可以确保我们传播我们的孤立的任何更改form.userContent到父范围。

我知道这是一个漫长的答案,也许不是很容易。 所以,我很乐意clearify什么,你的感觉是模糊的。



Answer 2:

第一个答案解释了这个问题很好,我相信我有避免额外的手表一个简单的解决方案。

总结答案1. ngModel不能分离范围内工作,因为你预期它的元素绑定到不在其范围。 他们是在父范围。

解决方案1,绑定到父母的财产

<div contenteditable name="myWidget" ng-model="form.userContent" required>Change me!</div>

<div contenteditable name="myWidget" ng-model="$parent.form.userContent" required>Change me!</div>

溶液2,移动ngModel分离物范围以外的

require : '?ngModel',变成require : '?^ngModel',在^告诉你的指令在ngModel父元素看

<div contenteditable name="myWidget" ng-model="form.userContent" required>Change me!</div>

<div ng-model="form.userContent">
    <div contenteditable name="myWidget" required>Change me!</div>
</div>


Answer 3:

我不知道这是否会为你工作。 然而,你可以试试看。

HTML:

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

JS

angular.module('customControl', []).directive('contenteditable', function() {
    return {
        restrict : 'A', // only activate on element attribute
        require : '?ngModel', // get a hold of NgModelController
        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({'content': element.html()});
            }
        }
    };
});


文章来源: Why is ngModel.$setViewValue(…) not working from