「Ruby on Rails Tutorial」のサンプルアプリをAngularJSとBootstrap3を使う形にして作成します。ユーザー登録時のバリデーションチェックについては、AngularJS側でのチェックは前回設定しました。今回は、サーバー側でRailsの機能でバリデーションチェックを行い、エラーコメントをAngularJSビューで表示します。
(1)Railsモデルにバリデーションを登録
nameとemailカラムに対し、下記チェックを行います。
・password
文字列長6
・name
存在チェック、文字列長20
・email
存在チェック、フォーマットチェック、一意性チェック、
$ vi app/models/user.rb
class User
(2)バリデーションエラーメッセージをAngularJSコントローラで設定
Rails側でのバリデーションエラーメッセージは、下記のようにJSONフォーマットでリターンしています。
render json: @user.errors, status: :unprocessable_entity
1)underscore.jsをインクルード
以下でunderscoreの機能を使用するので下記設定を追加します。
・underscore-min.jsを入手し、vendor/assets/javascriptsディレクトリに配置。
・application.jsに追加
$ vi app/assets/javascripts/application.js
//= require underscore-min
下記②でundersocoreのeachメソッドを使ってサーバーからリターンされたエラーメッセージを取り出します。
※underscoreのeachメソッドの使用方法
①配列
var array = [10, 20, 30];
_.each(array, function(element, index) {
console.log(element + ' : ' + index);
});
// 結果
10 : 0
20 : 1
30 : 2
②オブジェクト
var object = {
name: "test",
email: "test@example.com
};
_.each(object, function(value, key) {
console.log(key + ' : ' + value);
});
// 結果
name : "test"
email : "test@example.com"
2)AngularJSの"UsersNewCtrl"コントローラにバリデーションエラーメッセージの設定追加
$resourceサービスのcreateアクション失敗時のコールバック関数にバリデーションメッセージを設定します。
$ vi app/assets/javascripts/mymodule.js.erbmyModule.controller("UsersNewCtrl", function($scope, userResource, $location, flashService) { : $scope.submit = function() { : function failure(response) { _.each(response.data, function(errors, key) { _.each(errors, function(e) { $scope.userNewForm[key].$dirty = true; $scope.userNewForm[key].$setValidity(e, false); }); }); } userResource.create($scope.user, success, failure); }; $scope.errorMessage = function(name) { var s = $scope.userNewForm[name].$error; result = []; _.each(s, function(key, value) { result.push(name + " " + value); }); return result.join(", "); }; });
●上記コードの説明
・入力データ
data:Object
email:Array
0: "has already taken"
password:Array
0: "is too short (minimum is 6 characters)"
①failure(response)関数
・フォーム内のemailとpasswordの$dirty属性をtrueにする。
$scope.userNewForm[key].$dirty = true;
・$setValidityメソッドを使って、email、passwordのフォームコントロールのステータスをinvalidにし、$error属性にエラーメッセージをセットする。
$scope.userNewForm[key].$setValidity(e, false);
②errorMessage関数
上記①で$error属性にセットしたエラーメッセージを取り出し、ビューにリターンする。
"email has already taken"、"password is too short (minimum is 6 characters)"のメッセージがリターンされる。
3)フォームのエラー状態をリセット
サーバーからのエラーメッセージによって設定したフォームのエラー状態は、その後クライアント側でフォーム内に正しい値を入力しても解除されないので、エラーリセットボタンを設け、フォームのエラー状態を解除する。
$scope.resetError = function(form) {
if (form) {
form.$setPristine();
form.$setUntouched();
form.$setValidity();
}
}
(3)AngularJSビューにエラーを表示する記述追加
$ vi app/assets/templates/users/new.html.erb<div ng-controller="UsersNewCtrl" class="row"> <style> form.ng-invalid.ng-dirty { background-color: lightpink; } form.ng-valid.ng-dirty { background-color: lightgreen; } form { padding: 10px;} </style> <div class="col-md-6 col-md-offset-3"> <h1 class="text-center">Sign up</h1> <form name="userNewForm" novalidate> <div class="well"> <div class="form-group" ng-class="{'has-error': userNewForm.name.$invalid && userNewForm.name.$dirty}"> <label>Name</label> <input name="name" class="form-control" ng-model="user.name" required ng-maxlength="20" /> <span class="text-danger" ng-show="userNewForm.name.$dirty && userNewForm.name.$error.required"> Name can't be blank </span> <span class="text-danger" ng-show="userNewForm.name.$dirty && userNewForm.name.$error.maxlength"> Name is too long (maximum is 20 characters) </span> <span class="text-danger" ng-show="userNewForm.name.$dirty && userNewForm.name.$invalid"> {\{errorMessage('name')}} </span> </div> <div class="form-group" ng-class="{'has-error': userNewForm.email.$invalid && userNewForm.email.$dirty}"> <label>Email</label> <input type="email" name="email" class="form-control" ng-model="user.email" required /> <span class="text-danger" ng-show="userNewForm.email.$dirty && userNewForm.email.$error.required"> Email can't be blank </span> <span class="text-danger" ng-show="userNewForm.email.$dirty && userNewForm.email.$error.email"> Email is invalid </span> <span class="text-danger" ng-show="userNewForm.email.$dirty && userNewForm.email.$invalid"> {\{errorMessage('email')}} </span> </div> <div class="form-group" ng-class="{'has-error': userNewForm.password.$invalid && userNewForm.password.$dirty}"> <label>Password</label> <input type="password" name="password" class="form-control" ng-model="user.password" required ng-minlength="6" /> <span class="text-danger" ng-show="userNewForm.password.$dirty && userNewForm.password.$error.required"> Password can't be blank </span> <span class="text-danger" ng-show="userNewForm.password.$dirty && userNewForm.password.$error.maxlength"> Password is too short (minimum is 6 characters) </span> <span class="text-danger" ng-show="userNewForm.password.$dirty && userNewForm.password.$invalid"> {\{errorMessage('password')}} </span> </div> <div class="form-group" ng-class="{'has-error': userNewForm.password_confirmation.$invalid && userNewForm.password_confirmation.$dirty}"> <label>Password_confirmation</label> <input type="password" name="password_confirmation" class="form-control" ng-model="user.password_confirmation" required ng-minlength="6" /> <span class="text-danger" ng-show="userNewForm.password_confirmation.$dirty && userNewForm.password_confirmation.$error.required"> Password_confirmation can't be blank </span> <span class="text-danger" ng-show="userNewForm.password_confirmation.$dirty && userNewForm.password_confirmation.$error.maxlength"> Password_confirmation is too short (minimum is 6 characters) </span> <span class="text-danger" ng-show="userNewForm.password_confirmation.$dirty && userNewForm.password_confirmation.$invalid"> {\{errorMessage('password_confirmation')}} </span> </div> <button ng-click="submit()" class="btn btn-primary" ng-disabled="userNewForm.$invalid" > Create my account </button> <button ng-click="resetError(userNewForm)" class="btn btn-primary"> Reset Error </button> </div> </form> </div> </div>