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

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

问题描述

我想在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:`
< D​​IV>
    <形成[ngFormModel] =wordForm
          (ngSubmit)=的onSubmit(wordForm.value)
          角色=形式>          < D​​IV CLASS =表单组
               [class.has错误] =lemma.valid&安培;!&安培; lemma.touched>
            <标签=引理>外稃< /标签>
            <输入类型=文本ID =引理
                   MAXLENGTH =100所需的拼写检查=假
                   类=表格控
                   占位=引理
                   [ngFormControl] =wordForm.controls [引理']>
             < D​​IV * ngIf =lemma.hasError(必要)及和放大器; lemma.touched
                  类=TEXT-危险小>要求外稃< / DIV>
             &所述;股利* ngIf =lemma.hasError('lemmaValidator')及与放大器; lemma.touched
                  类=TEXT-危险小>无效引理< / DIV>
          < / DIV>
            ...
          < D​​IV 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>    < D​​IV CLASS =表单组
         [class.has错误] =definitionCtl.valid!>
        <输入类型=文本
               类=表格控
               占位=定义
               [ngFormControl] =definitionCtl
               [(ngModel)] =sense.definition>
    < / DIV>    < D​​IV 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,YearVa​​lidator.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:`
    < D​​IV>
    (......)
  `
})
出口类...

删除它应该解决您的问题:

  @Component({
  选择:字,
  指令:[FORM_DIRECTIVES,WordSense]< -----
  templateUrl:`
    < D​​IV>
    (......)
  `
})
出口类...

另一个问题是,你使用 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 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],
  templateUrl: ` <----------
  `
  (...)

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

Hope it helps you, Thierry

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

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆