首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对子组件进行角度形式验证

对子组件进行角度形式验证
EN

Stack Overflow用户
提问于 2017-07-25 21:28:12
回答 3查看 18.3K关注 0票数 14

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

表单的HTML如下所示:

代码语言:javascript
复制
<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>

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

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

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-08-04 22:05:17

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

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

代码语言:javascript
复制
@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() { }

}

父html如下所示:

代码语言:javascript
复制
<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>

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

代码语言:javascript
复制
saveChanges() {

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

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

Stack Overflow用户

发布于 2019-09-10 02:28:40

对于未来的谷歌人来说,

我遇到了类似的问题,尽管子组件较少,并且在深入研究@penleychan关于这个主题的前述帖子后,我发现了一个小gem,它为我解决了这个问题,而不需要实现自定义指令。

代码语言:javascript
复制
import { ControlContainer, NgForm } from '@angular/forms';

@Component({
    ....
    viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
})

这适用于我的嵌套表单。只需要添加到组件中,这些组件直接包含输入

https://github.com/angular/angular/issues/9600#issuecomment-522898551

票数 26
EN

Stack Overflow用户

发布于 2017-08-01 22:23:14

希望我还来得及。我最近在这个问题上也遇到了模板方法,因为反应式表单不适合我需要做的事情。

这个问题与您的组件需要实现的ControlValueAccessor有关。然而,我不能让它工作。

请参阅:https://github.com/angular/angular/issues/9600

andreev-artem提供的解决方案运行良好,我还添加了我的解决方案,将其包装在ngModelGroup中,而不是form的根对象controls属性中。

对于您的情况,您没有使用ngModelGroup,您可以只使用以下指令

代码语言:javascript
复制
@Directive({
selector: '[provide-parent-form]',
providers: [
    {
        provide: ControlContainer,
        useFactory: function (form: NgForm) {
            return form;
        },
        deps: [NgForm]
    }
  ]
})
export class ProvideParentForm {}

用法:在您的组件中的根元素处,在拥有[(ngModel)]之前添加指令。示例:

代码语言:javascript
复制
<div provide-parent-form> 
   <input name="myInput" [(ngModel)]="myInput"> 
</div>

现在,如果您在控制台或其他地方输出form object,您可以在form对象的controls属性下看到组件的控件。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45304810

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档