希望这将是一个更通用的 Angular JS 问题,而不是特定于 Angular Formly 的问题。
我一直在遵循此处提供的框架,以在有角度的形式上构建错误摘要。一切正常......但是!
在示例中,他们的模型如下:
vm.fields = [
{
key: 'picky',
type: 'customInput',
templateOptions: {
label: 'Picky field...',
placeholder: 'This is required and has a maxlength of 5 and minlength of 3',
required: true,
maxlength: 5,
minlength: 3
}
},
.....
{
key: 'ip',
type: 'customInput',
validators: {
ipAddress: {
expression: function(viewValue, modelValue) {
var value = modelValue || viewValue;
return /(\d{1,3}\.){3}\d{1,3}/.test(value);
},
message: '$viewValue + " is not a valid IP Address"'
}
},
templateOptions: {
label: 'IP Address',
required: true,
type: 'text',
placeholder: '127.0.0.1',
}
}
];
然后,如果我们查看 HTML,我们可以看到这些字段被传递到错误摘要中,如下所示:
<formly-error-summary form="vm.form" fields="vm.fields"></formly-error-summary>
对于简单的表单结构,这可以正常工作,但是,如果您想使用 Bootstrap 布局,如此处所述,那么您的模型最终会看起来像我的模型:
vm.rentalFields = [
{
template: '<div class="row"><div class="col-xs-12"><h3>About You</h3></div></div>'
},
{
className: 'row',
fieldGroup: [
{
className: 'col-xs-6',
type: 'customInput',
key: 'first_name',
templateOptions: {
type: 'text',
label: 'First Name',
placeholder: 'Enter your first name',
required: true
}
},
{
className: 'col-xs-6',
type: 'customInput',
key: 'last_name',
templateOptions: {
type: 'text',
label: 'Last Name',
placeholder: 'Enter your last name',
required: true
},
expressionProperties: {
'templateOptions.disabled': '!model.first_name'
}
}
]
},
{
template: '<div class="row"><div class="col-xs-12"><h3>License and Insurance Details</h3></div></div>',
hideExpression: '!model.email'
}
.....
现在,当我们将 vm.rentalFields 传递给错误摘要时,它不是访问字段,而是验证每个对象。我可以通过执行以下操作来解决此问题:
<formly-error-summary form="vm.rentalForm" fields="vm.rentalFields[1].fieldGroup"></formly-error-summary>
这当然不是理想的,因为在其他字段组中会有我想要验证的字段,以证明这个问题,虽然现在很好。我试过只传入'vm.rentalFields.fieldGroup',但正如我所怀疑的那样,它什么也没返回。
那么,有没有一种方法可以递归地传入 vm.rentalField 对象中的所有 fieldGroups,或者这是我应该在指令本身的代码中处理的东西。
angular.module("formlyApp").directive('formlyErrorSummary', function() {
return {
scope: {},
bindToController: {
form: '=',
fields: '='
},
templateUrl: 'js/Directives/formly-error-summary.html',
controllerAs: 'vm',
controller: function() {
var vm = this;
vm.getErrorAsList = getErrorAsList;
console.log(vm.fields);
function getErrorAsList(field) {
return Object.keys(field.formControl.$error).map(function(error) {
// note, this only works because the customInput type we have defined.
return field.data.getValidationMessage(error);
}).join(', ');
}
}
};
});
编辑
好的,所以,在听取了下面 Ken 的建议后,我已经能够修改我的 formlyErrorSummary 指令,以便它现在至少能够获取模型的错误。这有很多问题,因为 $scope.$watch 正在进行深度比较,甚至在第一页加载时,整个事情都被触发了 3 次!我已经添加了一些基本的转义来尝试解决这个问题,现在至少我有错误,我遇到的下一个问题是在我调用的 HTML 中,ng-repeat="field in vm.fields"
这实际上是同一个问题,所以我将如何解决这个问题? 我的一部分是在考虑一些匿名对象,它会保存字段消息以及是否有效,然后在 HTML 中解析它,但我不确定这种思维方式是否适用于 Angular?
controller: function($scope) {
var vm = this;
$scope.$watch('vm.fields', function(){
for(var i = 0; i < vm.fields.length; i++)
if(vm.fields[i].fieldGroup) {
for(var j = 0; j < vm.fields[i].fieldGroup.length; j ++)
if(vm.fields[i].fieldGroup[j].formControl) {
var err = getErrorAsList(vm.fields[i].fieldGroup[j]);
if(err)
vm.getErrorAsList = err;
}
}
}, true);
解决方案 - 可能
经过多次修改后,我想我终于有了这个工作,因此错误消息现在同时显示在内联和顶部的摘要中。
我的最终指令函数现在每次运行时都会创建一个数组,该数组将保存所有错误消息,它必须被刷新,$watch
否则当字段有效时,错误消息将保留在数组中,所以我们只需重建整个每次的事情......鉴于我已经在这里使用了深层手表,我希望任何性能影响都可以忽略不计。
vm.errs = [];
$scope.$watch('vm.fields', function(){
vm.errs = [];
for(var i = 0; i < vm.fields.length; i++)
if(vm.fields[i].fieldGroup) {
for(var j = 0; j < vm.fields[i].fieldGroup.length; j ++)
if(vm.fields[i].fieldGroup[j].formControl) {
var err = getErrorAsList(vm.fields[i].fieldGroup[j]);
if(err)
if(vm.errs.indexOf(err) === -1)
vm.errs.push(err);
}
}
}, true);
然后,在指令模板中,我不得不删除vm.fields
引用,因为这显然不适用于这种方法。由于我知道只有在表单无效时才会显示此摘要,因此我可以删除正在执行的其他检查,并最终以以下 HTML 结束:
<div class="row">
<div class="col-xs-12">
<div class="formly-error-summary bg-danger" ng-if="vm.form.$invalid">
<div ng-repeat="err in vm.errs" class="color-error">
<i class="glyphicon glyphicon-remove"></i>
<span>
{{err}}
</span>
</div>
</div>
</div>
</div>
我仍然对此不是 100% 满意,它完成了工作,但我不确定这是否是“角度”的方式,而且我$scope.$watch
在字段对象上使用的事实有点烦人我的开发人员强迫症,但解决方案都是一样的。
如果有人对此有任何改进或改进建议,请告诉我,仍然可以掌握 Angular,但这是一次非常有趣的学习体验!