如何将自定义输入指令及其父表单重置为 $pristine
How to reset custom input directive and its parent form to $pristine
我已经实现了自定义输入指令 - counter
具有重置功能。该指令有 require: "ngModel"
.
我正在用 $setPristine()
重置指令 ngModel
的原始状态。与 $setDirty()
不同,$setPristine()
不会触及父窗体的 $pristine
状态。
问:如何"notify"父窗体不再有这个指令"dirty",这样父窗体就可以有它的$pristine
状态重置?
请记住,仅调用 form.$setPristine()
是不够的,因为表单中可能还有其他 "dirty" 控件,我的指令不会(也不应该)知道这些控件。
这是指令的 link 函数:
link: function(scope, element, attrs, ngModel){
var original;
ngModel.$render = function(){
original = scope.counter = ngModel.$viewValue;
};
scope.up = function(){
ngModel.$setViewValue(++scope.counter);
};
scope.reset = function(){
scope.counter = original;
ngModel.$setViewValue(scope.counter);
ngModel.$setPristine(); // this sets $pristine on the directive, but not the form
};
}
使用方法如下:
<div ng-form="form">
<counter ng-model="count"></counter>
</div>
这是一个不太好的解决方案。遍历附加到表单的控件并检查是否还有脏控件。
我使用解释方法 here 来获取控件。
app.directive("counter", function(){
return {
require: "ngModel",
template: '<button ng-click="up()">{{counter}}</button><button ng-click="reset()">reset</button>',
link: function(scope, element, attrs, ngModel){
var original;
ngModel.$render = function(){
original = scope.counter = ngModel.$modelValue;
};
scope.up = function(){
ngModel.$setViewValue(++scope.counter);
};
scope.reset = function(){
scope.counter = original;
ngModel.$setViewValue(scope.counter);
ngModel.$setPristine();
// check if one of the controls attached to the form is still dirty
var dirty = false;
angular.forEach(scope.form, function(val, key) {
if (key[0] != '$') {
if (val.$dirty) {
dirty = true;
}
}
});
if(!dirty) scope.form.$setPristine();
};
}
};
});
从 Angular1.3.x 开始,没有内置解决方案。
form.$setPristine()
在其所有子控件上设置 pristine。 (link to code)
ngModel.$setPristine()
仅在其自身上设置 $pristine (link to code)
解决此问题的一种方法是创建一个与 form
指令并存的指令,并劫持 form.$setDirty
以跟踪脏控件计数。这可能最好在 link 之前的阶段完成(即在子控件开始自行注册之前)。
app.directive("pristinableForm", function() {
return {
restrict: "A",
require: ["pristinableForm", "form"],
link: function(scope, element, attrs, ctrls) {
var me = ctrls[0],
form = ctrls[1];
me.form = form;
me.dirtyCounter = 0;
var formSetDirtyFn = form.$setDirty;
form.$setDirty = function() {
me.dirtyCounter++;
formSetDirtyFn();
};
},
controller: function() {
this.$notifyPristine = function() {
if (this.dirtyCounter === 0) return;
if (this.dirtyCounter === 1) {
this.dirtyCounter = 0;
if (this.form) this.form.$setPristine();
} else {
this.dirtyCounter--;
}
};
}
};
});
然后,自定义输入指令需要require: ["ngModel", "^pristinableForm"]
并在其重置函数中调用pristinableForm.$notifyPristine()
:
scope.reset = function(){
if (ngModel.$dirty){
scope.counter = original;
ngModel.$setViewValue(scope.counter);
ngModel.$setPristine();
pristinableForm.$notifyPristine();
}
};
用法是:
<div ng-form="form" pristinable-form>
<counter ng-model="count1"></counter>
<counter ng-model="count2"></counter>
<input ng-model="foo" name="anotherControl">
</div>
我已经实现了自定义输入指令 - counter
具有重置功能。该指令有 require: "ngModel"
.
我正在用 $setPristine()
重置指令 ngModel
的原始状态。与 $setDirty()
不同,$setPristine()
不会触及父窗体的 $pristine
状态。
问:如何"notify"父窗体不再有这个指令"dirty",这样父窗体就可以有它的$pristine
状态重置?
请记住,仅调用 form.$setPristine()
是不够的,因为表单中可能还有其他 "dirty" 控件,我的指令不会(也不应该)知道这些控件。
这是指令的 link 函数:
link: function(scope, element, attrs, ngModel){
var original;
ngModel.$render = function(){
original = scope.counter = ngModel.$viewValue;
};
scope.up = function(){
ngModel.$setViewValue(++scope.counter);
};
scope.reset = function(){
scope.counter = original;
ngModel.$setViewValue(scope.counter);
ngModel.$setPristine(); // this sets $pristine on the directive, but not the form
};
}
使用方法如下:
<div ng-form="form">
<counter ng-model="count"></counter>
</div>
这是一个不太好的解决方案。遍历附加到表单的控件并检查是否还有脏控件。
我使用解释方法 here 来获取控件。
app.directive("counter", function(){
return {
require: "ngModel",
template: '<button ng-click="up()">{{counter}}</button><button ng-click="reset()">reset</button>',
link: function(scope, element, attrs, ngModel){
var original;
ngModel.$render = function(){
original = scope.counter = ngModel.$modelValue;
};
scope.up = function(){
ngModel.$setViewValue(++scope.counter);
};
scope.reset = function(){
scope.counter = original;
ngModel.$setViewValue(scope.counter);
ngModel.$setPristine();
// check if one of the controls attached to the form is still dirty
var dirty = false;
angular.forEach(scope.form, function(val, key) {
if (key[0] != '$') {
if (val.$dirty) {
dirty = true;
}
}
});
if(!dirty) scope.form.$setPristine();
};
}
};
});
从 Angular1.3.x 开始,没有内置解决方案。
form.$setPristine()
在其所有子控件上设置 pristine。 (link to code)
ngModel.$setPristine()
仅在其自身上设置 $pristine (link to code)
解决此问题的一种方法是创建一个与 form
指令并存的指令,并劫持 form.$setDirty
以跟踪脏控件计数。这可能最好在 link 之前的阶段完成(即在子控件开始自行注册之前)。
app.directive("pristinableForm", function() {
return {
restrict: "A",
require: ["pristinableForm", "form"],
link: function(scope, element, attrs, ctrls) {
var me = ctrls[0],
form = ctrls[1];
me.form = form;
me.dirtyCounter = 0;
var formSetDirtyFn = form.$setDirty;
form.$setDirty = function() {
me.dirtyCounter++;
formSetDirtyFn();
};
},
controller: function() {
this.$notifyPristine = function() {
if (this.dirtyCounter === 0) return;
if (this.dirtyCounter === 1) {
this.dirtyCounter = 0;
if (this.form) this.form.$setPristine();
} else {
this.dirtyCounter--;
}
};
}
};
});
然后,自定义输入指令需要require: ["ngModel", "^pristinableForm"]
并在其重置函数中调用pristinableForm.$notifyPristine()
:
scope.reset = function(){
if (ngModel.$dirty){
scope.counter = original;
ngModel.$setViewValue(scope.counter);
ngModel.$setPristine();
pristinableForm.$notifyPristine();
}
};
用法是:
<div ng-form="form" pristinable-form>
<counter ng-model="count1"></counter>
<counter ng-model="count2"></counter>
<input ng-model="foo" name="anotherControl">
</div>