在带有嵌入表单的自定义指令中使用ngModel,并进行有效的验证
Use ngModel on a custom directive with an embedded form, with working validation?
我有一组在整个应用程序中重用的常用表单输入,所以我试图将它们封装在自定义指令中。我想在我的指令上设置一个ngModel
,并将其拆分为可在主指令内的几个不同输入(其中一些是指令本身)中编辑。
同时,我需要将表单验证结果向上传递到父表单,以便我可以显示适当的消息和样式。
实现这个的最简单和最习惯的方法是什么?
这些(简化的)模板应该给你一个我要做什么的例子…
OuterTemplate.html
<form name="outerForm">
<my-directive
ng-model="ctrl.myComplexModel"
name="myDirectiveInstance"
custom-required="ctrl.EnableValidateOne"
toggle-another-validation="ctrl.EnableValidateTwo">
</my-directive>
<div ng-messages="outerForm.myDirectiveInstance.$error">
<ng-message when="customRequired">This is required.</ng-message>
<ng-message when="anotherValidation">This is required.</ng-message>
<ng-message when="innerValidationOne">Something wrong with field 1.</ng-message>
<ng-message when="innerValidationTwo">Something wrong with field 2.</ng-message>
<ng-message when="innerValidationThree">Something wrong with field 3.</ng-message>
<!-- etc... -->
</div>
</form>
myDirectiveTemplate.html
<div ng-form="myDirectiveForm">
<div ng-class="{'has-error': myDirectiveForm.fieldOne.$invalid}">
<ui-select
ng-model="model.fieldOne"
name="fieldOne"
required>
</ui-select>
</div>
<div ng-class="{'has-error': myDirectiveForm.fieldTwo.$invalid}">
<input
type="number"
ng-model="model.fieldTwo"
name="fieldTwo"
ng-pattern="directiveCtrl.someRegEx"
ng-required="directiveCtrl.fieldTwoIsRequired">
</div>
<!-- etc... -->
</div>
目前,myDirectiveForm
和myDirectiveInstance
都将自己发布为outerForm
FormController
的属性。我希望使这个指令一个黑盒子,所以myDirectiveForm
直接连接到outerForm
的事实困扰着我,似乎表明我做错了什么。
这是我的指令定义现在的样子。
myDirective.js
app.directive('myDirective', function() {
return {
restrict: 'E',
template: 'myDirectiveTemplate.html',
controller: 'MyDirectiveCtrl',
scope: {
model: '=ngModel',
customRequired: '=?',
toggleAnotherValidation: '=?'
},
require: 'ngModel',
link: function(scope, iElem, iAttrs, ngModelController) {
// Black-box the internal validators
// Custom validator to avoid conflicts with ngRequired
ngModelController.$validators.customRequired = function(modelValue, viewValue) {
if(!scope.customRequired)
return true;
// On first digest the field isn't registered on the form controller yet
if(angular.isUndefined(scope.myDirectiveForm.fieldOne))
return true;
return !scope.myDirectiveForm.fieldOne.$error.required;
};
ngModelController.$validators.anotherValidation = function(modelValue, viewValue) {
if(!scope.anotherValidation)
return true;
return scope.passesBusinessRule();
};
ngModelController.$validators.innerValidationOne = function(modelValue, viewValue) {
if(!scope.anotherValidation)
return true;
if(angular.isUndefined(scope.myDirectiveForm.fieldTwo))
return true;
return !scope.myDirectiveForm.fieldTwo.$error.pattern;
};
/* etc... */
// Deep-watching model so that validations will trigger on updates of properties
scope.$watch('model', function() {
ngModelController.$validate();
}, true);
}
};
});
这就是我对指令的理解。在这种情况下,ng-model和myDirective都是指令。我不清楚如果你在做
1) ng模型OR上的包装器2)自定义指令
因为如果你想做自定义指令,你可以直接传入数据,例如
{scope: {data: '='}
如果你想做一个包装器,你可能不应该传入其他与ngModel相关的属性,这意味着你仍然可以传入数据
ctrl.myComplexModel
, btw。模型对象不能被赋值给ng-model,因为ng-model并不保存对象,它只是保存数据。
注:实际上我发现这篇文章,AngularJS -创建一个指令,使用ng-model
显然,你可以传入model,https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
无论如何,它对我来说太复杂了:)如果你想做一个包装器,模式对我来说似乎
- 传入数据
- "有一个"对象
但显然你可能在做"is a"对象
我想出了一个不错的解决办法。简而言之,我已经从我的自定义指令中删除了NgModelController
的实现,我完全依赖于我的自定义指令中的form
指令的内部FormController
。据我所知,NgModelController
只是没有设计成包装自定义指令表单。然而,嵌套表单在Angular中得到了很好的支持,所以这是可行的方法。
我还没有意识到的是,从Angular 1.3开始,你可以动态地为表单分配名称。虽然我不能阻止"黑盒"泄漏和附加自己到父窗体控制器,我至少可以控制它用来在父范围内发布自己的名称,这是可以接受的,是非常类似于ngModel
提供的API。
更新了下面的例子。
OuterTemplate.html
<form name="outerForm">
<my-directive
model="ctrl.myComplexModel"
name="myDirectiveInstance"
custom-required="ctrl.EnableValidateOne"
toggle-another-validation="ctrl.EnableValidateTwo">
</my-directive>
<div>
<span ng-if="outerForm.myDirectiveInstance.fieldOne.$error.required">Internal field 1 is required.</span>
<span ng-if="outerForm.myDirectiveInstance.fieldTwo.$error.required">Internal field 2 is required.</span>
<span ng-if="outerForm.myDirectiveInstance.fieldTwo.$error.pattern">Internal field 2 format error.</span>
<!-- etc... -->
<ng-messages for="outerForm.myDirectiveInstance.$error">
<ng-message when="required">At least one required field is missing.</ng-message>
<ng-message when="custom">
Some directive-wide error set by validate-custom on outerForm.myDirectiveInstance.internalField
</ng-message>
<!-- etc... -->
</ng-messages>
</div>
</form>
在外部模板中,我删除了ng-model
指令,以支持自定义属性。name
属性仍然可以用来决定内部表单以什么名称发布。
或者,可以保留ng-model
,并且可以使用属性form-name
(对下面的隔离作用域绑定进行适当的更改)来将自定义指令的FormController
发布到父FormController
,但这可能有些误导,因为ng-model
指令除了隔离作用域绑定之外没有用于任何事情。
无论哪种方式,ng-model
都不应该在这个用例中与name
属性一起使用。否则,可能会有冲突,因为NgModelController
和FormController
试图以相同的属性名(outerForm.myDirectiveInstance
)将自己发布给父FormController
(outerForm
)。
由于验证错误会冒泡到父form
指令,ngMessages
可以与这个自定义指令一起使用,如下所示。对于更细粒度的错误处理,也可以访问指令的内部字段。
myDirectiveTemplate.html
<div ng-form="{{ formName }}">
<div ng-class="{'has-error': isInvalid('fieldOne')}">
<ui-select
ng-model="model.fieldOne"
name="fieldOne"
required>
</ui-select>
</div>
<div ng-class="{'has-error': isInvalid('fieldTwo')}">
<input
type="number"
ng-model="model.fieldTwo"
name="fieldTwo"
ng-pattern="directiveCtrl.someRegEx"
ng-required="directiveCtrl.fieldTwoIsRequired">
</div>
<!-- etc... -->
<input
type="hidden"
ng-model="someCalculatedValue"
name="internalField"
validate-custom>
</div>
指令的内部模板基本保持不变。最大的不同是ngForm
的名称现在是动态设置的。
为了用ngClass处理这个问题,angular表达式不能工作,所以我更新了我的例子,在$scope
上使用一个函数。
最后,对于指令范围的业务规则,我使用带有ngModel
指令和name
集的隐藏输入。我附加了一个用于验证的自定义迷你指令到这个字段。该字段的验证错误将被父指令使用。
myDirective.js
app.directive('myDirective', function() {
return {
restrict: 'E',
template: 'myDirectiveTemplate.html',
controller: 'MyDirectiveCtrl',
scope: {
model: '=',
customRequired: '=?',
toggleAnotherValidation: '=?',
formName: '@name'
},
};
});
现在几乎所有的逻辑都从指令定义中删除了。
- 正在验证8个真/假复选框或复选框中的2个
- 借助asp.net验证或java脚本对多个文本进行验证
- jQuery自定义验证比较多个输入的序列
- 使用html中的外部javascript进行数据验证
- 如何使用jquery Validation验证Formspread
- jquery中的文本框验证
- 在验证和发送邮件后更改联系人表单的 html
- 代码不会验证
- JS验证ajax返回的html中的表单数据
- 同步调用,直到用户通过angular验证为访问者
- 带有加号的电话号码验证(可选)
- 解析javascript表单验证器
- 两位数的月份日期验证
- 使用angularjs验证文本框中的电子邮件
- 验证Javascript中的Textarea
- AngularJS摘要周期错误,ngmodel在自定义验证时被删除
- 什么是ngModel$验证器管道
- 在带有嵌入表单的自定义指令中使用ngModel,并进行有效的验证
- 使用 AngularJS ngModel 和表单验证来$watch两个元素,并在任一输入更改时测试它们是否相等
- Angular: $scope.$watch($ attrs . ngmodel)只在指令的验证器返回true时起作用