追記:require: '^ngModel'
で解決しました。
自作directive内でng-modelを使ってみました。
デモ:Plunker - ng-model in a my directive.
作ってみるとわかりますが、自分で作ったdirectiveからng-modelを使おうとおもったらけっこう大変!
なんとisolated scope内では親スコープのngModelControllerを参照できないんですね。
ただ、isolated scopeにしないとせっかくのAngularJSでテンプレートが使えないという(複数個要素を作ると干渉するので)。
これを解決するためにscope.$new()
で子スコープを作って、$compile
を使ってテンプレートを手動でコンパイルした上で、さらに手動で親と子をバインドしてみました(以下コード例)。面倒くさいですが、現状わかっている範囲だと、こんなかんじで実装しないとng-changeが動かなかったり、ngModelControllerの便利なメソッドも使えないのでこれがベストかと思われます。
修正前:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.onChange = function () {
console.log('name is changed!');
};
$scope.onChange2 = function () {
console.log('name is changed!!');
}
$scope.onChange3 = function () {
console.log('name is changed!!!');
}
});
app.directive('hoge', function ($compile) {
var template = '<label>{{label}}</label><input ng-model="model" type="text">';
return {
restrict: 'E',
replace: true,
require: '?ngModel',
template: '<div></div>',
link: function (scope, element, attrs, ngModelCtrl) {
// 子スコープを作って
var child = scope.$new();
// テンプレートをコンパイルして
element.append($compile(template)(child));
attrs.$observe('label', function() {
child.label = attrs.label;
});
// 親から子にバインドして
scope.$watch(attrs.ngModel, function (value) {
child.model = value;
});
// 子から親にバインドする
child.$watch('model', function (value) {
ngModelCtrl.$setViewValue(value);
});
}
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js@1.2.x" src="http://code.angularjs.org/1.2.3/angular.js" data-semver="1.2.3"></script>
<script src="app.js"></script>
</head>
<body ng-app="plunker">
<div ng-controller="MainCtrl">
<input ng-model="name" ng-change="onChange()">
<p>Hello {{name}}!</p>
<hoge ng-model="name" label="{{name}}" ng-change="onChange2()"></hoge>
<hoge ng-model="name" label="Bar" ng-change="onChange3()"></hoge>
</div>
</body>
</html>
追記:
require: 'ngModel'
のところをrequire: '^ngModel'
にすることで親ディレクティブのコントローラーを参照できるようです。よくみたらチュートリアルに書いてあったという。
app.directive('hoge', function() {
return {
restrict: 'E',
require: '^ngModel',
scope: {
label: '@'
},
template: '<div><label>{{label}}</label><input ng-model="model"></div>',
replace: true,
link: function(scope, element, attrs, ctrl) {
scope.$watch('model', function (value) {
ctrl.$setViewValue(value);
});
scope.$parent.$watch(attrs.ngModel, function (value) {
scope.model = value;
});
}
}
});
・・・ずいぶん簡単になりました。