AngularJS 1.3のフォームまわりの機能強化
こんにちは中川です。
先日、AngularJS 1.3 がリリースされましたね。
動作速度の改善や、メモリ消費量の削減などパフォーマンス面での改善もうれしいところですが、
機能的にはフォーム関連の機能強化が特にうれしく感じましたので、紹介したいと思います。
■ ngModel.$validators
https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
ngModel.$validators を使うと、独自のバリデーション関数を簡単に定義することができるようになりました。
以下の例のように、入力値を引数で受け取り、返り値で真偽値を返す関数を$validatorsオブジェクトに定義します。
$validatorsのキー(ここではvalidCharacters)が、エラーメッセージ表示時などの参照用に利用できます。
ngModel.$validators.validCharacters = function(modelValue, viewValue) {
var value = modelValue || viewValue;
return /[0-9]+/.test(value) & &
/[a-z]+/.test(value) & &
/[A-Z]+/.test(value) & &
/\W+/.test(value);
};
■ ngModel.$asyncValidators
サーバへの問い合わせが必要な場合など、非同期の処理がある場合のバリデーションにも対応しています。
以下の例のように、ngModel.$asyncValidators にpromiseを返す関数を定義します。
ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
var value = modelValue || viewValue;
// Lookup user by username
return $http.get('/api/users/' + value).
then(function resolved() {
//username exists, this means validation fails
return $q.reject('exists');
}, function rejected() {
//username does not exist, therefore this validation passes
return true;
});
};
今まで、パスワード確認入力やユーザー名重複のサーバ問い合わせなど、
controller(service)の値を組み合わせたバリデーションをdirectiveで
指定するのが面倒で、ui-validateというcontrollerの関数定義を指定できる外部モジュールを利用していました。
ui-vaidateでも今まで対応できていたので、別にそれでもいいといえばいいのですが、
しかし、1.3からは以下のような汎用的なdirectiveを定義すれば、とても素直に指定することができます。
app.directive('appValidators', function () {
return {
require: 'ngModel',
scope: {
appValidators: '=',
},
link: function (scope, elem, attrs, ctrl) {
var validators = scope.appValidators || {};
angular.forEach(validators, function (val, key) {
ctrl.$validators[key] = val;
});
}
};
});
app.directive('appAsyncValidators', function () {
return {
require: 'ngModel',
scope: {
appAsyncValidators: '='
},
link: function (scope, elem, attrs, ctrl) {
var asyncValidators = scope.appAsyncValidators || {};
angular.forEach(asyncValidators, function (val, key) {
ctrl.$asyncValidators[key] = val;
});
}
};
});
利用時は以下のように、controllerでオブジェクトをテンプレート側のapp-validators属性で渡せます。
app.controller('AppCtrl', function($scope) {
$scope.user_name_validators = {
hoge: function (modelValue, viewValue) {
var val = modelValue || viewValue;
return val == 'hoge';
},
fuga: function (modelValue, viewValue)