Help us understand the problem. What is going on with this article?

自作directive内でng-modelをきちんと動作させる

More than 5 years have passed since last update.

追記: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の便利なメソッドも使えないのでこれがベストかと思われます。

修正前

app.js
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);
      });
    }
  }
});
index.html
<!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.js
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;
      });
    }
  }
});

・・・ずいぶん簡単になりました。

ukyo
こんにちはちは
https://ukyoweb.com
wacul
人工知能でWebサイトの課題を発見する AIアナリスト https://wacul-ai.com を開発しています
https://wacul.co.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away