Angular2 beta:嵌套基于表单的父/子组件并从父组件验证 [英] Angular2 beta: nesting form-based parent/child components and validating from parent
问题描述
我正在尝试在 Angular2(Plunker 中带有 TS 的 beta 0)中实现一个具有 2 个嵌套表单的场景,每个表单由一个组件表示.
父组件是Word
,代表一个假字典中的单词.子组件是 WordSense
的,每个都代表父词的一种含义.
两个组件都使用模型驱动的表单,子表单使用 ngModel
将其模型的值绑定到表单控件.这样,父组件可以轻松地将其词义传递给子组件,剩下的事情就由 2 向绑定完成.
两个表单都附加了简单的自定义验证器.除其他事项外,我想禁用提交按钮,不仅在 word 表单无效时,而且在其任何感觉无效时.为此,我在正在编辑的模型中添加了一个 isValid
属性,并添加了代码来观察感知表单的变化:每当发生变化时,我都会检查表单的 valid
属性并相应地设置模型的属性.然后,我可以轻松地在视图和代码中的父组件级别添加检查,以便只有在两个表单都正常时才能发布.
为了支持自定义验证和附加逻辑,我将初始代码从基于模板的表单切换为基于模型的表单;然而,一旦我启动重构的代码,我就会收到几个未找到指令注释的错误,我不确定它们的含义.
可能我遗漏了一些明显的东西,但我是这里的新手.谁能给个建议?你可以在这个 plunker 上找到一个重现:http://plnkr.co/edit/v9Dj5j5opJmonxEeotcR.这是它的一些基本代码:
a) 父组件:
@Component({选择器:字",指令:[FORM_DIRECTIVES,FORM_PROVIDERS,WordSense],模板网址:`<div><form [ngFormModel]="wordForm"(ngSubmit)="onSubmit(wordForm.value)"角色=形式"><div class="form-group"[class.has-error]="!lemma.valid && lemma.touched"><label for="引理">引理</label><输入类型="文本" id="引理"maxlength="100" 需要拼写检查="false"类=形式控制"占位符=引理"[ngFormControl]="wordForm.controls['引理']"><div *ngIf="lemma.hasError('required') && lemma.touched"class="text-danger small">需要引理</div><div *ngIf="lemma.hasError('lemmaValidator') && lemma.touched"class="text-danger small">无效引理</div>
...<div class="form-group"><table class="table table-bordered">
<tr *ngFor="#s of senses"><td><word-sense [sense]="s" [ranks]="ranks" [fields]="fields"></word-sense></td></tr></tbody>...<按钮类型=提交"[ngClass]="{禁用:!wordForm.valid}"class="btn btn-primary btn-sm">保存</button></表单>
`,输入:[单词"]})导出类词{私人 _word:IWordModel;公共设置字(值:IWordModel){this._word = 值;this.setFormValues();}公共获取单词(){返回 this._word;}//...//形式公共词表:ControlGroup;公共引理:控制;公共语言:控制;公共类:控制;公共等级:IPair b) 子组件: <div class="form-group"[class.has-error]="!yearCtl.valid"><输入类型=数字"类=形式控制"占位符=日期"[ngFormControl]="yearCtl"[(ngModel)]="sense.year"> ...</表单>`,输入:[感觉","排名",领域"]})导出类 WordSense {//正在编辑的模型公众意识:ISenseViewModel;//查找数据公共等级:IPair 为了有更多可读性错误,您可以将 Angular2 这样做,你现在有以下错误: 未在 FormBuilder 上找到指令注释 实际上,问题在于您将 删除它应该可以解决您的问题: 另一个问题是您使用 你应该改用这个: 这是重构后的 plunkr:http://plnkr.co/edit/x0d5oiW1J9C2JrJG8NdT?p预览. 希望对你有帮助蒂埃里 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 Both the components use model-driven forms, and the child form binds its model's values to form controls using 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 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: b) the child component:
To have more readable errors, you can change Angular2 Doing this, you have now the following error: No Directive annotation found on FormBuilder In fact, the problem is that you set the Removing it should fix your problem: Another problem is that you use You should use this instead: Here is the refactored plunkr: http://plnkr.co/edit/x0d5oiW1J9C2JrJG8NdT?p=preview. Hope it helps you,
Thierry 这篇关于Angular2 beta:嵌套基于表单的父/子组件并从父组件验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!@Component({选择器:词义",指令:[FORM_DIRECIVES],模板:`<form class="form-inline" role="form" [ngFormModel]="senseForm"><div class="form-group"[class.has-error]="!definitionCtl.valid"><输入类型="文本"类=形式控制"占位符=定义"[ngFormControl]="definitionCtl"[(ngModel)]="sense.definition">
.min.js
文件更改为 .dev.js
代码> 那些.FORM_PROVIDERS
设置为组件的 directives
属性.所以它尝试使用提供者作为指令,但它们不是...@Component({选择器:字",指令:[FORM_DIRECTIVES, FORM_PROVIDERS, WordSense], <-----模板网址:`<div>(……)`})出口类...
@Component({选择器:字",指令:[FORM_DIRECTIVES, WordSense], <-----模板网址:`<div>(……)`})出口类...
templateUrl
而不是 template
作为您的 Word
组件:@Component({选择器:字",指令:[FORM_DIRECTIVES,WordSense],模板网址:` <-----------`(……)
@Component({选择器:字",指令:[FORM_DIRECTIVES,WordSense],模板:` <---------`(……)
Word
, which represents the word in a fake dictionary. The children components are WordSense
's, each representing a sense of the parent word.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.isValid
property to the model being edited, and code to observe changes in the sense form: whenever a change occurs, I check the form's valid
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.@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"];
// ...
}
}
@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"];
}
// ...
}
.min.js
files to the .dev.js
ones.
FORM_PROVIDERS
into the directives
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 ...
@Component({
selector: "word",
directives: [FORM_DIRECTIVES, WordSense], <-----
templateUrl: `
<div>
(...)
`
})
export class ...
templateUrl
instead of template
for your Word
component:@Component({
selector: "word",
directives: [FORM_DIRECTIVES,WordSense],
templateUrl: ` <----------
`
(...)
@Component({
selector: "word",
directives: [FORM_DIRECTIVES,WordSense],
template: ` <----------
`
(...)
登录
关闭