我写一个指令,需要一个孤立的范围,但我想绑定到父范围通过
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()); } } }; });
演示:Fiddle。
这工作正常,如果我不使用隔离范围的指令
演示:Fiddle。
原因是,由于您为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: {},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会单独改变。
所以我打赌你在想“足够的解释,并给我一个解决方案!好吧,(对我的知识)没有一个漂亮的解决方案,但有一个工作。您想要做的是将模型的引用引入到您的隔离范围中,并确保它在您的隔离范围中具有与父范围中相同的名称。
这里是你做的,而不是像这样创建一个空的范围:
... scope: {} ...
你像这样绑定模型:
... scope: { model: '=ngModel' } ....
现在你在你的隔离范围上有一个模型属性,它是对父范围的form.userContent的引用。但ng-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上的任何更改传播到父作用域。
我意识到这是一个冗长的答案,也许不是很容易遵循。所以我很乐意澄清你觉得模糊的东西。