jiayisheji / blog

没事写写文章,喜欢的话请点star,想订阅点watch,千万别fork!
https://jiayisheji.github.io/blog/
505 stars 50 forks source link

angular1表单验证 #6

Open jiayisheji opened 7 years ago

jiayisheji commented 7 years ago

前端发展很快,angular1前几年还很火,虽然现在很多新框架都出来了,angular2,angular4,react,vue。angular1市场是存在,angular做管理系统还是很不错的一个选择。angular里面有很多功能。指令,控制器,服务,过滤器,双向绑定,表单验证等,今天要说就是表单验证。

表单验证

表单验证,angular提供丰富的内置指令也很实用的指令,我们需要去使用它来做验证。唯一有个不足的地方,angular验证是边输入边验证,不是失去焦点验证。如果需要失去焦点验证需要自己去做些处理。后面回讲一些。

form标签

说到表单一定要说form标签,表单元素都是包裹在里面,我们写个表单以后,怎么和angular去关联。

拿常用一个简单登录页面来安利:

<form id="login">
    用户名:<input type="type" name=“username” id="username"> <br />
   密码:<input type="password" name="password" id="password">  <br />
    <button type="submit">提交</button>
</form>

传统做法,获取login这个id,然后监听onsubmit事件,把username和password传给后台,需要验证username和password正确性,前端可以做一层简单拦截,用户体验一种,如果用户输入不是预期就给提示警告,直到正确为止,才让用户提交,当然后台也会做相同的验证。不是我们研究的重点。

使用angular.js该如何玩呢?

<form name="login" novalidate ng-submit="submitForm(login.$valid)">
    用户名:<input type="type" name=“username” ng-model="vm.username"> <br />
   密码:<input type="password" name="password" ng-model="vm.password">  <br />
    <button type="submit">提交</button>
</form>

看到angular里面会出现一些奇怪东西。先不管奇怪的东西。我们来一一解释。

  1. from里面的name这个是重点,它是和angular绑定唯一标识,一个页面不要重名,重名就被覆盖了。后面验证就需要用到它。很关键的东西,不要忽略。
  2. novalidate 屏蔽浏览器对表单的默认验证行为,忽略html5表单验证,html5提供常用表单验证规范,angular也是基于它,和它类似。使用方法。我们想用angular表单验证,就要把它忽略,禁用。
  3. ng-submit 是绑定提交事件。submitForm(login.$valid)控制器里面绑定一个方法,当用户点提交时候就会去调用它。
  4. ng-model 是一个表单双向绑定指令,表单里面很常用。其他标签也可以使用。ng-model是一个很复杂的东西,可以做很多功能,后面会单独列出来。
  5. 为什么需要绑定到一个vm上,类似一个哈希对象写法,很多栗子都是直接绑定一个值,这样的写一个工作实践。提交给后台时候只需要把vm这个对象放在ajax参数里面就好了,如果在多一个重置按钮,直接把vm设为空对象就好。也是一些小技巧。

上面和我们验证有什么关系了。接着继续。。。

表单验证常用属性

表单对象 formName

formName就是我们上面栗子的form的name值

表单元素对象 inputfieldName

inputfieldName就是我们上面栗子的input的name值

如果我们需要访问username,就需要如此如此这般这般, login.username

表单元素常用的4个状态

未修改过的表单

布尔值属性,表示用户是否修改了表单。如果为ture,表示没有修改过;false表示修改过:

formName.inputFieldName.$pristine;

修改的表单

布尔型属性,当且仅当用户实际已经修改的表单。不管表单是否通过验证:

formName.inputFieldName.$dirty

经过验证的表单

布尔型属性,它指示表单是否通过验证。如果表单当前通过验证,他将为true:

formName.inputFieldName.$valid

未通过验证的表单

布尔值属性,表示用户没有通过验证。如果为ture,表示没有通过;false表示通过:

formName.inputFieldName.$invalid

同样这个这个状态也适用于formNam,回去看栗子submitForm(login.$valid)就是写的第三个,通过验证了才能提交。

常用的内置4个验证指令

那你肯定要问了这些状态根据什么判断,凭什么说我没有通过验证。下面就来说说说angular提交表单验证几个常用的指令。

以上4个是关于验证信息,来改变验证状态。

下面是几个常用的事件。

以上就是angular表单验证基本介绍,接下来会介绍怎么做验证处理,和验证相关的属性。

jiayisheji commented 7 years ago

书接上回,上面已经把angular表单验证基本东西都介绍完了。先说几个辅助指令。

表单辅助指令

  • ngDisabled:接受一个表达式,如果表达式为真, 指定的属性“disabled”会被设到元素上。一般用在按钮上面。
    <button type="submit" ng-disabled="login.$invalid">提交</button>

    这个就是如果未通过验证就不能提交

  • ngChecked:接受一个表达式,如果表达式为真,则在元素上放置"checked"属性。一般用在checkbox,radio这2个表单元素上
  • ngReadonly:接受一个表达式,如果表达式为真, 指定的属性"readonly" 会被设到元素上。一般都放在输入框,有了这个属性,就不能输入了。
  • ngTrim :默认会把首尾空格去掉,如果设置为false,Angular将不自动去除输入内容的空格。一般存在输入元素input[type=text],textarea上。
  • min:当输入的值小于min时,设置min 验证错误。一般只存在时间类型和数字类型,例如:input[type=dateTimeLocal],input[type=date],input[type=month],input[type=number],input[type=time],input[type=week]。
  • max:当输入的值大于max时,设置max验证错误。一般只存在时间类型和数字类型,例如:input[type=dateTimeLocal],input[type=date],input[type=month],input[type=number],input[type=time],input[type=week]。
  • ngValue:当被选中时,value会被置为表达式的值。只存在input[type=radio],其他表单原生也可以使用,不过没有什么意义,因为有ng-model,实际生成value不会改变任何东西。
  • ngTrueValue:当被选中时,value会被置为表达式的值。只存在input[type=checkbox]
  • ngFalseValue:当未被选中时,value会被置为表达式的值。只存在input[type=checkbox]
  • ngSelected:接受一个表达式,如果表达式为真, 指定的属性"selected" 会被设到元素上。一般存在option上。
  • ngOptions: 根据参数自动生成option标签。几种常见的写法,一般只用于select标签上 数组写法:
    label for value in array
    select as label for value in array
    label group by group for value in array
    select as label group by group for value in array track by trackexpr

    哈希对象写法:

    label for (key , value) in Object
    select as label for (key , value) in Object
    label group by group for (key, value) in Object
    select as label group by group for (key, value) in Object

    解释:

    array / Object:用于进行迭代的结果为一个数组或对象的表达式。
    value: 局部变量,迭代期间指向array中的每个条目,或Object的每个属性值。
    key: 局部变量,迭代期间指向Object的属性名称。 
    label: 这个表达式的结果作为<option>元素的标签。表达式通常指向value 变量(value.propertyName)。
    select:这个表达式的结果会绑定到父<select>元素的模型上。如果未指定,select表达式默认为value。
    group: 这个表达式的结果会被用于使用<optgroup> DOM 元素分组选项。
    trackexpr: 应用于对象数组。表达式的结果被用唯一标识数组中的对象。 trackexpr通常指向value变量(如value.propertyName)。

    特殊指令 ngForm

    上面都是ng扩展的指令,还有一个指令angular特别提供的ngForm。 在html里面form套form这个玩意不能共存的。但是很多时候我们在写angular是数据都是动态生成的,那么表单元素也是动态生成的,就会出现一些不方便的地方,这个时候ngForm就出现了,他可以这么写

    标准写法
    <ng-form name="addressList" ng-repeat="item in addressList">
    收货地址:<input type="address" name="address" ng-model="item.address">  <br />
    </ng-form>
    兼容老旧ie
    <div ng-form="addressList" ng-repeat="item in addressList">
    收货地址:<input type="address" name="address" ng-model="item.address">  <br />
    </div>

    那么它怎么取address的数据了。 login.addressList.address.$valid

王者属性 name

还有一个属性是原生自带的,它叫name,也是最重要的一项,没有它一切验证都挂了。所以最重要的东西最后出场。name是angular和html关联的一个重要桥梁。所有的取值都是formName.inputFieldName。

隐藏BOSS $error

表单验证里面最关心的就是错误那么angular对象下就有一个这样的错误对象。 formName.$error 整个表单里面所有的错误信息 formName.inputFieldName.$error 单个表单里面所有的错误信息 这个错误提示和验证指令有关。true就是错误,false就是正确。 formName.inputFieldName.$error.required 是否未填写 formName.inputFieldName.$error.minlength 是否小于最小长度 formName.inputFieldName.$error.maxlength 是否大于最大长度 formName.inputFieldName.$error.pattern 是否不符合正则表达式

以上就是angular表单相关基本都介绍完了,下面来说写一下和表单相关的问题和实践。

jiayisheji commented 7 years ago

书接上回,基本指令,理论都介绍完了,那么接下该实战了。

ngModel来扩展更多功能

在项目中编写指令,常常会依赖其他的指令来实现想要达到的功能,其中最常用到的便是ng-model,它为我们明确了需要绑定的属性,虽然在指令中可以通过通过使用独立作用域的”=”来进行双向绑定,但使用ng-model更能简化指令的传值,符合angular的使用习惯。注意:使用ng-model就可以使用ng-change事件。

指令依赖

angular中编写自定义指令是通过require属性来指定查找依赖指令的controller,并传入link函数中进行调用.

.directive("...",function(){
    return {
        ...
        require:'^?ngModel',
        link:function(scope,elem,attr,ngModelCtr){
            ...
        }
        ...
    }
})

require是angular指令一个依赖控制器,可以是一个字符串或者一个字符串数组

在查找指令的过程中,如果未找到指令,则会抛出一个编译错误。

可以通过加前缀来控制其查找的操作。

前缀 作用
^ 向上查找指令,未找到则报错
? 未找到,取消报错,将null传到link的第四个参数

接来看几个实战栗子:

jiayisheji commented 7 years ago

第一个问题,动态name问题

何为动态name,一般情况下name都是直接写死的,但是有一种情况,必须要写动态name,如果我们表单是不固定,通过ng-repeat循环添加的表单,这个时候你的input[name]可能是循环添加的,看下面栗子:

<div ng-form="addressList" ng-repeat="item in addressList">
    收货地址:<input type="address" name="item.name" ng-model="item.address">  <br />
</div>

这个时候name无法解析的。你可能会想到,那用表达式呀,ok,想法不错。

<div ng-form="addressList" ng-repeat="item in addressList">
    收货地址:<input type="address" name="{{item.name}}" ng-model="item.address">  <br />
</div>

这个时候,页面上解析是完全符合预期的,name=“address”,如果单纯的页面显示出来,就好了,那玩啥,我需要和angular关联,来验证我的表单,这是我重点。

无法用差值表达式绑定

我们在控制台打印控制器里面获取$scope对象,可以找到login这个对象,打开这个对象以后,会看到username,password,还有一个{{item.name}},等等,{{item.name}}是个什么鬼,我的预期的address去哪里的。

解决方案

问题是{{item.name}}差值表达式,等angular运行完了,数据全部稳定了才开始解析,这个时候控制器里面已经绑定好了字段。这确实就是一个坑爹的事情了。那怎么办,angular好像也没有提供一个ng-name的玩意。没有那我们就自己动手撸一个my-name(自定义指令不用ng开头,这是行业约定吧。这个和angular机制有关系,指令就相当于在angular上面挂了一个方法,如果同名的就会出现后面覆盖前面,所以写指令最好有自己的命名空间加前缀)

angular
    .module('myApp')
    .directive('myName', function(){
       return {
           require: "ngModel",
           link: function (scope, elm, iAttrs, ngModelCtr) {
              ngModelCtr.$name = scope.$eval(iAttrs.cbName);
              var formController = elm.controller('form') || {
                  $addControl: angular.noop
               };
               formController.$addControl(ngModelCtr);
              scope.$on('$destroy', function () {
                 formController.$removeControl(ngModelCtr);
              });
      }
   });