AngularJs Forms详解及简单示例


Posted in Javascript onSeptember 01, 2016

      控件(input、select、textarea)是用户输入数据的一种方式。Form(表单)是这些控件的集合,目的是将相关的控件进行分组。

表单和控件提供了验证服务,所以用户可以收到无效输入的提示。这提供了更好的用户体验,因为用户可以立即获取到反馈,知道如何修正错误。请记住,虽然客户端验证在提供良好的用户体验中扮演重要的角色,但是它可以很简单地被绕过,因此,客户端验证是不可信的。服务端验证对于一个安全的应用来说仍然是必要的。

一、Simple form

理解双向数据绑定的关键directive是ngModel。ngModel提供了从model到view和view到model的双向数据绑定。并且,它还向其他directive提供API,增强它们的行为。

<!DOCTYPE HTML>
<html lang="zh-cn" ng-app="SimpleForm">
<head>
  <meta charset="UTF-8">
  <title>PropertyEvaluation</title>
  <style type="text/css">
    .ng-cloak {
      display: none;
    }
  </style>
</head>
<body>
<div ng-controller="MyCtrl" class="ng-cloak">
  <form novalidate>
    名字: <input ng-model="user.name" type="text"><br/>
    Email: <input ng-model="user.email" type="email"><br/>
    性别: <input value="男" ng-model="user.gender" type="radio">男
    <input value="女" ng-model="user.gender" type="radio">女
    <br/>
    <button ng-click="reset()">还原上次保存</button>
    <button ng-click="update(user)">保存</button>
  </form>
  <pre>form = {{user | json}}</pre>
  <pre>saved = {{saved | json}}</pre>
</div>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
  var app = angular.module("SimpleForm", []);
  app.controller("MyCtrl", function ($scope,$window) {
    $scope.saved = {};

    $scope.update = function(user) {
      $scope.saved = angular.copy(user);
    };

    $scope.reset = function() {
      $scope.user = angular.copy($scope.saved);
    };

    $scope.reset();
    //不合法的值将不会进入user
  });
</script>
</body>
</html>
 

二、Using CSS classes

为了让form以及控件、ngModel富有样式,可以增加以下class:

  1. ng-valid
  2. ng-invalid
  3. ng-pristine(从未输入过)
  4. ng-dirty(输入过)

下面的例子,使用CSS去显示每个表单控件的有效性。例子中,user.name和user.email都是必填的,但当它们修改过之后(dirty),背景将呈现红色。这确保用户不会直到与表单交互之后(提交之后?),发现未能满足其有效性,才为一个错误而分心。

<!DOCTYPE HTML>
<html lang="zh-cn" ng-app="CSSClasses">
<head>
  <meta charset="UTF-8">
  <title>CSSClasses</title>
  <style type="text/css">
    .ng-cloak {
      display: none;
    }
    .css-form input.ng-invalid.ng-dirty {
      background-color: #fa787e;
    }
    .css-form input.ng-valid.ng-dirty {
      background-color: #78fa89;
    }
  </style>
</head>
<body>
<div ng-controller="MyCtrl" class="ng-cloak">
  <form novalidate class="css-form" name="formName">
    名字: <input ng-model="user.name" type="text" required><br/>
    Email: <input ng-model="user.email" type="email" required><br/>
    性别: <input value="男" ng-model="user.gender" type="radio">男
    <input value="女" ng-model="user.gender" type="radio">女
    <br/>
    <button ng-click="reset()">RESET</button>
    <button ng-click="update(user)">SAVE</button>
  </form>
  <pre>form = {{user | json}}</pre>
  <pre>saved = {{saved | json}}</pre>
</div>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
  var app = angular.module("CSSClasses", []);
  app.controller("MyCtrl", function ($scope,$window) {
    $scope.saved = {};
    $scope.update = function(user) {
      $scope.saved = angular.copy(user);
    };

    $scope.reset = function() {
      $scope.user = angular.copy($scope.saved);
    };

    $scope.reset();
    //不合法的值将不会进入user
  });
</script>
</body>
</html>

三、Binding to form and control state

在angular中,表单是FormController的一个实例。表单实例可以随意地使用name属性暴露到scope中(这里没看懂,scope里面没有一个跟form的name属性一直的property,但由于有“document.表单名”这种方式,所以还是可以获取到的)。相似地,控件是NgModelController的实例。控件实例可以相似地使用name属性暴露在form中。这说明form和控件(control)两者的内部属性对于使用标准绑定实体(standard binding primitives)绑定在视图中的这个做法是可行的。

这允许我们通过以下特性来扩展上面的例子:

  1. RESET按钮仅仅在form发生改变之后才可用。
  2. SAVE按钮仅仅在form发生改变而且输入有效的情况下可用。
  3. 为user.email和user.agree定制错误信息。
<!DOCTYPE HTML>
<html lang="zh-cn" ng-app="ControlState">
<head>
  <meta charset="UTF-8">
  <title>ControlState</title>
  <style type="text/css">
    .ng-cloak {
      display: none;
    }
    .css-form input.ng-invalid.ng-dirty {
      background-color: #fa787e;
    }
    .css-form input.ng-valid.ng-dirty {
      background-color: #78fa89;
    }
  </style>
</head>
<body>
<div ng-controller="MyCtrl" class="ng-cloak">
  <form novalidate class="css-form" name="formName">
    名字: <input ng-model="user.name" name="userName" type="text" required><br/>
    <div ng-show="formName.userName.$dirty&&formName.userName.$invalid">
      <span>请填写名字</span>
    </div>
    Email: <input ng-model="user.email" name="userEmail" type="email" required><br/>
    <div ng-show="formName.userEmail.$dirty && formName.userEmail.$invalid">提示:
      <span ng-show="formName.userEmail.$error.required">请填写Email</span>
      <span ng-show="formName.userEmail.$error.email">这不是一个有效的Email</span>
    </div>
    性别: <input value="男" ng-model="user.gender" type="radio">男
    <input value="女" ng-model="user.gender" type="radio">女
    <br/>
    <input type="checkbox" ng-model="user.agree" name="userAgree" required/>我同意:
    <input type="text" ng-show="user.agree" ng-model="user.agreeSign" required/>
    <br/>
    <div ng-show="!formName.userAgree || !user.agreeSign">请同意并签名~</div>
    <button ng-click="reset()" ng-disabled="isUnchanged(user)">RESET</button>
    <button ng-click="update(user)" ng-disabled="formName.$invalid || isUnchanged(user)">SAVE</button>
  </form>
  <pre>form = {{user | json}}</pre>
  <pre>saved = {{saved | json}}</pre>
</div>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
  var app = angular.module("ControlState", []);
  app.controller("MyCtrl", function ($scope,$window) {
    $scope.saved = {};
    $scope.update = function(user) {
      $scope.saved = angular.copy(user);
    };

    $scope.reset = function() {
      $scope.user = angular.copy($scope.saved);
    };

    $scope.isUnchanged = function(user) {
      return angular.equals(user, $scope.saved);
    };

    $scope.reset();
    //不合法的值将不会进入user
  });
</script>
</body>
</html>

四、Custom Validation

angular为大多数公共的HTML表单域(input,text,number,url,email,radio,checkbox)类型提供了实现,也有一些为了表单验证的directive(required,pattern,,inlength,maxlength,min,max)。

可以通过定义增加定制验证函数到ngModel controller(这里是连在一起的ngModelController吗?)中的directive来定义我们自己的验证插件。为了得到一个controller,directive如下面的例子那样指定了依赖(directive定义对象中的require属性)。

          Model到View更新 - 无论什么时候Model发生改变,所有在ngModelController.$formatters(当model发生改变时触发数据有效性验证和格式化转换)数组中的function将排队执行,所以在这里的每一个function都有机会去格式化model的值,并且通过NgModelController.$setValidity(http://code.angularjs.org/1.0.2/docs/api/ng.directive:ngModel.NgModelController#$setValidity)修改控件的验证状态。

          View到Model更新 - 一个相似的方式,无论任何时候,用户与一个控件发生交互,将会触发NgModelController.$setViewValue。这时候轮到执行NgModelController$parsers(当控件从DOM中取得值之后,将会执行这个数组中所有的方法,对值进行审查过滤或转换,也进行验证)数组中的所有方法。

在下面的例子中我们将创建两个directive。

       第一个是integer,它负责验证输入到底是不是一个有效的整数。例如1.23是一个非法的值,因为它包含小数部分。注意,我们通过在数组头部插入(unshift)来代替在尾部插入(push),这因为我们想它首先执行并使用这个控件的值(估计这个Array是当作队列来使用的),我们需要在转换发生之前执行验证函数。

        第二个directive是smart-float。他将”1.2”和”1,2”转换为一个合法的浮点数”1.2”。注意,我们在这不可以使用HTML5的input类型”number”,因为浏览器不允许用户输入我们预期的非法字符,如”1,2”(它只认识”1.2”)。

<!DOCTYPE HTML>
<html lang="zh-cn" ng-app="CustomValidation">
<head>
  <meta charset="UTF-8">
  <title>CustomValidation</title>
  <style type="text/css">
    .ng-cloak {
      display: none;
    }
    .css-form input.ng-invalid.ng-dirty {
      background-color: #fa787e;
    }
    .css-form input.ng-valid.ng-dirty {
      background-color: #78fa89;
    }
  </style>
</head>
<body>
<div class="ng-cloak">
  <form novalidate class="css-form" name="formName">
    <div>
      大小(整数 0 - 10):<input integer type="number" ng-model="size" name="size" min="0" max="10"/>{{size}}<br/>
      <span ng-show="formName.size.$error.integer">这不是一个有效的整数</span>
      <span ng-show="formName.size.$error.min || formName.size.$error.max">
        数值必须在0到10之间
      </span>
    </div>
    <div>
      长度(浮点数):
      <input type="text" ng-model="length" name="length" smart-float/>
      {{length}}<br/>
      <span ng-show="formName.length.0error.float">这不是一个有效的浮点数</span>
    </div>
  </form>
</div>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
  var app = angular.module("CustomValidation", []);
  var INTEGER_REGEXP = /^\-?\d*$/;
  app.directive("integer", function () {
    return {
      require:"ngModel",//NgModelController
      link:function(scope,ele,attrs,ctrl) {
        ctrl.$parsers.unshift(function (viewValue) {//$parsers,View到Model的更新
          if(INTEGER_REGEXP.test(viewValue)) {
            //合格放心肉
            ctrl.$setValidity("integer", true);
            return viewValue;
          }else {
            //私宰肉……
            ctrl.$setValidity("integer", false);
            return undefined;
          }
        });
      }
    };
  });
  var FLOAT_REGEXP = /^\-?\d+(?:[.,]\d+)?$/;
  app.directive("smartFloat", function () {
    return {
      require:"ngModel",
      link:function(scope,ele,attrs,ctrl) {
        ctrl.$parsers.unshift(function(viewValue) {
          if(FLOAT_REGEXP.test(viewValue)) {
            ctrl.$setValidity("float", true);
            return parseFloat(viewValue.replace(",", "."));
          }else {
            ctrl.$setValidity("float", false);
            return undefined;
          }
        });
      }
    }
  });
</script>
</body>
</html>

五、Implementing custom form control (using ngModel)

angular实现了所有HTML的基础控件(input,select,textarea),能胜任大多数场景。然而,如果我们需要更加灵活,我们可以通过编写一个directive来实现自定义表单控件的目的。

为了制定控件和ngModel一起工作,并且实现双向数据绑定,它需要:

实现render方法,是负责在执行完并通过所有NgModelController.$formatters方法后,呈现数据的方法。

调用$setViewValue方法,无论任何时候用户与控件发生交互,model需要进行响应的更新。这通常在DOM事件监听器里实现。

可以查看$compileProvider.directive获取更多信息。

下面的例子展示如何添加双向数据绑定特性到可以编辑的元素中。

<!DOCTYPE HTML>
<html lang="zh-cn" ng-app="CustomControl">
<head>
  <meta charset="UTF-8">
  <title>CustomControl</title>
  <style type="text/css">
    .ng-cloak {
      display: none;
    }
    div[contenteditable] {
      cursor: pointer;
      background-color: #D0D0D0;
    }
  </style>
</head>
<body ng-controller="MyCtrl">
<div class="ng-cloak">
  <div contenteditable="true" ng-model="content" title="点击后编辑">My Little Dada</div>
  <pre>model = {{content}}</pre>
  <button ng-click="reset()">reset model tirgger model to view($render)</button>
</div>
<script src="../angular-1.0.1.js" type="text/javascript"></script>
<script type="text/javascript">
  var app = angular.module("CustomControl", []);
  app.controller("MyCtrl", function ($scope) {
    $scope.reset = function() {
      $scope.content = "My Little Dada";
      //在这里如何获取NgModelController呢?如果你们知道,希望可以指点指点!谢谢
    };
  });
  app.directive("contenteditable", function () {
    return {
      require:"ngModel",
      link:function (scope, ele, attrs, ctrl) {
        //view -> model
        ele.bind("blur keyup",function() {
          scope.$apply(function() {
            console.log("setViewValue");
            ctrl.$setViewValue(ele.text());
          });
        });

        //model -> view
        ctrl.$render = function(value) {
          console.log("render");
          ele.html(value);
        };
        //读取初始值
        ctrl.$setViewValue(ele.text());
      }
    };
  });
</script>
</body>
</html>
Javascript 相关文章推荐
JavaScript 页面编码与浏览器类型判断代码
Jun 03 Javascript
jquery.cvtooltip.js 基于jquery的气泡提示插件
Nov 19 Javascript
jWiard 基于JQuery的强大的向导控件介绍
Oct 28 Javascript
可自己添加html的伪弹出框实现代码
Sep 08 Javascript
js借助ActiveXObject实现创建文件
Sep 29 Javascript
浅谈 jQuery 事件源码定位问题
Jun 18 Javascript
js实现内容显示并使用json传输数据
Mar 16 Javascript
IScroll5 中文API参数说明和调用方法
May 21 Javascript
bootstrap-table组合表头的实现方法
Sep 07 Javascript
基于casperjs和resemble.js实现一个像素对比服务详解
Jan 10 Javascript
分享JS表单验证源码(带错误提示及密码等级)
Jan 05 Javascript
js实现鼠标滑动到某个div禁止滚动
Sep 17 Javascript
vue.js入门教程之计算属性
Sep 01 #Javascript
AngularJs expression详解及简单示例
Sep 01 #Javascript
分享JS代码实现鼠标放在输入框上输入框和图片同时更换样式
Sep 01 #Javascript
vue.js入门教程之基础语法小结
Sep 01 #Javascript
AngularJs directive详解及示例代码
Sep 01 #Javascript
AngularJs concepts详解及示例代码
Sep 01 #Javascript
通过JS和PHP两种方法判断用户请求时使用的浏览器类型
Sep 01 #Javascript
You might like
PHP 遍历XP文件夹下所有文件
2008/11/27 PHP
PHP判断远程url是否有效的几种方法小结
2011/10/08 PHP
php 无法加载mysql的module的时候的配置的解决方案引发的思考
2012/01/27 PHP
php实现判断访问来路是否为搜索引擎机器人的方法
2015/04/15 PHP
JavaScript 对象、函数和继承
2009/07/07 Javascript
jquery插件制作 表单验证实现代码
2012/08/17 Javascript
载入jQuery库的最佳方法详细说明及实现代码
2012/12/28 Javascript
js判断选择的时间是否大于今天的代码
2013/08/20 Javascript
jQuery ajax dataType值为text json探索分享
2013/09/23 Javascript
js导出table数据到excel即导出为EXCEL文档的方法
2013/10/10 Javascript
js对象转json数组的简单实现案例
2014/02/28 Javascript
JS+CSS实现DIV层的展开、收缩效果
2016/01/28 Javascript
JS判断是否长按某一键的方法
2016/03/02 Javascript
vue中的event bus非父子组件通信解析
2017/10/27 Javascript
AngularJS下$http服务Post方法传递json参数的实例
2018/03/29 Javascript
node.js中TCP Socket多进程间的消息推送示例详解
2018/07/10 Javascript
怎么使用javascript深度拷贝一个数组
2019/06/06 Javascript
解决vuex数据异步造成初始化的时候没值报错问题
2019/11/13 Javascript
JavaScript实现省份城市的三级联动
2020/02/11 Javascript
用Python编写一个每天都在系统下新建一个文件夹的脚本
2015/05/04 Python
Python爬虫包BeautifulSoup异常处理(二)
2018/06/17 Python
python linecache 处理固定格式文本数据的方法
2019/01/08 Python
django中related_name的用法说明
2020/05/20 Python
详解pandas中利用DataFrame对象的.loc[]、.iloc[]方法抽取数据
2020/12/13 Python
CSS3实现的炫酷菜单代码分享
2015/03/12 HTML / CSS
Book Depository澳大利亚:世界领先的专业在线书店之一
2018/12/27 全球购物
关于毕业的广播稿
2014/01/10 职场文书
清明节网上祭英烈活动总结
2014/04/30 职场文书
材料成型及控制工程专业求职信
2014/06/19 职场文书
2015年乡镇卫生院工作总结
2015/04/22 职场文书
中学教师教学工作总结
2015/08/13 职场文书
2016年情人节问候语
2015/11/11 职场文书
浅谈如何写好演讲稿?
2019/06/12 职场文书
详解redis分布式锁的这些坑
2021/05/19 Redis
logback如何自定义日志存储
2021/08/30 Java/Android
Netflix《海贼王》真人版剧集多张片场照曝光
2022/04/04 日漫