Angular2测试:嵌套基于表单的父/子组件和从父验证 [英] Angular2 beta: nesting form-based parent/child components and validating from parent
问题描述
我想在Angular2(试用版0与TS在Plunker)与2嵌套形式的方案来实现,每个重由组件psented $ P $。
父组件是字
,从而重新presents在一个假的字典的单词。孩子们组件 WordSense
的,每个重presenting父字的感觉。
这两个组件使用模型驱动的形式,和子窗体绑定其模式的价值使用 ngModel
形成对照。这样,父组件可以很容易地通过其词的感官下来给孩子组件,以及2路绑定做休息。
简单的自定义验证连接到两种形式。除其他事项外,我想禁用提交按钮不仅在词形是无效的,而且当任何感官无效。为此,我添加了一个的isValid
属性模型被编辑和code,观察在这个意义上形式的改变:每当发生变化时,我检查形式的有效
属性,并相应设置模型的属性。我可以然后轻松地在该视图和code父组件的水平增加一个检查,所以我可以张贴只有当这两种形式都OK了。
要支持自定义的验证和额外的逻辑,我转我的初始code从模板为基础,以基于模型的形式;然而,当我启动重构code,我得到几个否指令标注找到错误,我不知道他们的意思。
也许我缺少明显的东西,但我是一个新手在这里。谁能给个建议?你会发现在这个plunker一个摄制: http://plnkr.co/edit/v9Dj5j5opJmonxEeotcR 。下面是它的一些重要的code:
一)父组件:
@Component({
选择:字,
指令:[FORM_DIRECTIVES,FORM_PROVIDERS,WordSense]
templateUrl:`
< DIV>
<形成[ngFormModel] =wordForm
(ngSubmit)=的onSubmit(wordForm.value)
角色=形式> < DIV CLASS =表单组
[class.has错误] =lemma.valid&安培;!&安培; lemma.touched>
<标签=引理>外稃< /标签>
<输入类型=文本ID =引理
MAXLENGTH =100所需的拼写检查=假
类=表格控
占位=引理
[ngFormControl] =wordForm.controls [引理']>
< DIV * ngIf =lemma.hasError(必要)及和放大器; lemma.touched
类=TEXT-危险小>要求外稃< / DIV>
&所述;股利* ngIf =lemma.hasError('lemmaValidator')及与放大器; lemma.touched
类=TEXT-危险小>无效引理< / DIV>
< / DIV>
...
< DIV CLASS =表单组>
<表类=表表镶上>
<&TBODY GT;
< TR * ngFor =感官#的>
&所述; TD>
<词义[意义] =S[队伍] =行列[田] =田>< /词义>
< / TD>
< / TR>
< / TBODY>
< /表>
< / DIV>
...
<按钮式=提交
[ngClass] ={禁用:wordForm.valid}
类=BTN BTN-主要BTN-SM>保存< /按钮>
< /表及GT;
< / DIV>
`
输入:[
字
]
})
出口类字{
私人_word:IWordModel; 公开组字(值:IWordModel){
this._word =价值;
this.setFormValues();
}
公众获取词(){
返回this._word;
}
// ... //形式
公共wordForm:ControlGroup;
公共引理:控制;
公共语言:控制;
公共类:控制;
公共行列:IPair<&号GT; [];
公众的感觉:ISenseViewModel [];
公共字段:IFieldModel []; 构造函数(私人formBuilder:FormBuilder){
// ...
this.senses = [
this.createSense()
];
// ...
//构建形式
this.wordForm = this.formBuilder.group({
引理:[,Validators.compose([Validators.required,LemmaValidator.isValidLemma])],
语言:工程,Validators.required]
阶级:[S,Validators.required]
});
this.lemma =<控制> this.wordForm.controls [引理];
this.language =<控制> this.wordForm.controls [语言];
this.class =<控制> this.wordForm.controls [类];
// ...
}
}
二)子组件:
@Component({
选择:词义
指令:[FORM_DIRECTIVES]
模板:`
<窗体类=表单内联角色=形式[ngFormModel] =senseForm> < DIV CLASS =表单组
[class.has错误] =definitionCtl.valid!>
<输入类型=文本
类=表格控
占位=定义
[ngFormControl] =definitionCtl
[(ngModel)] =sense.definition>
< / DIV> < DIV CLASS =表单组
[class.has错误] =yearCtl.valid!>
<输入类型=数字
类=表格控
占位=日期
[ngFormControl] =yearCtl
[(ngModel)] =sense.year>
< / DIV>
...
< /表及GT;
`
输入:[
感,
行列,
田
]
})
出口类WordSense {
//模型被编辑
公众意识:ISenseViewModel;
//查找数据
公共行列:IPair<&号GT; [];
公共字段:IFieldModel [];
公共领域:IFieldModel;
// form
公共senseForm:ControlGroup;
公共definitionCtl:控制;
公共yearCtl:控制;
公共rankCtl:控制;
公共fieldsCtl:控制; 构造函数(私人formBuilder:FormBuilder){
this.senseForm = this.formBuilder.group({
定义:[,Validators.required]
年:[0,Validators.compose([Validators.required,YearValidator.isValidYear])],
等级:[{值:2标签:媒体},Validators.required]
田:]
});
this.definitionCtl =<控制> this.senseForm.controls [定义];
this.yearCtl =<控制> this.senseForm.controls [年];
this.rankCtl =<控制> this.senseForm.controls [军衔];
this.fieldsCtl =<控制> this.senseForm.controls [字段];
}
// ...
}
要拥有更具可读性的错误,你可以改变Angular2 .min.js
文件复制到 .dev.js
的。
这样做,你现在有以下错误:
没有上FormBuilder找到指令注释
块引用>事实上,问题是你设置你的组件的
FORM_PROVIDERS
到指令
属性。所以它尝试使用提供商的指令,但它们不是...@Component({
选择:字,
指令:[FORM_DIRECTIVES,FORM_PROVIDERS,WordSense]< -----
templateUrl:`
< DIV>
(......)
`
})
出口类...删除它应该解决您的问题:
@Component({
选择:字,
指令:[FORM_DIRECTIVES,WordSense]< -----
templateUrl:`
< DIV>
(......)
`
})
出口类...另一个问题是,你使用
templateUrl
而不是模板
您字
组件:@Component({
选择:字,
指令:[FORM_DIRECTIVES,WordSense]
templateUrl:`< ----------
`
(......)您应该使用这样的:
@Component({
选择:字,
指令:[FORM_DIRECTIVES,WordSense]
templateUrl:`< ----------
`
(......)下面是重构plunkr: http://plnkr.co/edit/x0d5oiW1J9C2JrJG8NdT p = preVIEW 。
希望它可以帮助你,
蒂埃里I'm trying to implement in Angular2 (beta 0 with TS in the Plunker) a scenario with 2 nested forms, each represented by a component.
The parent component is
Word
, which represents the word in a fake dictionary. The children components areWordSense
's, each representing a sense of the parent word.Both the components use model-driven forms, and the child form binds its model's values to form controls using
ngModel
. This way, the parent component can easily pass its word's senses down to the children components, and 2-way bindings do the rest.Simple custom validators are attached to both forms. Among other things, I'd like to disable the submit button not only when the word form is invalid, but also when any of its senses is invalid. To this end, I added an
isValid
property to the model being edited, and code to observe changes in the sense form: whenever a change occurs, I check the form'svalid
property and set the model's property accordingly. I could then easily add a check at the level of the parent component in the view and in the code so I can post only when both forms are OK.To support custom validation and additional logic, I switched my initial code from template-based to model-based forms; yet, as soon as I launch the refactored code I get several No Directive annotation found errors, and I'm not sure about their meaning.
Probably I'm missing something obvious, but I'm a newbie here. Could anyone give a suggestion? You can find a repro at this plunker: http://plnkr.co/edit/v9Dj5j5opJmonxEeotcR. Here is some essential code from it:
a) the parent component:
@Component({ selector: "word", directives: [FORM_DIRECTIVES, FORM_PROVIDERS, WordSense], templateUrl: ` <div> <form [ngFormModel]="wordForm" (ngSubmit)="onSubmit(wordForm.value)" role="form"> <div class="form-group" [class.has-error]="!lemma.valid && lemma.touched"> <label for="lemma">lemma</label> <input type="text" id="lemma" maxlength="100" required spellcheck="false" class="form-control" placeholder="lemma" [ngFormControl]="wordForm.controls['lemma']"> <div *ngIf="lemma.hasError('required') && lemma.touched" class="text-danger small">lemma required</div> <div *ngIf="lemma.hasError('lemmaValidator') && lemma.touched" class="text-danger small">invalid lemma</div> </div> ... <div class="form-group"> <table class="table table-bordered"> <tbody> <tr *ngFor="#s of senses"> <td> <word-sense [sense]="s" [ranks]="ranks" [fields]="fields"></word-sense> </td> </tr> </tbody> </table> </div> ... <button type="submit" [ngClass]="{disabled: !wordForm.valid}" class="btn btn-primary btn-sm">save</button> </form> </div> `, inputs: [ "word" ] }) export class Word { private _word: IWordModel; public set word(value: IWordModel) { this._word = value; this.setFormValues(); } public get word() { return this._word; } // ... // form public wordForm: ControlGroup; public lemma: Control; public language: Control; public class: Control; public ranks: IPair<number>[]; public senses: ISenseViewModel[]; public fields: IFieldModel[]; constructor(private formBuilder:FormBuilder) { // ... this.senses = [ this.createSense() ]; // ... // build the form this.wordForm = this.formBuilder.group({ "lemma": ["", Validators.compose([Validators.required, LemmaValidator.isValidLemma])], "language": ["eng", Validators.required], "class": ["s.", Validators.required], }); this.lemma = <Control> this.wordForm.controls["lemma"]; this.language = <Control> this.wordForm.controls["language"]; this.class = <Control> this.wordForm.controls["class"]; // ... } }
b) the child component:
@Component({ selector: "word-sense", directives: [FORM_DIRECTIVES], template: ` <form class="form-inline" role="form" [ngFormModel]="senseForm"> <div class="form-group" [class.has-error]="!definitionCtl.valid"> <input type="text" class="form-control" placeholder="definition" [ngFormControl]="definitionCtl" [(ngModel)]="sense.definition"> </div> <div class="form-group" [class.has-error]="!yearCtl.valid"> <input type="number" class="form-control" placeholder="date" [ngFormControl]="yearCtl" [(ngModel)]="sense.year"> </div> ... </form> `, inputs: [ "sense", "ranks", "fields" ] }) export class WordSense { // model being edited public sense: ISenseViewModel; // lookup data public ranks: IPair<number>[]; public fields: IFieldModel[]; public field: IFieldModel; // form public senseForm: ControlGroup; public definitionCtl: Control; public yearCtl: Control; public rankCtl: Control; public fieldsCtl: Control; constructor(private formBuilder: FormBuilder) { this.senseForm = this.formBuilder.group({ "definition": ["", Validators.required], "year": [0, Validators.compose([Validators.required, YearValidator.isValidYear])], "rank": [{value: 2, label: "media"}, Validators.required], "fields": [""] }); this.definitionCtl = <Control> this.senseForm.controls["definition"]; this.yearCtl = <Control> this.senseForm.controls["year"]; this.rankCtl = <Control> this.senseForm.controls["rank"]; this.fieldsCtl = <Control> this.senseForm.controls["fields"]; } // ... }
解决方案To have more readable errors, you can change Angular2
.min.js
files to the.dev.js
ones.Doing this, you have now the following error:
No Directive annotation found on FormBuilder
In fact, the problem is that you set the
FORM_PROVIDERS
into thedirectives
attribute of your component. So it tries to use providers as directives but they aren't...@Component({ selector: "word", directives: [FORM_DIRECTIVES, FORM_PROVIDERS, WordSense], <----- templateUrl: ` <div> (...) ` }) export class ...
Removing it should fix your problem:
@Component({ selector: "word", directives: [FORM_DIRECTIVES, WordSense], <----- templateUrl: ` <div> (...) ` }) export class ...
Another problem is that you use
templateUrl
instead oftemplate
for yourWord
component:@Component({ selector: "word", directives: [FORM_DIRECTIVES,WordSense], templateUrl: ` <---------- ` (...)
You should use this instead:
@Component({ selector: "word", directives: [FORM_DIRECTIVES,WordSense], templateUrl: ` <---------- ` (...)
Here is the refactored plunkr: http://plnkr.co/edit/x0d5oiW1J9C2JrJG8NdT?p=preview.
Hope it helps you, Thierry
这篇关于Angular2测试:嵌套基于表单的父/子组件和从父验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!