子组件上的 Angular Form 验证 [英] Angular Form validation on child components

查看:25
本文介绍了子组件上的 Angular Form 验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了一个动态表单,其中有一个主要部分和基于在主要部分 (widget.type) 中选择的类型的子部分.显示和隐藏子部分是通过 ngSwitch 完成的.

表单的 HTML 如下所示:

<div class="forms-group"><label for="title" i18n="@@title">Titel</label><input class="form-control" id="title" name="title" type="text" [(ngModel)]="widget.title" required/>

<div class="forms-group"><label class="checkbox-label" for="show" i18n><input id="show" name="show" type="checkbox" [(ngModel)]="widget.show"/><span>应用中的标题音</span>

<div class="forms-group"><label for="type" i18n="@@type">Type</label><select class="form-control" id="type" name="type" [(ngModel)]="widget.type" required><option value="text-widget" i18n="@@Text">Tekst</option><option value="tasklist-widget" i18n="@@Tasklists">Takenlijst</option><option value="image-widget" i18n="@@Text">Afbeelding(en)</option><option value="video-widget" i18n="@@Video">Youtube</option><option value="link-widget" i18n="@@Link">Link</option><option value="contacts-widget" i18n="@@Contacts">Contactpersonen</option><option value="attachment-widget" i18n="@@Attachments">Bijlage(n)</option></选择>

<ng-container [ngSwitch]="widget.type"><text-widget *ngSwitchCase="'text-widget'" [data]="widget"></text-widget><tasklist-widget *ngSwitchCase="'tasklist-widget'" [data]="widget"></tasklist-widget><image-widget *ngSwitchCase="'image-widget'" [data]="widget"></image-widget><video-widget *ngSwitchCase="'video-widget'" [data]="widget"></video-widget><link-widget *ngSwitchCase="'link-widget'" [data]="widget"></link-widget><contacts-widget *ngSwitchCase="'contacts-widget'" [data]="widget"></contacts-widget><attachment-widget *ngSwitchCase="'attachment-widget'" [data]="widget"></attachment-widget></ng-容器></表单>

每个小部件都是它自己的组件.

问题是表单验证只检查主要部分的输入,而忽略子部分(小部件组件).如何确保来自小部件的输入字段包含在验证中?

我尝试向小部件组件添加一个 isValid() 方法,但我无法获取组件的实例,可能是因为它们在 ngSwitch 中使用.@ContentChild、@ContentChildren、@ViewChild 等都返回未定义.

解决方案

决定在子组件上有一个 isValid 方法,用于指示小部件是否正确填写.只有当表单和小部件组件都有效时才能保存表单.

所有小部件组件都实现了 IWidgetComponent 接口,该接口需要更改的 EventEmitter 属性和 isValid 方法.其中一个子小部件组件如下所示.

@Component({选择器:'视频小部件',templateUrl: './video.component.html',styleUrls: ['./video.component.css'],提供者:[YouTubeIdExistsValidator]})导出类 VideoComponent 实现 OnInit、OnDestroy、IWidgetComponent {@输入数据")小部件:IWidget;@输出(改变")更改 = 新 EventEmitter();视频:任何;模型更改:主题<字符串>= 新主题<字符串>();公共 isValid(): 布尔 {返回 this.widget.youtube_id &&this.widget.youtube_id !== "" &&这个视频 ?真假;}构造函数(私有 youtubeService:YoutubeService){this.modelChanged.debounceTime(500)//在发出最后一个事件之前,在最后一个事件之后等待 500 毫秒.distinctUntilChanged()//仅当值与前一个值不同时才发出.subscribe(youtube_id => this.getYoutubeVideo(youtube_id));}ngOnDestroy(): 无效 {this.widget.youtube_id = "";}getYoutubeVideo(youtube_id: string) {this.youtubeService.getById(youtube_id).subscribe((视频) => {this.video = 视频;//表示视频已更改this.changed.emit();}, (错误) =>{this.video = null;});}youtubeIdChanged(youtube_id:字符串){this.modelChanged.next(youtube_id);}ngOnInit() { }}

父 html 如下所示:

...<ng-container [ngSwitch]="widget.type"><text-widget #ref *ngSwitchCase="'text-widget'" [data]="widget" (change)="saveChanges()"></text-widget><tasklist-widget #ref *ngSwitchCase="'tasklist-widget'" [data]="widget" (change)="saveChanges()"></tasklist-widget><image-widget #ref *ngSwitchCase="'image-widget'" [data]="widget" (change)="saveChanges()"></image-widget><video-widget #ref *ngSwitchCase="'video-widget'" [data]="widget" (change)="saveChanges()"></video-widget><link-widget #ref *ngSwitchCase="'link-widget'" [data]="widget" (change)="saveChanges()"></link-widget><contacts-widget #ref *ngSwitchCase="'contacts-widget'" [data]="widget" (change)="saveChanges()"></contacts-widget><attachment-widget #ref *ngSwitchCase="'attachment-widget'" [data]="widget" (change)="saveChanges()"></attachment-widget></ng-容器>...</表单>

每次小部件更改时都会发出一个事件(this.changed.emit()),该事件会触发父组件中的保存表单方法.在这种方法中,我检查表单和小部件是否有效,如果有效,则可以保存数据.

saveChanges() {如果 (this.ref && this.ref.isValid() && this.widgetForm.valid) {//保存表格this.toastr.success("保存了!");}别的 {this.toastr.warning("表格未保存!");}}

I've written a dynamic form in which there is a main part and sub parts based on a type that's selected in the main part (widget.type). Showing and hiding the sub parts is done with an ngSwitch.

HTML of the form looks like this:

<form class="widget-form cc-form" (ngSubmit)="saveChanges()" novalidate>
  <div class="forms-group">
    <label for="title" i18n="@@title">Titel</label>
    <input class="form-control" id="title" name="title" type="text" [(ngModel)]="widget.title" required />
  </div>

  <div class="forms-group">
    <label class="checkbox-label" for="show" i18n>
      <input id="show" name="show" type="checkbox" [(ngModel)]="widget.show" /> <span>Titel tonen in app</span>
    </label>
  </div>

  <div class="forms-group">
    <label for="type" i18n="@@type">Type</label>
    <select class="form-control" id="type" name="type" [(ngModel)]="widget.type" required>
      <option value="text-widget" i18n="@@Text">Tekst</option>
      <option value="tasklist-widget" i18n="@@Tasklists">Takenlijst</option>      
      <option value="image-widget" i18n="@@Text">Afbeelding(en)</option>
      <option value="video-widget" i18n="@@Video">Youtube</option>
      <option value="link-widget" i18n="@@Link">Link</option>
      <option value="contacts-widget" i18n="@@Contacts">Contactpersonen</option>
      <option value="attachment-widget" i18n="@@Attachments">Bijlage(n)</option>
    </select>
  </div>

  <ng-container [ngSwitch]="widget.type">

    <text-widget *ngSwitchCase="'text-widget'" [data]="widget"></text-widget>

    <tasklist-widget *ngSwitchCase="'tasklist-widget'" [data]="widget"></tasklist-widget>

    <image-widget *ngSwitchCase="'image-widget'" [data]="widget"></image-widget>

    <video-widget *ngSwitchCase="'video-widget'" [data]="widget"></video-widget>

    <link-widget *ngSwitchCase="'link-widget'" [data]="widget"></link-widget>

    <contacts-widget *ngSwitchCase="'contacts-widget'" [data]="widget"></contacts-widget>

    <attachment-widget *ngSwitchCase="'attachment-widget'" [data]="widget"></attachment-widget>

  </ng-container>

</form>

Every widget is it's own component.

The problem is that the form validation only checks the inputs from the main part and disregards the sub part (widget components). How can I make sure the input fields from the widgets are included in the validation?

I tried adding an isValid() method to the widget components but I couldn't get the instances of the components, probably because they are used in an ngSwitch. @ContentChild, @ContentChildren, @ViewChild etc. all returned undefined.

解决方案

Decided to have an isValid method on the child component which indicates if the widget is filled out correctly. The form can only be saved when the form and widget component are both valid.

All widget components implement an IWidgetComponent interface which requires a changed EventEmitter property and an isValid method. One of the child widget components looks like this.

@Component({
  selector: 'video-widget',
  templateUrl: './video.component.html',
  styleUrls: ['./video.component.css'],
  providers: [YouTubeIdExistsValidator]
})
export class VideoComponent implements OnInit, OnDestroy, IWidgetComponent {

  @Input("data")
  widget: IWidget;

  @Output("change")
  changed = new EventEmitter<any>();

  video: any;
  modelChanged: Subject<string> = new Subject<string>();

  public isValid(): boolean {
    return this.widget.youtube_id && this.widget.youtube_id !== "" && this.video ? true : false;
  }

  constructor(private youtubeService: YoutubeService) {
    this.modelChanged
      .debounceTime(500) // wait 500ms after the last event before emitting last event
      .distinctUntilChanged() // only emit if value is different from previous value
      .subscribe(youtube_id => this.getYoutubeVideo(youtube_id));
  }

  ngOnDestroy(): void {
    this.widget.youtube_id = "";
  }

  getYoutubeVideo(youtube_id: string) {
    this.youtubeService
      .getById(youtube_id)
      .subscribe((video) => {
        this.video = video;

        // Indicate that video was changed
        this.changed.emit();
      }, (error) => {
        this.video = null;
      });
  }

  youtubeIdChanged(youtube_id: string) {
    this.modelChanged.next(youtube_id);
  }

  ngOnInit() { }

}

The parent html looks like this:

<form #widgetForm novalidate>

...

<ng-container [ngSwitch]="widget.type">

      <text-widget #ref *ngSwitchCase="'text-widget'" [data]="widget" (change)="saveChanges()"></text-widget>

      <tasklist-widget #ref *ngSwitchCase="'tasklist-widget'" [data]="widget" (change)="saveChanges()"></tasklist-widget>

      <image-widget #ref *ngSwitchCase="'image-widget'" [data]="widget" (change)="saveChanges()"></image-widget>

      <video-widget #ref *ngSwitchCase="'video-widget'" [data]="widget" (change)="saveChanges()"></video-widget>

      <link-widget #ref *ngSwitchCase="'link-widget'" [data]="widget" (change)="saveChanges()"></link-widget>

      <contacts-widget #ref *ngSwitchCase="'contacts-widget'" [data]="widget" (change)="saveChanges()"></contacts-widget>

      <attachment-widget #ref *ngSwitchCase="'attachment-widget'" [data]="widget" (change)="saveChanges()"></attachment-widget>

</ng-container>

...

</form>

Each time the widget changes an event is emitted (this.changed.emit()) which triggers the save form method in the parent component. In this method I check if the form and widget are valid, if it is then the data may be saved.

saveChanges() {

    if (this.ref && this.ref.isValid() && this.widgetForm.valid) {
      // save form

      this.toastr.success("Saved!");
    }
    else {
      this.toastr.warning("Form not saved!");
    }
  }

这篇关于子组件上的 Angular Form 验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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