ngModel.NgModelController

NgModelControllerはng-modelディレクティブのためのAPIを提供します。 このコントローラーはデータバインディング、検証、CSS更新、値フォーマットと解析のためのサービスを含みます。 これには、DOM描画やDOMイベントのリッスンのロジックを含まないという意図があります。 そのようなDOMに関するロジックは、データバインディングのためにNgModelControllerを使用する他のディレクティブから提供されるべきです。

カスタム入力要素の例

下記は、入力要素をカスタマイズしてデータバインディングを実装するNgModelControllerの使用方法の例になります。 望む結果を得るために、異なるディレクティブ(contenteditable、ng-model、required)をどのように共存させているかを確認してください。

contenteditableはHTML5の属性であり、ブラウザにこの要素の内容がユーザーによって編集されるように指示します。 これは古いブラウザでは動作しません。

<!doctype html>
<html ng-app="customControl">
  <head>
    <script src="http://code.angularjs.org/1.2.0-rc.2/angular.min.js"></script>
    <script src="script.js"></script>
  </head>
  <body>
    <form name="myForm">
     <div contenteditable
          name="myWidget" ng-model="userContent"
          strip-br="true"
          required>Change me!</div>
      <span ng-show="myForm.myWidget.$error.required">Required!</span>
     <hr>
     <textarea ng-model="userContent"></textarea>
    </form>
  </body>
</html>
[contenteditable] {
  border: 1px solid black;
  background-color: white;
  min-height: 20px;
}

.ng-invalid {
  border: 1px solid red;
}
angular.module('customControl', []).
  directive('contenteditable', function() {
    return {
      restrict: 'A',         // 要素の属性のみを有効とする
      require: '?ngModel',   // NgModelControllerを保持する
      link: function(scope, element, attrs, ngModel) {
        if(!ngModel) return; // ng-modelで無ければ何もしない

        // UIがどのように更新されるべきかを指定
        ngModel.$render = function() {
          element.html(ngModel.$viewValue || '');
        };

        // 紐付けを有効にするためのチェンジイベントのリッスン
        element.on('blur keyup change', function() {
          scope.$apply(read);
        });
        read(); // 初期化

        // データをモデルに書き込み
        function read() {
          var html = element.html();
          // コンテンツ編集領域をクリアした際にブラウザが残す<br>を、
          // strip-br属性が提供されていたら、これを取り除きます。
          if( attrs.stripBr && html == '<br>' ) {
            html = '';
          }
          ngModel.$setViewValue(html);
        }
      }
    };
  });
it('should data-bind and become invalid', function() {
  var contentEditable = element('[contenteditable]');

  expect(contentEditable.text()).toEqual('Change me!');
  input('userContent').enter('');
  expect(contentEditable.text()).toEqual('');
  expect(contentEditable.prop('className')).toMatch(/ng-invalid-required/);
});
<!doctype html>
<html ng-app="customControl">
  <head>
    <script src="http://code.angularjs.org/1.2.0-rc.2/angular.min.js"></script>
<script>angular.module('customControl', []).
  directive('contenteditable', function() {
    return {
      restrict: 'A',         // 要素の属性のみを有効とする
      require: '?ngModel',   // NgModelControllerを保持する
      link: function(scope, element, attrs, ngModel) {
        if(!ngModel) return; // ng-modelで無ければ何もしない

        // UIがどのように更新されるべきかを指定
        ngModel.$render = function() {
          element.html(ngModel.$viewValue || '');
        };

        // 紐付けを有効にするためのチェンジイベントのリッスン
        element.on('blur keyup change', function() {
          scope.$apply(read);
        });
        read(); // 初期化

        // データをモデルに書き込み
        function read() {
          var html = element.html();
          // コンテンツ編集領域をクリアした際にブラウザが残す<br>を、
          // strip-br属性が提供されていたら、これを取り除きます。
          if( attrs.stripBr && html == '<br>' ) {
            html = '';
          }
          ngModel.$setViewValue(html);
        }
      }
    };
  });
</script>
  </head>
  <body>
    <form name="myForm">
     <div contenteditable
          name="myWidget" ng-model="userContent"
          strip-br="true"
          required>Change me!</div>
      <span ng-show="myForm.myWidget.$error.required">Required!</span>
     <hr>
     <textarea ng-model="userContent"></textarea>
    </form>
  </body>
</html>

孤立スコープの落とし穴

孤立スコープのディレクティブを持つ場合、モデルの値は外部スコープよりも孤立スコープ上から探されるため、 ngModelをrequire出来ないことに注意してください。 ディレクティブがモデル値を更新した際の、外部スコープ上のngModel.$setViewValue()は、値を更新しません。

下記はこの状況を再現した例になります。 'input'と'div'は同じモデルに割り当てられているように思えますが、実際には同期されないことを確認してください。

<!doctype html>
<html ng-app="badIsolatedDirective">
  <head>
    <script src="http://code.angularjs.org/1.2.0-rc.2/angular.min.js"></script>
    <script src="script.js"></script>
  </head>
  <body>
    <input ng-model="someModel">
    <div bad ng-model="someModel"></div>
  </body>
</html>
angular.module('badIsolatedDirective', []).directive('bad', function() {
  return {
    require: 'ngModel',
    scope: { },
    template: '<input ng-model="innerModel">',
    link: function(scope, element, attrs, ngModel) {
      scope.$watch('innerModel', function(value) {
        console.log(value);
        ngModel.$setViewValue(value);
      });
    }
  };
});
<!doctype html>
<html ng-app="badIsolatedDirective">
  <head>
    <script src="http://code.angularjs.org/1.2.0-rc.2/angular.min.js"></script>
<script>angular.module('badIsolatedDirective', []).directive('bad', function() {
  return {
    require: 'ngModel',
    scope: { },
    template: '<input ng-model="innerModel">',
    link: function(scope, element, attrs, ngModel) {
      scope.$watch('innerModel', function(value) {
        console.log(value);
        ngModel.$setViewValue(value);
      });
    }
  };
});
</script>
  </head>
  <body>
    <input ng-model="someModel">
    <div bad ng-model="someModel"></div>
  </body>
</html>

メソッド

メソッド 説明
$render() ビューの更新が必要な際に呼び出します。 ng-modelディレクティブを使用する際に、このメソッドが実装される事が期待されます。
$setPristine() 手つかず(pristine)状態を入力要素に設定します。 このメソッドを呼び出すことで、'ng-dirty'クラスを削除することができ、それによって入力要素が、手つかず(pristine)状態(ng-pristineクラス)になります。
$setValidity
(validationErrorKey, isValid)

「正当/不正」の検証状態を変更し、入力要素のその状態が変更された場合はフォームに通知します。 (例えば、もし既に与えられたバリデーターが「不正」としてマークされている場合、フォームには通知されません。)

このメソッドはバリデーターから呼び出されるべきです。(parserやformatter関数)

validationErrorKey
型:string
バリデーターの名前です。 データバインディングが利用出来るように、validationErrorKeyは、 $error[validationErrorKey]=isValidを割り当てます。 validationErrorKeyはキャメルケースにすべきで、クラス名はダッシュ(-)ケースに変換して取得します。 例えば、myErrorは、ng-valid-my-errorng-invalid-my-errorクラスとなり、 {{someForm.someControl.$error.myError}}として、紐付けられます。
isValid
型:boolean
現在の状態が、正当(true)または不正(false)なのかを指定します。
$setViewValue(value)

ビューから値を読み取ります。 このメソッドは、DOMイベントハンドラ内から呼び出されるべきです。 例えば、inputまたはselectディレクティブがこれを呼び出します。 これは内部で全ての$parsers(バリデーターを含む)を呼び出し、 $modelValueと実際のモデルのパスを更新します。 最後に、全ての登録されている変更リスナーを呼び出します。

value
型:string
ビューからの値

プロパティ

プロパティ 説明
$viewValue ビュー内の実際の文字列の値です。
$modelValue 入力要素が紐付けられたモデルの値です。
$parsers 入力要素がDOMから値を読み込む度に、パイプラインとして実行するための関数の配列です。 各functionは順に値を渡され、次へ次へと呼び出されます。 検証時と同様に、サニタイズ/変換が値に対して行われます。 検証のために、parsersは正当/不正の状態を$setValidity()を使用して更新する必要があり、 不正な値であればundefinedを返します。
$formatters

モデルの値が変更される度に、パイプラインとして実行するための関数の配列です。 各fncitonは順に値を渡され、次へ次へと呼び出されます。 表示するために、値のフォーマット/変換が入力要素と検証内で使用されます。

function formatter(value) {
if (value) {
  return value.toUpperCase();
}
}
ngModel.$formatters.push(formatter);
$error 全てのエラーをキー付きで格納しているオブジェクトハッシュです。
$pristine 入力要素にまだユーザーが触れて無ければ(手つかずであれば)、trueになります。
$dirty 入力要素にユーザーが触れていれば、trueになります。
$valid エラーが無ければ、trueになります。
$invalid 入力要素に少なくとも1つ以上エラーがあればtrueになります。

 Back to top

© 2017 Google
Licensed under the Creative Commons Attribution License 3.0.

このページは、ページトップのリンク先のAngularJS公式ドキュメント内のページを翻訳した内容を基に構成されています。 下記の項目を確認し、必要に応じて公式のドキュメントをご確認ください。 もし、誤訳などの間違いを見つけましたら、 @tomofまで教えていただければ幸いです。

  • AngularJSの更新頻度が高いため、元のコンテンツと比べてドキュメントの情報が古くなっている可能性があります。
  • "訳注:"などの断わりを入れた上で、日本人向けの情報やより分かり易くするための追記を行っている事があります。