作者:mobiledu2502886131 | 来源:互联网 | 2024-11-24 13:01
在AngularJS中,有时需要在表单内包含某些控件,但又不希望这些控件导致表单变为脏状态。例如,当用户对表单进行修改后,表单的$dirty属性将变为true,触发保存对话框。然而,对于一些导航或辅助功能控件,我们可能并不希望它们触发这种行为。
在AngularJS应用中,我遇到一个问题,即在一个页面上的表单内包含多个控件。这些控件需要在表单被修改时(即表单的$dirty属性为true)显示保存对话框。但是,表单中还包含了一些导航控件,我希望这些控件不会影响表单的脏状态。由于设计原因,我无法将这些控件移出表单。
请参阅示例:http://plnkr.co/edit/bfig4B
具体问题是,如何确保某个选择框不会使表单变脏?
解决方案
#1 使用自定义指令
通过创建一个名为'noDirtyCheck'的指令,可以阻止指定的输入元素使表单变脏。这个方案不需要使用$timeout,从而保持控制器的整洁。
.directive('noDirtyCheck', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$pristine = false;
}
}
});
使用方式如下:
#2 动态设置$pristine属性
仅在初始化时设置$pristine属性为false,这种方法在调用表单的$setPristine()方法后会失效。为了避免这个问题,可以在控件获得焦点时再次设置$pristine属性为false。
link: function(scope, elm, attrs, ctrl) {
elm.focus(function() {
ctrl.$pristine = false;
});
}
#3 覆盖$pristine和$dirty属性
为了彻底解决问题,可以覆盖ngModel的$pristine和$dirty属性,使其始终返回false。这可以通过使用Object.defineProperty来实现,虽然这种方法在IE8及其以下版本中不支持。
(function() {
angular
.module("myapp")
.directive("noDirtyCheck", noDirtyCheck);
function noDirtyCheck() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
var alwaysFalse = {
get: function() { return false; },
set: function() {}
};
Object.defineProperty(ctrl, '$pristine', alwaysFalse);
Object.defineProperty(ctrl, '$dirty', alwaysFalse);
}
};
}
})();
#4 控制器中设置$pristine
另一种简单的方法是在控制器中使用$timeout来设置$pristine属性为false。这样,即使表单中的其他控件被修改,该控件也不会影响表单的整体脏状态。
see: http://plnkr.co/edit/by3qTM
#5 增强的验证逻辑
基于上述方法,增加了一些额外的验证逻辑,并使用内联括号表示法保护代码免受压缩的影响。
"use strict";
angular.module("lantern").directive("noDirtyCheck", [function() {
return {
restrict: "A",
require: "ngModel",
link: function(scope, elem, attrs, ngModelCtrl) {
if (!ngModelCtrl) {
return;
}
var clean = (ngModelCtrl.$pristine && !ngModelCtrl.$dirty);
if (clean) {
ngModelCtrl.$pristine = false;
ngModelCtrl.$dirty = true;
}
}
};
}]);
#6 复杂的实现方案
这是一个更为复杂的实现方案,通过监听元素的焦点和失焦事件,动态地管理控件和表单的脏状态。
app.directive('noDirtyCheck', [function() {
return {
restrict: 'A',
require: ['^form', '^ngModel'],
link: function(scope, element, attrs, controllers) {
var form = controllers[0];
var currentCOntrol= controllers[1];
var formDirtyState = false;
var manualFocus = false;
element.bind('focus', function() {
manualFocus = true;
if (form) {
console.log('Saving current form ' + form.$name + ' dirty status: ' + form.$dirty);
formDirtyState = form.$dirty;
}
});
element.bind('blur', function() {
if (currentControl) {
console.log('Resetting current control (' + currentControl.$name + ') dirty status to false');
currentControl.$dirty = false;
if (!formDirtyState && form && manualFocus) {
console.log('Resetting ' + form.$name + ' form pristine state...');
form.$setPristine();
}
manualFocus = false;
}
});
}
};
}]);
#7 直接覆盖$setDirty方法
最直接的方法是直接覆盖ngModelController的$setDirty方法,使其成为无操作(noop),从而防止该控件影响表单的脏状态。
app.directive('noDirtyCheck', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, iElem, iAttrs, ngModelCtrl) {
ngModelCtrl.$setDirty = angular.noop;
}
};
});