読者です 読者をやめる 読者になる 読者になる

Can I do web?

AngularJS 中心の不定期ブログ

AngularJS 使って手軽でリッチな入力フォームを作ってみた

  

angularJS 入力フォーム

意外とAngularJSを使った入力フォームのサンプルが無かったので作ってみました。

まず作ったものがコチラ

 AngularJSを使えば手軽にシンプルかつリッチな入力フォームを作ることができます。自前のJavaScriptを用意すればさらに凝ったものも作ることができますが、今回は自前のJavaScriptを使わないでもこれだけできます!というサンプルです。

個別のバリデーションチェック

サンプルで行われているバリデーションは、全てAngularJSによるもので、JavaScriptによるコードの記述は一切書いていません。その代わりに、以下のような記述でチェックする内容を指定しています。 

<form method="post" name="userInfo">
...
<input type="text" name="userName" ng-model="userName" ng-minlength="6" ng-maxlength="12" required>
<span class="error" ng-show="userInfo.userName.$error.required">必須</span>
<span class="error" ng-show="userInfo.userName.$error.minlength">6文字以上です</span>
<span class="error" ng-show="userInfo.userName.$error.maxlength">12文字までです</span>
<span class="valid" ng-show="userInfo.userName.$valid">OK</span>
...
</form>

input要素にあるng-minlength、ng-maxlength、required 属性がチェック内容を指定する記述です。チェック内容は見たままですのでわかりやすいと思います。

これを記述するだけで、それぞれのチェックをしてくれます。

それぞれのチェック結果は、userInfo.userName.$error.required、userInfo.userName.$error.minlength、userInfo.userName.$error.maxlength に保持されます。

これをinput属性以降のspanタグにある ng-show="xxxx" と組み合わせることで、それぞれのエラーに対応するエラーメッセージを表示させています。

1つ注意すべきはエラーの場合 true、チェックOKの場合は falseが格納されますのでご注意を。

また、userInfo.userName.$valid とありますが、これはエラーではなく、チェックOKの場合に true が格納されます。こちらについては、後で説明します。

AngularJS の双方向バインディング

これは AngularJS の売りでもあるのですが、これを利用してパスワードの入力でよくある「パスワードを表示する」をチェックすると入力値が見えるようになるUIを実現しています。
<form method="post" name="userInfo">
...
<input type="password" name="password" ng-show="!showPassword" ng-model="password" required>
<input type="text" name="plainPassword" ng-show="showPassword" ng-model="password" required>
<span class="error" ng-show="userInfo.password.$error.required">必須</span>
<span class="valid" ng-show="userInfo.password.$valid">OK</span>
<div><input type="checkbox" id="showPassword" ng-model="showPassword"><label class="comment" for="showPassword">パスワードを表示する</label></div>
... 
</form> 

これだけできます。

ただし、required 以外のバリデーションチェックを定義すると、チェックエラーの際にng-modelの値が undefined になるため、同名で定義されたもう一方の値を更新してしまっているようで、結果おかしな動きになってしまいました。。。この場合は、JavaScriptを自前で用意して定義する必要がありそうです。ng-change 属性を定義すれば、自前で実装したJavaScript内でイベントをフックできます。

正規表現よるバリデーションチェック

正規表現を用いたバリデーションチェックも定義することができます。メールアドレスに対しては、今回は手抜きで gmail であるかどうかをチェックしています。

<form method="post" name="userInfo">
...
<input type="mail" name="mail" ng-model="mail" ng-pattern="/^.+@gmail\.com$/">
<span class="comment">任意</span>
<span class="error" ng-show="userInfo.mail.$error.pattern">gmailのみですー</span>
<span class="valid" ng-show="userInfo.mail.$valid">OK</span>
... 
</form> 

エラーハンドリングについては、前述した通りです。

入力文字数を扱う

わかりやすく、twitterの残り入力文字数としてみました。

<form method="post" name="userInfo">
...
<span ng-show="userInfo.twitter.$error.maxlength"><ng-show="userInfo.twitter.$error.maxlength" class="error">140文字こえてますよー</span>
<span ng-hide="userInfo.twitter.$error.maxlength">{{140 - twitter.length}}</span>
<textarea name="twitter" rows="10" ng-model="twitter" ng-maxlength="140"></textarea>
<span class="comment">任意</span>
<span class="valid" ng-show="userInfo.twitter.$valid">OK</span>
... 
</form> 

{{140 - twitter.length}}とすることで、残りの入力文字数を変更させています。

140文字を超えた場合は、エラーメッセージが表示されます。

formのsubmitボタンの状態変更

<form method="post" name="userInfo">
...
<input type="submit" value="登録" ng-disabled="userInfo.$invalid">
<span ng-show="userInfo.$invalid" class="comment">未入力の項目や正しく入力できていない項目があります</span>
<span ng-show="userInfo.$valid" class="comment">入力チェックOK!</span>
... 
</form> 

このsubmitボタンは、デフォルトではdisableとなっていてクリックすることができません。form内の項目全てのバリデーションチェックがOKになったときのみクリックできるようになります。こういったよくある仕組みも、自前で実装するのは結構手間だったりします。

AngularJS では、form内の要素すべてがvalidかinvalidか、さらに個々の要素に対してもvalidかinvalidの可否を取得することができます。先述した userInfo.userName.$valid であったり、この form 自体の userInfo.$valid がそれにあたります。

userInfo.userName.$valid であれば、 ng-minlength、ng-maxlength、required の全てがチェックOKであれば true になります。どれか1つでもエラーなら false になります。同様に userInfo.$valid とすれば、全入力項目でのチェックがOKであれば true となります。

とにかく簡単でした

コードを見ていただければわかると思いますが、JavaScript を自前で用意しなくてもこれだけのことができてしまいます。モックの作成でも利用できますし、WEBサイトのいわゆるマイページのようなSSL配下のページ群もWEBアプリケーション化することを目的として利用することもできるのではないかなーと思いました。