我正在处理一个应用程序,当用户更改某些内容时会自动保存更改,例如输入字段的值。我编写了一个autosave
指令,该指令被添加到应该自动触发保存事件的所有表单字段中。
模板:
<input ng-model="fooCtrl.name" autosave>
<input ng-model="fooCtrl.email" autosave>
指令:
.directive('autosave', ['$parse', function ($parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
function saveIfModelChanged () {
// save object containing name and email to server ...
}
ngModel.$viewChangeListeners.push(function () {
saveIfModelChanged();
});
}
};
}]);
到目前为止,这一切对我来说都很好。但是,当我在混合中添加验证时,例如验证输入字段是有效的电子邮件地址,只要viewValue更改为无效的电子邮件地址,modelValue就会设置为undefined
。
我想要做的是:记住最后一个有效的modelValue并在autosaving时使用它。如果用户将电子邮件地址更改为无效,则包含name
和email
的对象仍应保存到服务器。使用当前有效的name
和最后一个有效的email
。
我开始时保存最后一个有效的modelValue:
添加了验证的模板:
<input type="email" ng-model="fooCtrl.name" autosave required>
<input ng-model="fooCtrl.email" autosave required>
保存lastModelValue的指令:
.directive('autosave', ['$parse', function ($parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
var lastModelValue;
function saveIfModelChanged () {
// remeber last valid modelValue
if (ngModel.$valid) {
lastModelValue = ngModel.$modelValue;
}
// save object containing current or last valid
// name and email to server ...
}
ngModel.$viewChangeListeners.push(function () {
saveIfModelChanged();
});
}
};
}]);
我的问题是,如何在保存时使用lastModelValue
,但在视图中保留无效值?
修改
另一种可能性as suggested by Jugnu below,就是在验证器中包装和操作构建。
我试着跟随:包装所有现有验证器并记住最后一个有效值,如果验证失败则恢复它:
Object.keys(ngModel.$validators).forEach(function(validatorName, index) {
var validator = ngModel.$validators[validatorName];
ngModel.$validators[validatorName] = createWrapper(validatorName, validator, ngModel);
});
function createWrapper(validatorName, validator, ngModel){
var lastValid;
return function (modelValue){
var result = validator(modelValue);
if(result) {
lastValid = modelValue;
}else{
// what to do here? maybe asign the value like this:
// $parse(attrs.ngModel).assign(scope, lastValid);
}
return result;
};
}
但我不确定如何继续使用这种方法。我可以在没有AngularJS的情况下设置模型值并尝试验证新设置的值吗?
答案 0 :(得分:3)
由于您要为每个字段修改发送整个对象,因此必须将该整个对象的最后一个有效状态保留在某处。我记住的用例:
{ name: 'Valid', email: 'Valid' }
。autosave
指令知道它自己的最后一个有效值,因此发送了正确的对象。autosave
指令知道它自己的最后有效值,但不知道名称的有效值。如果最后的已知良好值未集中,则会发送{ name: 'inalid', email: 'Valid' }
之类的对象。所以建议:
fooCtrl.lastKnowngood
。让autosave
知道上次知道的良好状态,例如:为:
<input ng-model="fooCtrl.email" autosave="fooCtrl.lastKnowngood" required />
保留该对象中最后一个已知的良好本地值;利用ng-model
表达式,例如为:
var lastKnownGoodExpr = $parse(attrs.autosave);
var modelExpr = $parse(attrs.ngModel);
function saveIfModelChanged () {
var lastKnownGood = lastKnownGoodExpr(scope);
if (ngModel.$valid) {
// trick here; explanation later
modelExpr.assign({fooCtrl: lastKnownGood}, ngModel.$modelValue);
}
// send the lastKnownGood object to the server!!!
}
lastKnownGood
对象。 技巧,缺点以及如何改进:将本地模型值设置为lastKnownGood
对象时,使用不同于当前范围的上下文对象;此对象假定控制器名为fooCtrl
(请参阅第modelExpr.assign({fooCtrl: lastKnownGood}, ...)
行)。如果您想要更通用的指令,您可能希望将根作为不同的属性传递,例如:
<input ng-model="fooCtrl.email" autosave="fooCtrl.lastKnowngood" required
autosave-fake-root="fooCtrl" />
您也可以自己对ng-model
表达式进行一些解析,以确定第一个组件,例如:子串0→点的第一次出现(再次简单化)。
另一个缺点是如何处理更复杂的路径(在一般情况下),例如fooCtrl.persons[13].address['home'].street
- 但这似乎不是你的用例。
顺便说一下,这个:
ngModel.$viewChangeListeners.push(function () {
saveIfModelChanged();
});
可以简化为:
ngModel.$viewChangeListeners.push(saveIfModelChanged);
答案 1 :(得分:3)
我创建了一个简单的指令,作为 ng-model 指令的包装器,并始终保持最新的有效模型值。它被称为 valid-ng-model ,应该在您想拥有最新有效值的地方替换 ng-model 的用法。
我已经创建了一个示例用例here,我希望你会喜欢它。欢迎任何改进的想法。
这是 valid-ng-model 指令的实现代码。
app.directive('validNgModel', function ($compile) {
return {
terminal: true,
priority: 1000,
scope: {
validNgModel: '=validNgModel'
},
link: function link(scope, element, attrs) {
// NOTE: add ngModel directive with custom model defined on the isolate scope
scope.customNgModel = angular.copy(scope.validNgModel);
element.attr('ng-model', 'customNgModel');
element.removeAttr('valid-ng-model');
// NOTE: recompile the element without this directive
var compiledElement = $compile(element)(scope);
var ngModelCtrl = compiledElement.controller('ngModel');
// NOTE: Synchronizing (inner ngModel -> outside valid model)
scope.$watch('customNgModel', function (newModelValue) {
if (ngModelCtrl.$valid) {
scope.validNgModel = newModelValue;
}
});
// NOTE: Synchronizing (outside model -> inner ngModel)
scope.$watch('validNgModel', function (newOutsideModelValue) {
scope.customNgModel = newOutsideModelValue;
});
}
};
});
编辑:没有隔离范围的指令实现:Plunker。
答案 2 :(得分:2)
如果有效的电子邮件地址,Angular默认验证器只会为模型赋值。为了克服这一点,您需要覆盖默认验证器。
有关详细信息,请参阅:https://docs.angularjs.org/guide/forms#modifying-built-in-validators
您可以创建一个directive
,它会将荣誉模型值分配给某个范围变量,然后您就可以使用它。
我已经创建了一个用于电子邮件验证的小型演示,但您可以将其扩展到涵盖所有其他验证器。