主要讲解1.3后的一些新功能,和一些以前没有介绍的小功能 (ng-if,ng-switch).
1.one way bind
这个之前的版本已经有人自己实现了,但是在1.3之后,angularjs 有自带的了。用法极其简单 .
<div ng-app="app" ng-controller="ctrl"> {{ ::value }} </div> <script src="../../js/Stooges.js"></script> <script src="../../js/ng-1.3.10/angular.js"></script> <script> var app = angular.module("app", []); app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) { $scope.value = "keatkeat"; $timeout(function () { $scope.value = "xinyao"; },2000); }]); </script>
看到吗 ?只是把从前的 {{ value }} 改成 {{ ::value }} . 加了 :: 就表示这个值只是要单向绑定,之后$scope改变了也不会在同步到模板了。
2.ng-if
ng-if 是用来做动态模板的,如果你的模板有一部分内容是依据某些数据来决定的就可以用啦。
他和ng-hide主要区别在,ng-if 如果是false 的情况它不会生成dom,这有时对性能是好的。
ng-if 为true时,angularjs 会使用之前clone好的模板,然后compile了append出去。
<div ng-app="app" ng-controller="ctrl"> <div ng-if="isTrue"> ok </div> <div ng-if="!isTrue"> no </div> </div> <script src="../../js/Stooges.js"></script> <script src="../../js/ng-1.3.10/angular.js"></script> <script> var app = angular.module("app", []); app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) { $scope.isTrue = false; $timeout(function () { $scope.isTrue = true; }, 2000); }]); </script>
angularjs 没有 else 指令(不过有人自己实现了) , 不过我们可以用上面的写法来模拟 else
ng-if 会自动创建新的继承 scope . 在有ng-model 的情况下多留意,有坑.
<div ng-app="app" ng-controller="ctrl"> <div ng-form="form"> parent value : {{value}} <br /> model value :{{ form.age.$modelValue }} <div ng-if="isTrue"> <input type="text" name="age" ng-model="value" /> <br /> child value : {{value}} </div> </div> </div> <script src="../../js/Stooges.js"></script> <script src="../../js/ng-1.3.10/angular.js"></script> <script> var app = angular.module("app", []); app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) { $scope.isTrue = true; $scope.value = "keatkeat"; }]); </script>
当我们输入 text 的时候 , $modelValue 和 child value 都会更新,但是 parent value 却不会了。这是正确的原型概念 (http://www.cnblogs.com/keatkeat/p/3983952.html)
一般情况我们在做form 时, 通常我们设计一个对象用于装所有的 value ,这样就不必担心 继承scope引起的问题了 .
稍微改一下就可以了.
<div ng-app="app" ng-controller="ctrl"> <div ng-form="form"> parent value : {{value.age}} <br /> model value :{{ form.age.$modelValue }} <div ng-if="isTrue"> <input type="text" name="age" ng-model="value.age" /> <br /> child value : {{value.age}} </div> </div> </div> <script src="../../js/Stooges.js"></script> <script src="../../js/ng-1.3.10/angular.js"></script> <script> var app = angular.module("app", []); app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) { $scope.isTrue = true; $scope.value = { age : 13 }; //换成对象 }]); </script>
3.ng-switch
这个和 ng-if 差不多,和一般js switch 一样的概念 . 它也是会创建继承scope哦.
<div ng-app="app" ng-controller="ctrl"> <div ng-switch="switchCase"> <div ng-switch-when="case1">case2</div><!--case1 必须是一个 string, 不可以是表达式 , (case1 != $scope.case1)--> <div ng-switch-when="case2">case2</div> <div ng-switch-default>default case</div> </div> </div> <script src="../../js/Stooges.js"></script> <script src="../../js/ng-1.3.10/angular.js"></script> <script> var app = angular.module("app", []); app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) { $scope.switchCase = "case2"; $timeout(function () { $scope.switchCase = "case1"; }, 2000); }]); </script>
4. ng-messages
这个主要用于form 验证错误时的error message.
<div ng-app="app" ng-controller="ctrl"> <form novalidate> <div ng-form="form"> <input type="email" ng-minlength="2" required name="email" ng-model="formData.email" /> <div ng-messages="form.email.$error" ng-messages-multiple> <div ng-message="required">must fill in email</div> <div ng-message="email">no a email format</div> <div ng-message="minlength">at least 2 words</div> </div> </div> </form> </div> <script src="../../js/Stooges.js"></script> <script src="../../js/ng-1.3.10/angular.js"></script> <script src="../../js/ng-1.3.10/angular-messages.js"></script> <script> var app = angular.module("app", ['ngMessages']); app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) { $scope.formData = {}; }]); </script>
注意这是一个而且的模块必须加载 ['ngMessages'] 和引入文件 angular-messages.js
上面是一个能匹配多个错误信息的例子. 当错误出现时,特定的div就会被显示出来了。form.email.$error 是一个这样的对象 : { "email": true, "minlength": true } , true 表示验证失败了.
5. ng-model and ng-form
这2个我之前写过一篇了 http://www.cnblogs.com/keatkeat/p/3912530.html
当时还在1.3 beta , 现在我会把一些常用到的在讲解一篇.还有一些新新特性也一起说说。
首先要强调,如果你想把表单做好,做的很 "angular way" 的话, ng-form , ng-model , 自定义指令, 验证, 都必须很清楚。你把它们连在一起才能强大。
5.1 ngModel.$formatters 和 ngModel.$parsers
这2个东西是 ng-model 在同步数据的时候的 pipeline , ngModel 有2个主要属性 ($viewValue , $modelValue)
顾名思义啦, 这2个值通常是一样的。但是有时候我们希望他们不一样,比如日期格式。在 $modelValue 是 datetime , 在 $viewValue 是 string .
这时我们就可以通过拦截 pipeline , 做一些convertion 了.
<div ng-app="app" ng-controller="ctrl"> <form novalidate autocomplete="off"> <div ng-form="form"> model value : {{ form.name.$modelValue }} , Type : {{ getType(form.name.$modelValue) }} <br /> view value : {{ form.name.$viewValue }} , Type : {{ getType(form.name.$viewValue) }} <br /> <input type="text" name="name" ng-model="formData.name" my-datetime-convert /><br /> </div> </form> </div> <script src="../../js/ng-1.3.10/angular.js"></script> <script> var app = angular.module("app", []); app.directive("myDatetimeConvert", [function () { return { restrict: "A", require: "ngModel", link: function (scope, elem, attrs, ngModel) { ngModel.$formatters.push(function (value) { return value.toDateString(); //把 datetime 各式转换成 string 格式. }); ngModel.$parsers.push(function (value) { try { return new Date(value); } catch (e) { return undefined; } }); } } }]); app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) { $scope.formData = { name : new Date() }; $scope.getType = function (value) { return Object.prototype.toString.call(value).slice(8, -1).toLowerCase(); } }]); </script>
上面就是一个处理 datetime 的例子。
$formatters 触发当 model -> view
$parsers 触发当 view -> model
5.2 ng-model-options
ng-model-options="{ updateOn: 'blur' , debounce: 2000 }"
updateOn 表示当什么 event 触发时写入 $viewValue , debounce 表示delay多久后把 $viewValue 同步去 $modelValue
上面这个case , 如果我们写入一个 "keatkeat" , 当onblur 时 $viewValue 马上会是 "keatkeat" , 2秒后 $modelValue 会是 "keatkeat" .
值得注意的是 : 如果我们只是写了一个 debounce , 那么 $viewValue 并不会马上有"keatkeat",而是 2 秒后 $viewValue 和 $modelValue 同时是 "keatkeat" . 建议不一起使用 !
5.3 ng-model 属性方法
方法 :
$render
render中文是渲染,当$modelValue 被修改 $digest 后会同步到 $viewValue (中间经过 pipeline) , 之后angular会调用 $render.
在做自定义指令时,我们就是靠注册这个方法来完成我们 dom 修改的。
在方法中,我们应该使用 $viewValue 工作,而不是 $modelValue 哦。
$rollbackViewValue
调用这个方法,$modeValue 的值将同步到 $viewModel 上
<input type="text" name="name" ng-model="formData.name" ng-model-options="{ updateOn: 'blur' }" ng-keyup="keyup($event,form.name)" /> $scope.keyup = function (event, ngModel) { if (event.keyCode == 27) { ngModel.$rollbackViewValue(); } }
一般上是配合 keyup Esc 和 updateOn : 'blur' 使用
$commitViewValue
这个和 $rollbackViewValue 相反,是马上把 $viewValue 同步到 $modelValue 上。
属性 :
$validators
顾名思义就是做验证的啦,1.3以前我们是通过pipeline来做验证的,很麻烦。
现在呢用 $validators 就可以了
<div ng-app="app" ng-controller="ctrl"> <form novalidate autocomplete="off"> <div ng-form="form"> model value : {{ form.name.$modelValue }}<br /> view value : {{ form.name.$viewValue }}<br /> error : {{ form.name.$error | json }} <input type="text" name="name" ng-model="formData.name" ng-model-options="{ debounce: 2000 }" my-valid /> <br /> </div> </form> </div> <script src="../../js/Stooges.js"></script> <script src="../../js/ng-1.3.10/angular.js"></script> <script> var app = angular.module("app", []); app.directive("myValid", [function () { return { restrict: "A", require: "ngModel", link: function (scope, elem, attrCollection, ngModel) { ngModel.$validators["myValid"] = function (modelValue, viewValue) { return modelValue == "kknd"; } } } }]); app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) { $scope.formData = { name: "kknd" }; }]); </script>
$asyncValidators & $pending
而且还支持异步的
<div ng-app="app" ng-controller="ctrl"> <form novalidate autocomplete="off"> <div ng-form="form"> model value : {{ form.name.$modelValue }}<br /> view value : {{ form.name.$viewValue }}<br /> error : {{ form.name.$error | json }} <br /> pending : {{ form.name.$pending }} <input type="text" name="name" ng-model="formData.name" ng-model-options="{ debounce: 1000 }" my-valid /> <br /> </div> </form> </div> <script src="../../js/Stooges.js"></script> <script src="../../js/ng-1.3.10/angular.js"></script> <script> var app = angular.module("app", []); app.directive("myValid", ["$q",function ($q) { return { restrict: "A", require: "ngModel", link: function (scope, elem, attrCollection, ngModel) { ngModel.$asyncValidators["myValid"] = function (modelValue, viewValue) { var defer = $q.defer(); log("async valid send"); setTimeout(function () { log("async valid back"); if (modelValue == "kknd") { defer.resolve(true); } else { defer.reject(false); } }, 3000); return defer.promise; } } } }]); app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) { $scope.formData = { name: "kknd33" }; }]); </script>
$pending = { myValid : true } 表示正在验证.
注册到$asyncValidators 中的方法,我们需要返回一个 promise 对象就可以了.
验证如果不通过,$modelValue 将会是 undefined
$setViewValue
这个方法主要是用于自定义指令时,我们通过事件来更新ng model 的值
比如 input text , 就是写一个 keyup event , 当keyup 触发时就会调用 $setViewValue(inputValue);
这个方法会更新 $viewValue , $modelValue , 也会触发 pipeline , validators .
注:此方法不会触发 $digest (官方网站是这样写的啦)
But ! 我自己在1.4.5 版本用好像是会的
如果 value 和之前一样的话,digest 会触发,但是$validate 不会触发
$validate()
这个方法是手动调用验证
注:此方法不会触发 $digest (真的不会哦)
ng-form
有点懒, 这个就不怎么介绍了。
只说重点 ,下次才补上吧
<form novalidate autocomplete="off"> <div ng-form="form"> model value : {{ form.name.$modelValue }}<br /> view value : {{ form.name.$viewValue }}<br /> error : {{ form.name.$error | json }} <br /> pending : {{ form.name.$pending }} <input type="text" name="name" ng-model="formData.name" ng-model-options="{ debounce: 1000 }" my-valid /> <div ng-click="click(form.name)">ok</div> <br /> </div> </form>
angular 的form 支持嵌套 , 我觉得最佳做法就是上面这个样子,最外层写一个 form , 内层全部用 div + ng-form 指令
在form 里面,每一个涉及ng-model 的节点,如果有附带 name 的话,都会直接添加进 form controllers 里面 .
所以上面 form.name 可以访问到 ngModelCtrl
form 有一个 $submitted 属性,可以用于判断是否提交。
要reset 一个form , 除了把ng-model 值 clear 完之外,还必须 $setPristine() 和 $setUntouched();
ng-model 的valid , dirty 等等都会和 form 牵连, form 也会和parent form 牵连 . (这个概念要懂)
好啦,基本上就是这样了,下次我才给一个完整的案例吧 ^^