本当に苦労しました。いつもはvue.jsをひたすら触っているので、angularjsを理解するのに苦労しました。
リアルタイムのエラー表示を作るのは簡単ですが、どこでも使い回せるように作りました。
・ 最初の画面ロード時はエラーが表示されてないで、入力し始めてエラーが表示される。
・ input clearableができる
・ なるべく他のところにも共通で使用できる
なぜangularjsかというと、仕事で使ったからです笑。AngularJSは癖があって難しかったですが、なんとか要件を満たすようにできました。
まずコードを説明し、いくつか要点をまとめておきます。
form.html
<!doctype html>
<html ng-app="form" >
<head>
<meta charset="utf-8">
</head>
<body ng-controller="FormCtrl">
<form name="myForm">
<input-form type="{{formData.type}}" rules="formData.rules" label="{{formData.label}}" param="formData.param" trim="{{formData.trim}}" placeholder="{{formData.placeholder}}" show-error="formData.showError" clearable="formData.clearable"></input-form>
</form>
</body>
</html>
template.html
ディレクティブで読み込むhtml
<div class="form-group">
<label class="col-sm-3 control-label">{{label}}</label>
<div class="col-sm-9">
<input placeholder="{{placeholder}}" ng-model="param" ng-required="rules.required.status" ng-maxlength="rules.maxlength.max" ng-minlength="rules.minlength.min" ng-trim="trim">
<span ng-show="clearable && param" ng-click="clearText()" form-control-feedback" uib-tooltip="clear">x</span>
<div class="error-messages" ng-show="showError && error && dirty">
<div ng-show="errors.required">{{rules.required.message}}</div>
<div ng-show="errors.pattern">{{rules.regex.message}}</div>
<div ng-show="errors.maxlength">{{rules.maxlength.message}}</div>
</div>
</div>
</div>
var app = angular.module('form', []);
/* Controllers */
// patternとmaxlengthを併用する場合、patternが優先される
// https://stackoverflow.com/questions/18516001/angularjs-ng-minlength-validation-is-not-working-form-still-being-submitted
function FormCtrl($scope) {
$scope.formData = {
label: "label1",
placeholder: 'placeholderだよ',
param: "aaa",
trim: false,
clearable: true,
type: "text",
showError: true,
rules: {
required: {
status: true,
message: 'required'
},
minlength: {
min: 2,
message: 'min'
},
maxlength: {
max: 10,
message: 'max'
},
regex: {
message: 'pattern',
pattern: /^[0-9]{3}-[0-9]{4}$/
}
},
};
};
// 入力値がvalidationに引っかかっている場合、paramはundefinedになる
// https://www.buildinsider.net/web/angularjstips/0060
// placeholder="{{placeholder}}" ng-attr-placeholder="{{placeholder}}"
// そのためpatternの処理のみ別で行う
app.directive('inputForm', function() {
var scope, linkFn, template, success, checkError, clearText;
template = 'path to template.html';
scope = {
label: '@',
placeholder: '@',
trim: '@',
clearable: '=',
showError: '=',
rules: '=',
param: '='
};
linkFn = function(scope, el, attrs, formCtrl) {
var regexResult;
scope.clearText = function() {
formCtrl.$setDirty(false);
scope.param = '';
};
if ( formCtrl.$name === undefined ) throw new Error('please set form unique name');
scope.errors = formCtrl.$error;
scope.$watch('param', function(newValue, oldValue) {
scope.errors.pattern = false;
scope.error = formCtrl.$invalid;
regexResult = scope.rules.regex.pattern.test(newValue);
if ( newValue && !regexResult ) {
scope.error = !regexResult;
scope.errors.pattern = !regexResult;
}
scope.dirty = formCtrl.$dirty
});
};
return {
restrict: 'E',
scope: scope,
template: template,
require: '^form',
link: linkFn
};
});
ポイント
restrict
restrictのパラメーターは以下のようになっています。
option | Description |
---|---|
E | タグ |
A | 属性 |
C | クラス |
M | コメント |
作成したディレクティブの使い方によって選択して下さい。
今回はinputForm
を<input-form></input-form>
のようにタグとして使用したのでrestrict
はE
を指定しています。
<div input-form="something"></div>
のように属性として使用した場合は、restrict
をA
にします。
<span class="my-dir: exp;"></span>
のようにクラスとして使用する場合は、C
にして下さい。
そしてM
はあまり使わないと思いますが、<!-- directive: my-dir exp -->
のように使用します。
scope
scope
のパラメーターは、以下のようになっています。
Property | Description |
---|---|
@ | 親から子への単方向バインド |
= | 双方向バインド |
& | 関数 |
このようになっており、値の渡し方が異なります。
@は、{{rules.formData.label}}
のように{{}}
をつけて親から子へ値を渡しますが、=の双方向バインドでは、clearable="formData.clearable"
のように。、{{}}
をつけて渡す必要はありません。
最初はこれにちょっとだけハマりました。
template
template
は別のファイルに分けて読み込んでください。
link
formCtrl
で使用できるパラメータは以下になっています。
Property | Description |
---|---|
$dirty | form内のinputを一度でも変更した |
$pristine | form内のinputが全て変更されていない |
$valid | form内のinputが全てvalid |
$invalid | form内のinputにinvalidがある |
$submitted | formがsubmitされた |
このようになっており、今回は$dirty
と$valid
を使用しています。
初期状態ではエラーを表示させないので$dirty
をscopeに保持して、pattern
以外のエラー判定は$valid
を使っています。
validation
ここで一つ注意しておきたいのが、pattern
です。デフォルトの値を設定したい場合、patternの正規表現にマッチしていないとデフォルトでundefined
になってしまいます。そのためデフォルトの値が設定できません!!
それを回避するために、
regexResult = scope.rules.regex.pattern.test(newValue);
if ( newValue && !regexResult ) {
scope.error = !regexResult;
scope.errors.pattern = !regexResult;
}
このようにして、pattern
だけ個別でエラー判定しています。
他の記事ではng-model-options="{ allowInvalid: true }"
という設定をしていたのですが、私の環境では動きませんでしたし、ブラウザによって動かないことが多くあるそうです。なので、少々面倒ですが、自分で設定する以外に方法はないと思います。
まとめ
AngularJSは少し使いずらいですね。一応CodePenで作ったものをこちらに置いておきます。
AngularJSを使っている人は少ないと思いますが、誰かの参考になれば幸いです、それでは〜。