angularjs 表单提交最佳实践


环境: angular === '1.3.5'

demo

请输入email地址
不是正确的email
请输入密码
太短了
太长了
请输入密码
不一致

submit triggered: {{ pi.submitTriggered }}

this.formData = {{ pi.formData }}

this.password1 = {{ pi.password1 }}

this.form.$invalid = {{ pi.form.$invalid }}


html:

<div ng-controller="pi as th">
  <form name="th.form" ng-submit="th.submit()" novalidate="novalidate">
    <div class="form-group">
      <label>目录名</label>
      <input type="text" name="name" placeholder="目录名" ng-model="th.formData.name" required="required" ng-trim="true" ng-maxlength="20" class="form-control"/>
      <div ng-messages="th.form.name.$error" ng-if="th.form.name.$dirty" class="color-red">
        <div ng-message="required">不能为空</div>
        <div ng-message="maxlength">最长20</div>
      </div>
    </div>
    <div class="form-group">
      <label>slug</label>
      <input type="text" name="slug" placeholder="英文名" ng-model="th.formData.slug" required="required" ng-trim="true" ng-pattern="/[a-z1-9]{1,40}/" class="form-control"/>
      <div ng-messages="th.form.slug.$error" ng-if="th.form.slug.$dirty" class="color-red">
        <div ng-message="required">不能为空</div>
        <div ng-message="pattern">仅为小写字母数字和短线-,最长40</div>
      </div>
    </div>
    <div class="form-group">
      <label>目录描述</label>
      <textarea type="text" rows="3" name="desc" placeholder="目录描述" ng-model="th.formData.desc" required="required" ng-trim="true" ng-minlength="2" ng-maxlength="500" class="form-control"></textarea>
      <div ng-messages="th.form.desc.$error" ng-if="th.form.desc.$dirty" class="color-red">
        <div ng-message="required">不能为空</div>
        <div ng-message="minlength">最少10个字</div>
        <div ng-message="maxlength">最多500个字</div>
      </div>
    </div>
    <div class="form-group">
      <div id="msg-wrap"></div>
      <button type="submit" class="btn btn-primary">提交</button>
    </div>
  </form>
</div>

要点:

  • 利用form元素做表单验证
  • name='th.form' 实现controller内部对form的引用,然后通过this.form.$invalid判断表单是否通过验证
  • ng-message directive 实现错误提示
  • th.formData 保存要提交的数据
  • ng-submit 处理提交动作
  • 一定要有提交按钮,但是不要注册点击提交事件(由ng-submit处理),这样回车会触发提交动作,点击提交按钮也会触发提交动作,并且不会触发两次。
  • 提交动作发生时使用th.form.form_elem_name.$setDirty()触发验证

js:

//main
angular.module('pi', [
  'ngMessages'
])

//通用setDirty
.service('setDirty', function setDirty() {
  return function(form) {
    angular.forEach(form, function(value, key) {
      if(!/^\$/.test(key)) form[key].$setDirty()
    })
  }
})

.controller('pi', ['$scope', 'setDirty', function filters($scope, setDirty) {

  //start
  var th = this

  th.formData = {}

  th.onsubmit = function() {

    //submit 提交时候触发验证只需要setDirty即可
    if(th.form.$invalid) return setDirty(th.form)

    //do submit th.formData
    do_submit_action()

    //提交后记得重置表单 th.form.$setPristine()

  }

  //end
}])

自定义验证推荐用 ui-validate

<input name="a" ui-validate="{custom: 'custom_validate_expression()' }"/>
<div ng-messages="th.form.a.$error" ng-if="th.form.a.$dirty" class="color-red">
  <div ng-message="custom">自定义验证信息</div>
</div>

如果非要自行验证,以下可能是最简单的方案

// 以常见的密码输入一致性为例
$scope.$watch(function() {
    return th.formData.password2
}, function(newPass) {
    if(!customValidate(newPass)) th.form.password2.$setValidity('custom', false)
    else th.form.password2.$setValidity('custom', true)
})

//custom validate: password repeat must equal to password
function customValidate(v) {
    if(v === th.formData.password1) return true
    else return false
}