孤立したスコープを必要とするディレクティブを書いていますが、 ngModel を介して親スコープにバインドしたいです。
ここでの問題は、親のスコープ値が変更されていないことです。
Markup
<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
ディレクティブもその分離スコープを取得するためです。つまり、相互に接続されていない2つの異なるスコープがあり、どちらも別々に変更される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>
コンソールに表示されるように、2つの異なるスコープがあり、textareaのテキストを変更した場合、またはcontenteditable divのテキストを変更した場合、form.userContent
は別々に変更されます。
だから、あなたは「説明で十分で、解決策を見せて!」と考えているに違いない。まあ、これには(私の知る限りでは)きれいな解決策はありませんが、うまくいくものがあります。あなたがしたいことは、モデルの参照を隔離されたスコープに持ち込み、隔離されたスコープに親スコープと同じ名前があることを確認することです。
このような空のスコープを作成する代わりに、次のようにします。
...
scope: {}
...
次のようにモデルをバインドします。
...
scope: {
model: '=ngModel'
}
....
これで、親スコープのform.userContent
への参照である分離スコープにmodel
プロパティができました。しかし、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
に同期し、2番目のウォッチは、分離されたform.userContent
の変更を親に確実に伝播します範囲。
これは長い答えであり、たぶん従うのは簡単ではないことを理解しています。だから、私はあなたがぼやけていると感じるものは何でもクリアしたいと思います。
最初の答えは問題をうまく説明しています、余分な時計を避ける簡単な解決策があると思います。
答えをまとめると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>