概述
ng-model指令的作用是把輸入型的元素(input,select,textarea等)和scope中的數(shù)據(jù)進(jìn)行綁定的作用,也可以用在自定義的form元素上。ng-model指令需要和input、select等指令進(jìn)行配合使用。
同時ng-model實現(xiàn)了一些默認(rèn)的數(shù)據(jù)校驗的功能,比如:url、email等格式校驗。ng-model還實現(xiàn)了數(shù)據(jù)狀態(tài)的控制,比如:數(shù)據(jù)是否有效,是否是新數(shù)據(jù)等。如果ng-model在一個form指令中,ng-model指令會把自己的controller添加到form中去,和form建立關(guān)聯(lián),具體可以看form指令的說明。
詳細(xì)說明
ng-model指令的restrict屬性為'A',所以只支持屬性的方式。會依賴form指令(可選),ng-model-option(可選)。
指令的compile
ng-model的compile實現(xiàn)了pre和post兩個函數(shù),在pre函數(shù)中主要進(jìn)行的是把ng-model的controller添加到父form中去。核心代碼為:
formCtrl.$addControl(modelCtrl);
attr.$observe('name', function(newValue) {
if (modelCtrl.$name !== newValue) {
modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
}
});
scope.$on('$destroy', function() {
modelCtrl.$$parentForm.$removeControl(modelCtrl);
});
這段代碼主要實現(xiàn)的功能:
1、把model的controller添加的form的controller中去,具體實現(xiàn)過程在form指令中。
2、監(jiān)測屬性name,如果屬性值發(fā)生了變化,就修改form指令中的信息。
3、在ng-model指令銷毀的時候刪除form指令中的信息。
在post部分實現(xiàn)了對多個消息的監(jiān)聽,代碼為:
if (modelCtrl.$options.getOption('updateOn')) {
element.on(modelCtrl.$options.getOption('updateOn'), function(ev) {
modelCtrl.$$debounceViewValueCommit(ev && ev.type);
});
}
element.on('blur', function() {
if (modelCtrl.$touched) return;
if ($rootScope.$$phase) {
scope.$evalAsync(modelCtrl.$setTouched);
} else {
scope.$apply(modelCtrl.$setTouched);
}
});
在這里主要進(jìn)行了兩個過程:
1、監(jiān)聽ng-model-option中updateOn指定的消息,調(diào)用model controller的方法,進(jìn)行數(shù)據(jù)更新。
2、監(jiān)聽blur消息,會調(diào)用$apply或者$evalAsync方法進(jìn)行頁面更新。
指令的controller
在controller中定義了很多個方法:
| 方法 | 說明 |
|---|---|
| $$initGetterSetters | 初始化get和set方法,需要和option配合使用 |
| $render | 默認(rèn)是空函數(shù) |
| $isEmpty | 判斷一個值是不是空的 |
| $$updateEmptyClasses | 更新是否為empty的css類,設(shè)置ng-empty和ng-not-empty類 |
| $setPristine | 標(biāo)記數(shù)據(jù)是新的,刪除ng-dirty類,添加ng-pristine類 |
| $setDirty | 標(biāo)記數(shù)據(jù)被修改過,和$setPristine剛好相反,會調(diào)用父form的$setDirty |
| $setUntouched | 標(biāo)記為未觸摸狀態(tài),會設(shè)置ng-untouched類 |
| $setTouched | 和$setUntouched剛好相反 |
| $rollbackViewValue | 數(shù)據(jù)回滾 |
| $validate | 重新計算數(shù)據(jù)的有效性,受option的allowInvalid屬性影響 |
| $$runValidators | 直接運行有效性判斷 |
| $commitViewValue | 提交數(shù)據(jù)到scope |
| $$parseAndValidate | 解析數(shù)據(jù),然后判斷有效性,如果有效就會添加到scope中去 |
| $$writeModelToScope | 把model中的數(shù)據(jù)寫入scope中去 |
| $setViewValue | 設(shè)置view里的值 |
| $$debounceViewValueCommit | 默認(rèn)數(shù)據(jù)提交函數(shù) |
最后controller中還添加了數(shù)據(jù)監(jiān)控:
$scope.$watch(function ngModelWatch() {
var modelValue = ngModelGet($scope);
// if scope model value and ngModel value are out of sync
// TODO(perf): why not move this to the action fn?
if (modelValue !== ctrl.$modelValue &&
// checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
// eslint-disable-next-line no-self-compare
(ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
) {
ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
parserValid = undefined;
var formatters = ctrl.$formatters,
idx = formatters.length;
var viewValue = modelValue;
while (idx--) {
viewValue = formatters[idx](viewValue);
}
if (ctrl.$viewValue !== viewValue) {
ctrl.$$updateEmptyClasses(viewValue);
ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
ctrl.$render();
// It is possible that model and view value have been updated during render
ctrl.$$runValidators(ctrl.$modelValue, ctrl.$viewValue, noop);
}
}
return modelValue;
});
在這段代碼中主要干的事情:
1、獲取ng-model綁定的值
2、根據(jù)$formatters屬性指定的內(nèi)容進(jìn)行數(shù)據(jù)格式化,所有的格式化函數(shù)都會被迭代執(zhí)行。
3、更新數(shù)據(jù),包括更新class,運行有效性校驗等。
樣例代碼
<!DOCTYPE html>
<html lang="en" ng-app="app">
<!--<html>-->
<head>
<title>Test</title>
</head>
<body>
<style>
.my-input {
transition:all linear 0.5s;
background: transparent;
}
.my-input.ng-invalid {
color:white;
background: red;
}
</style>
<p id="inputDescription">
Update input to see transitions when valid/invalid.
Integer is a valid value.
</p>
<form name="testForm" ng-controller="ExampleController">
<input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
aria-describedby="inputDescription" />
</form>
<script src="./node_modules/angular/angular.js" type="text/javascript"></script>
<script>
angular.module('app', [])
.controller('ExampleController', ['$scope', function ($scope) {
$scope.val = '1';
}]);
</script>
</body>
</html>
這段代碼實現(xiàn)了對輸入數(shù)據(jù)進(jìn)行綁定、格式校驗的功能。