プログラマブログ

by wacul

menu

2014.06.23AngularJSで楽々、フォームを作ってみよう

AngularJSのformディレクティブは強力なバリデーション機能を提供します。 以下は基本的な使用例です。

1
2
3
4
5
<form name="form"><!-- 1 -->
  <input type="text" name="firstName" ng-model="user.firstName" required><!-- 2 -->
  <span ng-show="form.firstName.$error.required">何も入力されていません。</span><!-- 3 -->
  <button ng-disabled="foo.$invalid" ng-click="submit()">submit</button><!-- 4 -->
</form>

番号ごとに説明すると

  1. formのname属性を設定します。ここではname="form"としています。 formのプロパティからform全体のバリデーション情報を得ることができます。
  2. form以下のinputのname属性を設定します。ここではname="firstName"としています。 firstNameのプロパティからこのinputのバリデーション情報を得ることができます。 (バリデーションを有効にするためにはng-modelを設定する必要があります)
  3. foo.firstName.$error.requiredからrequired属性のバリデーション情報を取得しています。 エラー情報を表示したりするときに使用することができます。
  4. foo.$invalidからフォームに誤りがあるかどうかを取得できます。 ここでは誤りがある場合ボタンを無効化しています。

取得できる情報

formや各inputのname属性(上の例ではformform.firstName)のプロパティからは以下の表のようなバリデーションの情報を取得することができます。

プロパティ名 詳細
$dirty formに一回以上何かしらの入力を行った boolean
$pristine formに全く入力を行っていない($dirtyの逆) boolean
$valid formの値が正しい boolean
$invalid formの値が誤っている boolean
$error さらに詳細なエラー情報。上の例でいうところのfoo.firstName.$error.required。この他にemailmaxlengthなどがデフォルトで使えます。 Object

カスタムバリデーション

AngularJSではデフォルトで色々なバリデーションを行うことができますが、カスタムバリデーションを自作することもできます。 以下の例では3と5の倍数であるときだけ値が正しいことにするfizzbuzzディレクティブを作成しています。

1
2
3
4
<form name="form">
  <input type="text" name="num" ng-model="num" fizzbuzz>
  <span ng-show="form.num.$error.fizzbuzz">3と5の倍数でない。</span>
</form>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
angular.module('hoge', []).directive('fizzbuzz', function() {
  return {
    require: 'ngModel',
    link: function($scope, $element, $attrs, ctrl) {
      // ngModelControllerの$parsesプロパティに検証用の関数を登録します。
      ctrl.$parsers.unshift(function(viewValue) {
        viewValue = +viewValue;
        if (viewValue % 3 === 0 && viewValue % 5 === 0) {
          ctrl.$setValidity('fizzbuzz', true);
          return viewValue;
        } else {
          ctrl.$setValidity('fizzbuzz', false);
          return undefined;
        }
      });
    }
  };
});

DEMO

Tips

実際に役立ちそうな(まぁ、実際に使ってますが)Tipsをいくつか紹介します。

ng-modelを省略する

inputに一々ng-modelを設定するのは面倒です。 formのコンパイル時にinput(またはselect, textarea)のname属性からng-modelを補完するようなディレクティブを作成することで記述を減らすことができます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- ng-model="input1"として補完 -->
<form name="form1" auto-ng-model>
  <input type="text" name="input1">
  input1 = 
</form>

<!-- ng-model="data.input2"として補完 -->
<form name="form2" auto-ng-model="data">
  <input type="text" name="input2">
  <select name="select">
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
  </select>
  <br />
  <textarea name="textarea"></textarea>
  <pre>data = </pre>
</form>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
angular.module('hoge', []).directive('autoNgModel', function() {
  return {
    compile: function(element, attrs) {
      // auto-ng-modelが設定されている要素以下のinput要素にng-modelを設定します。
      function setNgModel(e) {
        if (e.getAttribute('ng-model')) return;
        e.setAttribute('ng-model', (attrs.autoNgModel ? attrs.autoNgModel + '.' : '') + e.name);
      }
      angular.forEach(element.find('input'), setNgModel);
      angular.forEach(element.find('select'), setNgModel);
      angular.forEach(element.find('textarea'), setNgModel);
    }
  };
});

DEMO

requiredのメッセージを出さないrequired

required属性はとても便利ですが、エラーメッセージを自分で用意しているときなどはポップアップが余計なことがあります。 しかし、novalidate属性をつけるとバリデーション自体行われないので困ります。 こういう場合は、AngularJSの枠組みだけでエラーを検出できるディレクティブを作成すると捗ります。

1
2
3
4
5
<form name="form">
  <input type="text" name="firstName" ng-model="user.firstName" silent-required>
  <span ng-show="form.firstName.$error.required">何も入力されていません。</span>
  <button ng-click="submit()">submit</button>
</form>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// https://github.com/angular/angular.js/blob/d606e66f7ab893f8799991883fd02b0e66e3225e/src/ng/directive/input.js#L2094
// のrequired判定する部分だけ拝借します。
angular.module('hoge', []).directive('silentRequired', function() {
  return {
    require: 'ngModel',
    link: function($scope, $element, $attrs, ctrl) {
      var validator = function(value) {
        if (ctrl.$isEmpty(value)) {
          ctrl.$setValidity('required', false);
          return;
        } else {
          ctrl.$setValidity('required', true);
          return value;
        }
      };
      ctrl.$formatters.push(validator);
      ctrl.$parsers.push(validator);
    }
  };
})

DEMO

debounce

バージョン1.3からですが、入力している途中はバリデーションを一旦止めてくれる機能があります。 使い方は簡単で、ng-model-options="{ debounce: num }"をinput要素に設定するだけです。

1
2
3
4
<form name="form">
  <input type="email" name="firstName" ng-model="user.firstName" ng-model-options="{ debounce: 300 }">
  <span ng-show="form.firstName.$error.email">正しくないメールアドレスです。</span>
</form>

DEMO

まとめ

AngularJSのフォームバリデーションは強力で拡張性が高いので、これを使いこなすとhtmlが読みやすくなり、コントローラーに記述するコードを減らすことができるでしょう。 みなさんもこれらの機能をどんどん使って楽々にフォームを作成してみて下さい。

参考URL

トップ画像参照元

この記事を書いた人kato

加藤です。JavaScriptに興味があります。

waculでは、プログラマを募集しています。

現在はプロダクトとして、課題発見から改善提案まで自動で行うWeb改善プラットフォーム「AIアナリスト」を開発中です。

waculの採用情報へ

ページトップへ