AngularJSでサーバサイドのバリデーションエラーをハンドリング

CakePHPのバリデーションエラーをフロント側で表示する

バックエンド側の準備

Modelの作成

AngularJSからCakePHPのRestful APIにAjaxリクエストを行うCRUDアプリケーションのサンプルを作成した。

本エントリーではそのサンプルアプリにサーバサイドバリデーションを追加して、エラーメッセージをフロント側に表示する部分を作ってみたい(参考 AngularJSからRestfulなリクエストを送信)。

CakePHP側でバリデーションエラーを制御するためModelを作成する。

app\Model\Recipe.php

というファイル名で、以下のようにバリデーションの設定を行う。

class Recipe extends AppModel {
	public $name = 'Recipe';

	public $validate = array(
		'title' => array(
	        'notEmpty' => array(
	            'rule' => 'notEmpty',
	            'message' => '題名を記入してください。',
	            'last' => true
	         ),
		'maxLength' => array(
		    'rule' => array('maxLength', '50'),
	            'message' => '題名は50文字以内で入力してください。'
		),
		),
		'body' => array(
	        'notEmpty' => array(
	            'rule' => 'notEmpty',
	            'message' => '本文を記入してください。',
	            'last' => true
	         ),
			'maxLength' => array(
				'rule' => array('maxLength', '3000'),
	            'message' => '本文は3000文字以内で入力してください。'
			),
		),
	);
}

データ登録APIの開発

Controller側では、

  • Modelの読み込み設定
  • データ登録用のAPIを追加

という2つの作業が必要。

app\Controller\RecipesController.php

にModelの利用設定を行う。

public $name = 'Recipes';
public $uses = array('Recipe');

AngularJSからHTTPのPOSTメソッドでリクエストされるAPIは以下のようにした。

public function add() {
	
        // AngularJSからパラメータとして送信されたデータをCakePHP側でバリデーションしやすいように整形
        $data['Recipe']['title'] = $this->params['data']['Recipe']['title'];
	$data['Recipe']['body']  = $this->params['data']['Recipe']['body'];

        // バリデーションとデータ追加処理
        if ($this->Recipe->save(h($data)))
	{
                $message = array(
                'text' => __('Saved'),
                'type' => 'success'
                );

	        $this->set(array(
	            'message' => $message,
	            '_serialize' => array('message')
	        ));
        }
		
	else
	{
		$this->autoRender = false;
	    	$this->response->statusCode(400); // バリデーションエラーの場合はHTTP400をレスポンスしてAngularJS側でerrorコールバックを発生させる

                $error = array(
                    'text' => __('Error'),
                    'type' => 'error',
		    'body' => $this->Recipe->validationErrors // エラーメッセージをフロント側に渡す
                );
			
		$json = json_encode($error);
		$this->response->body($json);
        }
}

Controller内でデータを整形

AngularJSからのAjaxリクエストで送信されてくるパラメータは、

$this->params['data']['Recipe']['title'];
1

としてAPI側で扱うことが可能。CakePHPのフォームからsubmitでデータを送信する場合は、

1
$this->request->data['Recipe']['title'];

となるが、Ajaxリクエストの場合はこの点が異なるので注意したい。

バリデーションエラーが発生した場合は、HTTP400のステータスコードを返して、AngularJS側で意図的にerrorコールバックが生じるようにしている。その際に、

$this->Recipe->validationErrors

CakePHPのModelで設定したエラーメッセージなどをまとめて、フロント側に送信する処理を追加してある。

$this->response->body($json);

この処理で送り返されたデータをフロント側でエラーメッセージとして利用する。

フロント側のコールバック処理

errorコールバック内でバックエンドからのレスポンスデータにアクセス

AngularJSサイドのデータ登録用のControllerは以下のようにした。

ajs.controller('NewPostCtrl', function($scope, $rootScope, $http, $location) {

        $scope.post = {};

        $scope.savePost = function() {
            var _data = {};
            _data.Recipe = $scope.recipe;
            $http.post($rootScope.appUrl + '/recipes.json', _data)
			.success(function(data, status, headers, config) {
				$location.path('/posts');
                        })
			.error(function(data, status, headers, config) {
				console.log(data);
                                console.log(data.body);
                                console.log(data.body.title);
				$scope.error = data.body;
			});
        }
});

NewPostCtrlは、AngularJSからバックエンド側のデータ登録APIに対してリクエストを行うControllerになる。errorコールバックの部分にログ出力の設定をした上で、フォームからtitle無しで送信したのが下の画像。

angularjs-validation-error

フロント側にバリデーションのエラーメッセージがレスポンスされていることが確認できる。フォームのエラー表示はcssフレームワークのFoundationで整えた。

部分HTMLでデータを表示

AngularJSのNewPostCtrlに対応する部分HTMLを以下のように組んだ

<div ng-controller="NewPostCtrl">
    <h2 class="page-header">New Recipe</h2>
    <form novalidate class="form-horizontal">
    <fieldset>    
        <div class="control-group">
            <label class="control-label" for="title">Title
            <input id="title" type="text" class="input-block-level" required="true" ng-model="recipe.title"></input>
		</label>
	    <small class="error" ng-show="error.title">{{error.title}}</small>
        </div>
        <div class="control-group">
            <label class="control-label" for="body">Body</label>
            <div class="controls">
                <textarea id="body" class="input-block-level" rows="10" ng-model="recipe.body" required="true"></textarea>
		<small class="error" ng-show="error.body">{{error.body}}</small>
            </div>
        </div>
    </fieldset> 
    <div class="form-actions">			
        <button class="btn btn-primary" ng-click="savePost()"><b class="fa fa-plus-square"></b>Add Recipe</button>
    </div>
    </form>
</div>

NewPostCtrlのerrorコールバック内で、$scopeにバックエンド側からレスポンスされたエラーメッセージを渡している点にも注目したい。$scopeにエラーを渡すことで上の部分HTMLで

  • ng-showを利用したエラーの有無チェック
  • エラーメッセージの展開

を行っている。

また、ng-modelを設定し、Ajaxリクエスト時のパラメータデータを構成している点にも注意したい。

参考
angular-seedでAngularJS入門

Webエンジニアブログにコメント

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

AngularJSでサーバサイドのバリデーションエラーをハンドリングの記事にコメントを投稿