templateurl parameter directives directivas custom compile angularjs angularjs-directive angularjs-scope

angularjs - parameter - ¿Por qué ngModel. $ SetViewValue(...) no funciona desde



scope directive angularjs (3)

El motivo es que, dado que está creando un ámbito aislado para su directiva contenteditable , la directiva ng-model en el mismo elemento también obtiene ese ámbito aislado. Lo que significa que tiene dos ámbitos diferentes que no están conectados entre sí, y que ambos tienen una propiedad form.userContent que cambia por separado. Supongo que podrías ejemplificarlo con este código:

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

Como verá en su consola, hay dos ámbitos diferentes y form.userContent en ellos cambia por separado si cambia el texto en el área de texto o si cambia el texto en su div contenteditable.

Así que apuesto a que estás pensando "basta con las explicaciones y muéstrame una solución". Bueno, no hay (que yo sepa) una solución bonita para esto, pero hay una que funciona. Lo que desea hacer es traer una referencia del modelo a su alcance aislado y asegurarse de que tenga el mismo nombre en su alcance aislado que en el alcance principal.

Esto es lo que haces, en lugar de crear un ámbito vacío como este:

... scope: {} ...

Atas el modelo así:

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

Ahora tiene una propiedad model en su ámbito aislado que es una referencia a form.userContent en su ámbito primario. Pero ng-model no está buscando una propiedad model , está buscando una form.userProperty que todavía no existe en nuestro ámbito aislado. Así que para solucionar esto, agregamos esto dentro de nuestra función de enlace:

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

El primer reloj sincroniza los cambios en form.userContent que vienen de fuera de nuestra directiva a nuestro form.userContent , y el segundo reloj se asegura de que form.userContent cualquier cambio en nuestro form.userContent aislado. form.userContent hasta el ámbito principal.

Me doy cuenta de que esta es una respuesta larga, y quizás no muy fácil de seguir. Así que me encantaría aclarar cualquier cosa que consideren borrosa.

Estoy escribiendo una directiva que necesita un alcance aislado, pero quiero vincularlo al alcance principal a través de ngModel .

Aquí el problema es que el valor del alcance del padre no se modifica.

Margen

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

Demostración: Fiddle .

Esto funciona bien si no uso un ámbito aislado para la directiva

Demostración: Fiddle .


La primera respuesta explica bien el problema, creo que tengo una solución más simple que evita los relojes adicionales.

para resumir la respuesta 1. ngModel no puede funcionar dentro del ámbito de aislamiento porque los elementos a los que pretendía enlazar no están en su alcance. están en el ámbito principal.

solución 1, se unen a la propiedad del padre

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

se convierte en

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

solución 2, mueva ngModel fuera del alcance del aislamiento

require : ''?ngModel'', convierte en require : ''?^ngModel'', el ^ le dice a su directiva que busque elementos primarios para ngModel

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

se convierte en

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


No estoy seguro de si esto funcionará para usted. Sin embargo, puedes darle una oportunidad.

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