5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AngularでDBユニーク列の利用可能問い合わせバリデーション

Posted at

 AngularJSでフォームを作成すると、バリデーションが色々いじれて便利です。

 ここまで色々出来るようになってくるとJS側でかなりガッツリバリデーションを済ませてしまって登録のHTTPレスポンスは正常応答のみを前提として、異常応答は極端な処理ですませる…としたくなるところです。

 ただちょっと変わったバリデーションをかけようとするとたちまちうまく行かなくなるのがフレームワーク系の辛い所。

 今回は、入力されたログインIDが利用可能かの確認をDBに問い合わせる、というバリデーションをやってみます。

とりあえずコード

html
            <input type="text" class="pure-u-1" name="login_id" id="form_login_id" 
                   ng-model="post.login_id" ng-pattern="/^[0-9a-zA-Z_]+$/" 
                   ng-maxlength="10" ng-change="validateLoginId()" required/>
            <div class="panel-red" ng-show="joinForm.login_id.$error.pattern">
                ログインIDは半角英数字で入力してください。
            </div>
            <div class="panel-red" ng-show="joinForm.login_id.$error.unique">
                そのログインIDは使用できません。
            </div>
            <div class="panel-green" 
                   ng-show="joinForm.login_id.$dirty &&!joinForm.login_id.$error.inquired &&!joinForm.login_id.$error.unique">
                そのログインIDは使用可能です。
            </div>
            <div class="panel-red" ng-show="joinForm.login_id.$error.maxlength">
                ログインIDは10文字以内で入力してください。
            </div>

んでngChangeは以下の様に実装する。

coffeescirpt
  $scope.validateLoginId = ()->
    $scope.joinForm.login_id.$setValidity "inquired",false #DB照会待ちエラーのセット   
    startVal = $("#form_login_id").val()
    setTimeout ->
      do (val = startVal) ->
        if val == $("#form_login_id").val()
          Config.log "test for #{val} #{$("#form_login_id").val()}"
          # http発行
          result = $http(
            method: "POST"
            url: "#{Config.api}user/valid_user_id/#{val}"
          )
          result.success (data,status,headers,config) ->
            $scope.joinForm.login_id.$setValidity "inquired",true    
            $scope.joinForm.login_id.$setValidity "unique",true
          result.error (data,status,headers,config) ->
            $scope.joinForm.login_id.$setValidity "inquired",true    
            $scope.joinForm.login_id.$setValidity "unique",false
    ,1000

joinFormはフォーム名、Config.logconsole.logのラッパー、Config.apiはAPIドメインです。
コピペで動くかは微妙。参考程度に。

ポイント

エラーのセット

今回はinquired uniqueの二つのエラーを登録しています。

  • inquired: DBへの照会を済ませているかのチェック
  • unique: DBにいまだ登録されていないキーであることのチェック

DBに確認しにいく、という処理の都合上、この二つのエラーをきっちりわけないと
DB確認中の数秒間はバリデーションOKになってしまう…という問題が発生します。

angularのエラーのセットは$setValidity で。form.$setValidityとしてもいいし、form.elem.$setValidityとしてもいい。同じエラー名で複数の要素のエラーを判別したいなら後者の方がオススメです。

ng-changeの問題

ng-clickは$eventがとれるのに、ng-changeは$eventが取れない。ので、ng-change内は上記の用にIDベタ書きで要素取得するしかなさそうです。
あと一文字一文字の変更毎にng-changeが発火してしまうので、setTimeoutを使って1秒間変動がなければ、という条件をつけています。処理フロー的には

  1. フォーム内容の変更
  2. 問い合わせ済みエラーのinquiredを発行
  3. フォームデータの保存
  4. 1秒待つ
  5. フォームデータの取得&&1秒前のフォームデータ(3.で保存)と比較
  6. 変化がなければDBへの問い合わせを実行
  7. DBからの応答を受け取ったら、問い合わせ済みエラーのinquiredを削除
  8. 応答でNGとの内容なら重複エラーのuniqueを発行

みたいな感じです。

気になる点

DB問い合わせ応答の順番入れ替わりについて

 無数にサーバ問い合わせを行うと、順番が入れ替わって結果が返ってくる事がある。
 今回の処理では1回めの問い合わせから2回めの問い合わせまでには少なくとも1秒の猶予があるので、1秒以内にDB問い合わせが帰ってくれば順番の入れ替わりについての心配は無いのだけれども…
 きっちり処理を固めるなら多分そのへんに手をいれることになるのか…

ng-showがごちゃごちゃする点について

 イライラする。

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?