Angular2 beta:嵌套基于表单的父/子组件并从父组件验证 [英] Angular2 beta: nesting form-based parent/child components and validating from parent

查看:28
本文介绍了Angular2 beta:嵌套基于表单的父/子组件并从父组件验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 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[];公众感觉:ISenseViewModel[];公共字段:IFieldModel[];构造函数(私有表单构建器:FormBuilder){//...this.senses = [this.createSense()];//...//构建表单this.wordForm = this.formBuilder.group({"引理": ["", Validators.compose([Validators.required, LemmaValidator.isValidLemma])],"语言": ["eng", Validators.required],"class": ["s.", Validators.required],});this.lemma = <控制>this.wordForm.controls["引理"];this.language = <控制>this.wordForm.controls["语言"];this.class = <控制>this.wordForm.controls["class"];//...}}

b) 子组件:

@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">

<div class="form-group"[class.has-error]="!yearCtl.valid"><输入类型=数字"类=形式控制"占位符=日期"[ngFormControl]="yearCtl"[(ngModel)]="sense.year">

...</表单>`,输入:[感觉","排名",领域"]})导出类 WordSense {//正在编辑的模型公众意识:ISenseViewModel;//查找数据公共等级:IPair[];公共字段:IFieldModel[];公共字段:IFieldModel;//形式公众意识形式:ControlGroup;公共定义Ctl:控制;public yearCtl:控制;public rankCtl:控制;public fieldsCtl:控制;构造函数(私有表单构建器:FormBuilder){this.senseForm = this.formBuilder.group({"定义": ["", Validators.required],"年": [0, Validators.compose([Validators.required, YearValidator.isValidYear])],"rank": [{value: 2, label: "media"}, Validators.required],字段":["]});this.definitionCtl = <Control>this.senseForm.controls["定义"];this.yearCtl = <Control>this.senseForm.controls["年"];this.rankCtl = <控制>this.senseForm.controls["rank"];this.fieldsCtl = <Control>this.senseForm.controls["字段"];}//...}

解决方案

为了有更多可读性错误,您可以将 Angular2 .min.js 文件更改为 .dev.js代码> 那些.

这样做,你现在有以下错误:

<块引用>

未在 FormBuilder 上找到指令注释

实际上,问题在于您将 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],模板:` <---------`(……)

这是重构后的 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 Word, which represents the word in a fake dictionary. The children components are WordSense'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'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.

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 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 ...

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 of template for your Word component:

@Component({
  selector: "word",
  directives: [FORM_DIRECTIVES,WordSense],
  templateUrl: ` <----------
  `
  (...)

You should use this instead:

@Component({
  selector: "word",
  directives: [FORM_DIRECTIVES,WordSense],
  template: ` <----------
  `
  (...)

Here is the refactored plunkr: http://plnkr.co/edit/x0d5oiW1J9C2JrJG8NdT?p=preview.

Hope it helps you, Thierry

这篇关于Angular2 beta:嵌套基于表单的父/子组件并从父组件验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆