Powodem jest to, że ponieważ tworzysz izolowany zakres dla dyrektywy contenteditable
, dyrektywa ng-model
dla tego samego elementu również otrzyma ten izolowany zakres. Oznacza to, że masz dwa różne zakresy, które nie są ze sobą połączone, a oba mają właściwość form.userContent
, która zmienia się osobno. Chyba można zilustrować go przez ten kod:
<!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>
Jak zobaczysz w konsoli, istnieją dwa różne zakresy i form.userContent
na ich zmieniać oddzielnie, jeśli zmienić tekst w textarea lub w przypadku zmiany tekst w twoim contenteditable div.
Założę się, że myślisz "wystarczy z wyjaśnieniem i pokaż mi rozwiązanie!". Cóż, nie ma (według mojej wiedzy) całkiem odpowiedniego rozwiązania, ale jest jedna, która działa. To, co chcesz zrobić, to wprowadzić odniesienie modelu do wyizolowanego zakresu i upewnić się, że ma ono taką samą nazwę w odizolowanym zakresie, jak w zakresie nadrzędnym.
Oto co trzeba zrobić, zamiast tworzyć pustą zakres takiego:
...
scope: {}
...
powiązać model takiego:
...
scope: {
model: '=ngModel'
}
....
Teraz masz nieruchomość model
na pojedyncze zakresie, jest odniesieniem do form.userContent
w twoim zasięgu nadrzędnym. Ale ng-model
nie szuka właściwości model
, szuka ona form.userProperty
, która wciąż nie istnieje w naszym wyizolowanym zakresie. Tak aby rozwiązać ten problem, dodajemy to wewnątrz naszej funkcji łączącej:
scope.$watch('model', function() {
scope.$eval(attrs.ngModel + ' = model');
});
scope.$watch(attrs.ngModel, function(val) {
scope.model = val;
});
Pierwszy zegarek synchronizuje zmiany na form.userContent
że pochodzi spoza naszego dyrektywy do naszego izolowanym form.userContent
, a drugi zegarek daje pewność, że mamy propagować żadnych zmian na naszym izolowanym form.userContent
aż do zakresu nadrzędnego.
Zdaję sobie sprawę, że jest to długa odpowiedź i być może nie bardzo łatwa do naśladowania. Tak więc z radością wyjaśniłbym wszystko, co wydaje ci się niewyraźne.
Gdyby nie 'element.html (ngModel $ viewValue ....) Będzie' element.html ($ sce.getTrustedHtml (ngModel $. viewValue) ..) "Wiem, że to prawie dokładnie to samo, co w przykładzie na Ng docs, ale właśnie odkryłem, że w ten sposób skutecznie pomija ochronę xss. – cirrus
@arun Czy możesz wyjaśnić, dlaczego zakres izolatu sprawia, że nie działa? – geckob
Komentarz @rirrus, wydaje mi się, że aby uniknąć XSS, za każdym razem, gdy cokolwiek zostanie umieszczone w elemencie, powinno najpierw zostać odkażone przez $ sanitize (tj. Nie ufaj HTML). Coś jak element.html ($ sanitize (ngModel. $ ViewValue)) '. – Soferio