[TOC]
注:如果想在input显示一个值,然后实际上是另一个值的话,有多种方法,思路就是element.val()用来显示用于展示的值,model的值可以用
- ctrl.$parsrs.push(function(value){
return 这里返回ctrl的值
}) - ctrl.$setViewValue()
-ctrl.$ViewValue=""
具体看下文
ngModelController
1、指令的required
假如我们现在需要编写两个指令,在linking函数中有很多重合的方法,为了避免重复自己(著名的DRY原则),我们可以将这个重复的方法写在第三个指令的controller中,然后在另外两个需要的指令中require这个拥有controller字段的指令,最后通过linking函数的第四个参数就可以引用这些重合的方法。代码的结构大致如下:
var app = angular.modeule('myapp',[]);
app.directive('common',function(){
return {
...
controller: function($scope){
this.method1 = function(){
};
this.method2 = function(){
};
},
...
}
});
app.directive('d1',function(){
return {
...
require: '?^common',
link: function(scope,elem,attrs,common){
scope.method1 = common.method1;
..
},
...
}
});
app.directive('d2',function(){
return {
...
require: '?^common',
link: function(scope,elem,attrs,common){
scope.method1 = common.method1;
..
},
...
}
});
所以link的第四个参数就是required的指令对应的ctrl
2 ngModel中的内建属性和方法
先看看例子:
<form name="myForm">
<div contenteditable
name="myWidget" ng-model="userContent"
strip-br="true"
required>Change me!</div>
<span ng-show="myForm.myWidget.$error.required">Required!</span>
<hr>
<textarea ng-model="userContent"></textarea>
</form>
指令:
angular.module('customControl', [])
.directive('contenteditable', function() {
return {
restrict: 'A', // 作为元素属性
require: '?ngModel', // 获取ngModelController
link: function(scope, element, attrs, ngModel) {
if(!ngModel) return; // 如果没有ng-model则什么都不做
// 指定UI的更新方式
ngModel.$render = function() {
element.html(ngModel.$viewValue || '');
};
// 监听change事件来开启绑定
element.on('blur keyup change', function() {
scope.$apply(read);
});
read(); // 初始化
// 将数据写入model
function read() {
var html = element.html();
// 当我们清空div时浏览器会留下一个<br>标签
// 如果制定了strip-br属性,那么<br>标签会被清空
if( attrs.stripBr && html == '<br>' ) {
html = '';
}
ngModel.$setViewValue(html);
}
}
};
});
ngModelController方法
- $render(); 当视图需要更新的时候会被调用。使用ng-model的指令应该自行实现这个方法。
- $isEmpty(value);该方法用于判断输入值是否为空。
例如,使用ngModelController的指令需要判断其中是否有输入值的时候会使用该方法。该方法可用来判断值是否为undefined,'',null或者NaN。 你可以根据自己的需要重载该方法。 - $setValidity(validationErrorKey, isValid);该方法用于改变验证状态,以及在控制变化的验证标准时通知表格。这个方法应该由一个验证器来调用。例如,一个解析器或者格式化函数。
- $setPristine();该方法用于设置控制到原始状态。该方法可以移除'ng-dirty'类并将控制恢复到原始状态('ng-pristine'类)。
- $cancelUpdate();该方法用于取消一次更新并重置输入元素的值以防止$viewCalue发生更新,它会由一个pending debounced事件引发或者是因为input输入框要等待一些未来的事件。
- $setViewValue(value, trigger)方法 该方法用来更新视图值。这个方法应该在一个视图值发生变化时被调用,一般来说是在一个DOM事件处理函数中。例如,input和select指令就调用了这个函数。
这个方法将会更新$viewValue属性,然后在$pasers中通将这个值传递给每一个函数,其中包括了验证器。这个值从$parsers输出后,将会被用于$modelValue以及ng-model属性中的表达式。
最后,所有位于$viewChangeListeners列表中注册的监听器将会被调用。
ngModelController属性
$viewValue
视图中的实际值$modelValue
model中的值,它金额控制器绑定在一起$parsers
将要执行的函数的数组,无论什么时候控制器从DOM中读取了一个值,它都将作为一个管道。其中的函数依次被调用,并将结果传递给下一个。最后出来的值将会被传递到model中。其中将包括验证和转换值的过程。对于验证步骤,这个解析器将会使用$setValidity方法,对于不合格的值将返回undefined。
- $formatters
一个包含即将执行函数的数组,无论什么时候model的值发生了变化,它都会作为一个管道。其中的每一个函数都被依次调用,并将结果传递给下一个函数。该函数用于将模型传递给视图的值进行格式化。
$viewChangeListeners
只要视图的值发生变化,其中的函数就会被执行。其中的函数执行并不带参数,它的返回值也会被忽略。它可以被用在额外的#watches中。$error
一个包含所有error的对象$pristine
如果用户还没有进行过交互,值是true。$dirty
如果用户已经进行过交互,值是true。$valid
如果没有错误,值是true。$invalid
如果有错误,值是true。
关于双向绑定其他
ng-model-options
ng-model-options
,就是可以实现对延迟更新、如何触发更新、时区(timezone针对input[type='date']
等)等的控制
//github上的block-example/表单操作-11/ng-model-options.html
angular.module('optionsExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.user = { name: 'say', data: '' };
$scope.cancel = function(e) {
if (e.keyCode == 27) {
$scope.userForm.userName.$rollbackViewValue();
}
};
}]);
<div ng-controller="ExampleController">
<form name="userForm">
Name:
<input type="text" name="userName"
ng-model="user.name"
ng-model-options="{ updateOn: 'blur',debounce: 1000 ,getterSetter: false }"
ng-keyup="cancel($event)" /><br />
Other data:
<input type="text" ng-model="user.data" /><br />
</form>
<pre>user.name = <span ng-bind="user.name"></span></pre>
</div>
updateOn:可以写入事件名字,将此element按所写事件触发更新
debounce:当我们写进keydowm事件的时候,我需要的是它尽可能说是当我输入完毕后,再去触发更新,那么这个时候我们可以延迟个1s!
getterSetter:为true的时候,则是指element的值是从函数return过来滴!
FormController的方法:
$rollbackViewValue(); $commitViewValue(); $addControl(); $removeControl(); $setValidity(); $setDirty(); $setPristine(); $setUntouched(); $setSubmitted();
- $addControl() //添加ngModel controller ,ngModel会自动添加,除非自定义指令或许会用上
- $removeControl() //与$addControl()相反
- $setValidity() //在自定义表单检验有着很大作用
- $rollbackViewValue() //这个我是这样理解的,回滚到上一个ViewValue
属性:
$pristine(form没被动过) $dirty(form被动过) $valid(全部验证通过) $invalid(验证不通过) $submitted $error
email、max、maxlength、min、minlength、number、pattern、required、url、date、datetimelocal、time、week、month
ngModel.NgModelController
ngModel指令提供了API.它不包含任何逻辑处理DOM渲染或DOM event,这样的DOM相关逻辑应使用其他指令,NgModelController用来控制元素的数据绑定
点击查看官方文档
方法:
$render(); $isEmpty(value); $setValidity(validationErrorKey, isValid); $setPristine(); $setDirty(); $setUntouched(); $setTouched(); $rollbackViewValue(); $validate(); $commitViewValue(); $setViewValue(value, trigger);
- $render: angular 会把 $modelValue 经过 $formatters 得出来的值放入 $viewValue中,(这时 $viewValue = $modelValue 经过 $formatters) 然后触发我们写好的 - - $render . 跟着$setViewValue(value, trigger);一起使用。
- $setViewValue:scope改变$modelValue,使用$setViewValue(),改变$viewValue
- $setValidity:使用这个配合$parsers可以实现表单自定义验证
拥有的属性:
$viewValue //界面显示的数据
$modelValue //$scope上面的value
$parsers //在view->model的时候会触发的一个函数组,无论什么时候Model发生改变,所有的ngModelController.$formatters(model发生改变时触发数据有效验证和格式化转变)数组中的function将排队执行,所以在这里每一个function都有机会去格式化model的值,并且通过NgModelController.$setValidity修改空间的验证状态。
$formatters //在model->view的时候会触发的一个函数组, 无论任何时候用户与控件发生交互,将会触发NgModelCtroller.$setViewValue。这时候轮到执行NgModelController.$parsers(当控件从dom取值之后,将会执行这个数组中的所有方法,对值进行审查过滤或转换,也进行验证)数组中的所有方法。
$validators
$asyncValidators
$viewChangeListeners
$error
$pending
$untouched
$touched
$pristine
$dirty
$valid
$invalid
$name
双向绑定的机制可以在$parsers和$formatters可以体现出来,通过这些我们可以在view->scope做类似表单验证(自定义)的功能(配合$setValidity(validationErrorKey, isValid);),scope->view数据格式自定义等操作(配合$setViewValue(value, trigger);),可以看一下下面的这个例子:
https://github.com/xiaobin5201314/AngularJS-Learning/blob/master/block-example/表单操作-11/ng-model.html
var custom = angular.module('customControl', ['ngSanitize']);
custom.directive("noxiaobin", function () {
return {
restrict: "A",
require: "?ngModel",
link: function (scope, element, attrs, ngModel) {
if (!ngModel) return;
ngModel.$parsers.push(function (v) { //传说中的验证器
if (v != "xiaobin") {
ngModel.$setValidity('noxiaobin', true); //通过获取从dom过来的值,然后进行验证,使用$setValidity('noxiaobin', true);改变noxiaobin的值,然后反馈会dom
return v;
} else {
ngModel.$setValidity('noxiaobin', false);
return undefined;
}
});
}
}
});
custom.directive('contenteditable', ['$sce', function($sce) {
return {
restrict: 'A', //指定该指令是为属性类型的指令
require: '?ngModel', // 与ngModel指令的相互交流
link: function(scope, element, attrs, ngModel) { //scope分别是指令作用的作用域,element触发指令的元素,attrs是element的属性集合,ngmodel是控制器就是引入的ngModel
if (!ngModel) return;
// output data to the view
ngModel.$render = function() {
element.html($sce.getTrustedHtml(ngModel.$viewValue || '')); //$viewValue的值进行format
};
//对element的监听
element.on('blur keyup change', function() {
scope.$evalAsync(read); //执行read方法
});
read(); // 初始化
// Write data to the model
function read() {
var html = element.html();
// When we clear the content editable the browser leaves a <br> behind
// If strip-br attribute is provided then we strip this out
if ( attrs.stripBr && html == '<br>' ) {
html = '';
}
ngModel.$setViewValue(html); //获取ViewValue,设置$viewValue
}
}
};
}]);