Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
38
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

Organization

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

追記: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;
      });
    }
  }
});

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

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
38
Help us understand the problem. What are the problem?