AngularJS Form Validation
今天,我们来看看 Angular 是怎样帮我们做表单验证的。我们会集中讲怎么用 Angular (就像另外一篇文章: Submitting AJAX Forms: The AngularJS Way). 别担心,不需要看那篇,打个广告而已。
我们集中将客户端验证,和怎样用 Angualr 内置表单属性。我们来个快速实现的例子。
例子
Html:
<div ng-app="validationApp" ng-controller="mainController">
<div class="container">
<div class="row">
<div class="col-sm-6">
<!-- =================================================================== -->
<!-- FORM ============================================================== -->
<!-- =================================================================== -->
<form name="userForm" ng-submit="submitForm()" novalidate>
<!-- NAME -->
<div class="form-group" ng-class="{ 'has-error' : userForm.name.$invalid && !userForm.name.$pristine }">
<label>Name</label>
<input type="text" name="name" class="form-control" ng-model="user.name" required>
<p ng-show="userForm.name.$invalid && !userForm.name.$pristine" class="help-block">You name is required.</p>
</div>
<!-- USERNAME -->
<div class="form-group" ng-class="{ 'has-error' : userForm.username.$invalid && !userForm.username.$pristine }">
<label>Username</label>
<input type="text" name="username" class="form-control" ng-model="user.username" ng-minlength="3" ng-maxlength="8">
<p ng-show="userForm.username.$error.minlength" class="help-block">Username is too short.</p>
<p ng-show="userForm.username.$error.maxlength" class="help-block">Username is too long.</p>
</div>
<!-- EMAIL -->
<div class="form-group" ng-class="{ 'has-error' : userForm.email.$invalid && !userForm.email.$pristine }">
<label>Email</label>
<input type="email" name="email" class="form-control" ng-model="user.email">
<p ng-show="userForm.email.$invalid && !userForm.email.$pristine" class="help-block">Enter a valid email.</p>
</div>
<button type="submit" class="btn btn-primary" ng-disabled="userForm.$invalid">Submit</button>
</form>
</div>
<div class="col-sm-6">
<!-- =================================================================== -->
<!-- VALIDATION TABLES ================================================= -->
<!-- =================================================================== -->
<div class="row">
<div class="col-xs-3">
<h3>Form</h3>
<table class="table table-bordered">
<tbody>
<tr>
<td ng-class="{ success: userForm.$valid, danger: userForm.$invalid }">Valid</td>
</tr>
<tr>
<td ng-class="{ success: userForm.$pristine, danger: !userForm.$pristine }">Pristine</td>
</tr>
<tr>
<td ng-class="{ success: userForm.$dirty }">Dirty</td>
</tr>
</tbody>
</table>
</div>
<div class="col-xs-3">
<h3>Name</h3>
<table class="table table-bordered">
<tbody>
<tr>
<td ng-class="{ success: userForm.name.$valid, danger: userForm.name.$invalid }">Valid</td>
</tr>
<tr>
<td ng-class="{ success: userForm.name.$pristine, danger: !userForm.name.$pristine }">Pristine</td>
</tr>
<tr>
<td ng-class="{ success: userForm.name.$dirty }">Dirty</td>
</tr>
</tbody>
</table>
</div>
<div class="col-xs-3">
<h3>Username</h3>
<table class="table table-bordered">
<tbody>
<tr>
<td ng-class="{ success: userForm.username.$valid, danger: userForm.username.$invalid }">Valid</td>
</tr>
<tr>
<td ng-class="{ success: userForm.username.$pristine, danger: !userForm.username.$pristine }">Pristine</td>
</tr>
<tr>
<td ng-class="{ success: userForm.username.$dirty }">Dirty</td>
</tr>
</tbody>
</table>
</div>
<div class="col-xs-3">
<h3>Email</h3>
<table class="table table-bordered">
<tbody>
<tr>
<td ng-class="{ success: userForm.email.$valid, danger: userForm.email.$invalid }">Valid</td>
</tr>
<tr>
<td ng-class="{ success: userForm.email.$pristine, danger: !userForm.email.$pristine }">Pristine</td>
</tr>
<tr>
<td ng-class="{ success: userForm.email.$dirty }">Dirty</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
CSS:
body { padding-top:30px; }
Javascript:
<!-- lang: js -->
// create angular app
var validationApp = angular.module('validationApp', []);
// create angular controller
validationApp.controller('mainController', function($scope) {
// function to submit the form after all validation has occurred
$scope.submitForm = function() {
// check to make sure the form is completely valid
if ($scope.userForm.$valid) {
alert('our form is amazing');
}
};
});
好了现在我们知道需要什么了,来看看怎么做。
$valid, $invalid, $pristine, $dirty
Angular 为表单提供了便于验证的属性。它们给我们表单或它的输入的各种信息,并且可应用到表单和输入上。
<form name>.<angular property>
<form name>.<input name>.<angular property>
我们用一个简单的表单做演示。
我们需要 2个文件:
index.html
用于显示表单的代码app.js
Angular 应用和控制器(所有代码)index.html
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<!-- CSS ===================== -->
<!-- load bootstrap -->
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
<style>
body { padding-top:30px; }
</style>
<!-- JS ===================== -->
<!-- load angular -->
<script src="http://code.angularjs.org/1.2.6/angular.js"></script>
<script src="app.js"></script>
</head>
<!-- apply angular app and controller to our body -->
<body ng-app="validationApp" ng-controller="mainController">
<div class="container">
<div class="col-sm-8 col-sm-offset-2">
<!-- PAGE HEADER -->
<div class="page-header"><h1>AngularJS Form Validation</h1></div>
<!-- FORM -->
<!-- pass in the variable if our form is valid or invalid -->
<form name="userForm" ng-submit="submitForm(userForm.$valid)" novalidate> <!-- novalidate prevents HTML5 validation since we will be validating ourselves -->
<!-- NAME -->
<div class="form-group">
<label>Name</label>
<input type="text" name="name" class="form-control" ng-model="name" required>
</div>
<!-- USERNAME -->
<div class="form-group">
<label>Username</label>
<input type="text" name="username" class="form-control" ng-model="user.username" ng-minlength="3" ng-maxlength="8">
</div>
<!-- EMAIL -->
<div class="form-group">
<label>Email</label>
<input type="email" name="email" class="form-control" ng-model="email">
</div>
<!-- SUBMIT BUTTON -->
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div><!-- col-sm-8 -->
</div><!-- /container -->
</body>
</html>
有几个点值得注意的:
ng-model
应用到输入框,使得我们表单中的数据可以绑定到 Angular 变量上ng-minlength
和 ng-maxlength
会让 username 适用这些验证规则name
设为 requiredemail
设为 type=“email”除了 ng-minlength
和 ng-maxlength
外,Angular 提供了许多验证规则。
下面是 Angular 输入框可用验证属性的一个演示。查看 Angular input directive 获取更多信息。
<!-- lang: js -->
<input
ng-model="{ string }"
name="{ string }"
required
ng-required="{ boolean }"
ng-minlength="{ number }"
ng-maxlength="{ number }"
ng-pattern="{ string }"
ng-change="{ string }">
</input>
好了现在我们有了一个简单的表单,下面让我们来创建 Angualr 的 app 和 controller。应用到页面的 ng-app
和 ng-controller
上。
app.js
<!-- lang: js -->
// app.js
// create angular app
var validationApp = angular.module('validationApp', []);
// create angular controller
validationApp.controller('mainController', function($scope) {
// function to submit the form after all validation has occurred
$scope.submitForm = function(isValid) {
// check to make sure the form is completely valid
if (isValid) {
alert('our form is amazing');
}
};
});
novalidate
我们在表单中使用 novalidate
,因为我们要自己处理验证。如果我们让表单自己处理的话,看起来相当丑逼。
ng-disabled
好玩的来了。我们现在开始使用 Angular 属性 。首先让我们禁用我们的提交按钮。我们希望我们的表单是 $invalid
的时候让它不可用。
<!-- lang: js -->
<!-- index.html -->
...
<!-- SUBMIT BUTTON -->
<button type="submit" class="btn btn-primary" ng-disabled="userForm.$invalid">Submit</button>
...
只需要一丁点代码(ng-disabled
),我们的表单按钮就会在表单 $invalid
的时候被禁用。意思就是,我们的 name
输入字段必须有值,email
输入字段必须是可用 email 格式。
ng-show
如果我们表单设置的规则可用的话,ng-valid 和 ng-invalid 都会被自动判定。
让我们给我们的输入框都加上异常信息,如果他们是没有 $valid
并且已经被用到了的话(我们肯定不希望在没填入之前就有异常提示啊)。
<!-- lang: js -->
<!-- index.html -->
...
<!-- NAME -->
<div class="form-group">
<label>Name</label>
<input type="text" name="name" class="form-control" ng-model="name" required>
<p ng-show="userForm.name.$invalid && !userForm.name.$pristine" class="help-block">You name is required.</p>
</div>
<!-- USERNAME -->
<div class="form-group">
<label>Username</label>
<input type="text" name="username" class="form-control" ng-model="user.username" ng-minlength="3" ng-maxlength="8">
<p ng-show="userForm.username.$error.minlength" class="help-block">Username is too short.</p>
<p ng-show="userForm.username.$error.maxlength" class="help-block">Username is too long.</p>
</div>
<!-- EMAIL -->
<div class="form-group">
<label>Email</label>
<input type="email" name="email" class="form-control" ng-model="email">
<p ng-show="userForm.email.$invalid && !userForm.email.$pristine" class="help-block">Enter a valid email.</p>
</div>
...
只需要像这样加几个 ng-show
, Angular 就会自动的决定是否显示异常,基于输入框的$invalid
和 $pristine
属性。
Angular 已经为我们的输入框和表单提供了一些样式,用于显示验证是否通过。可以参考我们文章一开始例子中的样式(ng-valid
,ng-invalid
,ng-pristine
,ng-dirty
)。
如果你高兴,你也可以用 CSS 定义这些样式。你可以对这些样式做任何事情。甚至是,你如果需要对特殊的情况进行定义的时候,可以基于特殊的要求对它们进行定义。
<!-- lang: js -->
.ng-valid { }
.ng-invalid { }
.ng-pristine { }
.ng-dirty { }
/* really specific css rules applied by angular */
.ng-invalid-required { }
.ng-invalid-minlength { }
.ng-valid-max-length { }
ng-class
因为我们用 Bootsrap,会用到它提供的样式(has-error
)。这让我们可以在 form-group
的边框加上颜色,对用户更加友好。
ng-class
允许我们基于异常来添加样式。比如说,如果一个输入框是 $invalid
并且不 pristine
的时候,我们给 form-group 加上 has-error
样式。
实现的方式是 ng-class="{ <class-you-want> : <expression to be evaluated > }"
。更多信息可以查看 Angular ngClass docs。
<!-- lang: js -->
<!-- index.html -->
...
<!-- NAME -->
<div class="form-group" ng-class="{ 'has-error' : userForm.name.$invalid && !userForm.name.$pristine }">
<label>Name</label>
<input type="text" name="name" class="form-control" ng-model="user.name" required>
<p ng-show="userForm.name.$invalid && !userForm.name.$pristine" class="help-block">You name is required.</p>
</div>
<!-- USERNAME -->
<div class="form-group" ng-class="{ 'has-error' : userForm.username.$invalid && !userForm.username.$pristine }">
<label>Username</label>
<input type="text" name="username" class="form-control" ng-model="user.username" ng-minlength="3" ng-maxlength="8">
<p ng-show="userForm.username.$error.minlength" class="help-block">Username is too short.</p>
<p ng-show="userForm.username.$error.maxlength" class="help-block">Username is too long.</p>
</div>
<!-- EMAIL -->
<div class="form-group" ng-class="{ 'has-error' : userForm.email.$invalid && !userForm.email.$pristine }">
<label>Email</label>
<input type="email" name="email" class="form-control" ng-model="user.email">
<p ng-show="userForm.email.$invalid && !userForm.email.$pristine" class="help-block">Enter a valid email.</p>
</div>
...
于是,我们的表单正确套用了 Bootstrap 的异常样式。
有时候在用户输入的时候显示异常是不好的。现在的例子是当用户在表单中输入的时候,立刻反映出来。这归功于 Angular 牛逼的数据绑定功能。但在表单验证的时候,所有的改变都立刻反映,倒可以算是一个缺点了。
对于这种场景,比如你只希望在提交表单之后显示异常,你需要对上面的代码做一些改变。
ng-disabled
属性去掉,因为我们希望用户在即使没有完全验证的情况下,也可以提交表单。submitForm()
方法中,加上 $scope.submitted = true;
。一旦表单被提交,变量立刻就被设置为 true 了。ng-class="{ 'has-error' : userForm.name.$invalid && !userForm.name.$pristine }"
改为 ng-class="{ 'has-error' : userForm.name.$invalid && !userForm.name.$pristine && submitted }"
。这保证了异常只有在表单被提交之后才会显示。你应该需要对所有的 ng-class
和 ng-show
都做一下同样的设置现在表单只有在 submitted
变量是 true
的时候才会显示异常了。
点击输入框外后显示异常(也就是所谓的blur)比提交之后显示异常复杂点。当 blur 的时候检查验证是一个自定义指令。指令允许你操作 DOM。
我们把这个话题分开写了一篇教程。这里还有其他的一些关于创建处理 blur 的自定义指令的教程:
现在一旦我们把我们的信息都填对了,我们的表单提交按钮就会被激活,一旦我们提交了我们的表单,我们可以看到弹出框显示这样的信息。
罗列一下我们现在有的功能:
如你所见,用 Angular 来做客户端的表单验证非常简单。
标准情况,实现点击输入框外后显示异常并不是件简单的事情。 Angular 团队已经认识到了这点,并且他们说他们计划会追加更多的状态来处理,比如说 form.submitted
,input.$visited
,input.$blurred
,或者是 input.$touched
。这里有些关于表单认证的资源:
希望越快越好,我们能够在应用的各种情况下,更简单的验证表单。