简体   繁体   中英

Acces @ViewChild NgForm refence from child components in parent component

I have following app structure:

+--------------------------------------------+                                                                         
|                 Parent                     |                                                                         
|+------------------------------------------+|                                                                         
||                Header                    ||                                                                         
||  <form #headerForm="ngForm">             ||                                                                         
||       ...                                ||                                                                         
||  </form>                                 ||                                                                         
|+------------------------------------------+|                                                                         
|+------------------------------------------+|                                                                         
||                Content                   ||                                                                         
||  <form #contentForm="ngForm">            ||                                                                         
||      ...                                 ||                                                                         
||                                          ||                                                                         
||  </form>                                 ||                                                                         
||                                          ||                                                                         
|+------------------------------------------+|                                                                         
+--------------------------------------------+                                                                         
                                             

I would like to reset the states of the forms in the header and content from the Parent component. I tried to use @ViewChild/@ViewChildren from the parent component, but I do not know how to access the childs form refs.

So far I was forced to get a reference of the Header and Content components in the Parent and call a resetForm method:

export class ParentComponent {
  @ViewChildren('componentWithForm') componentsWithForms: QueryList<unknown>;

  ...

  this.componentsWithForms.forEach(component => (component as any).resetFormState());




@Component({
  selector: 'app-header',
   ...
  providers: [{ provide: 'componentWithForm', useExisting: forwardRef(() => HeaderComponent) }],
})
export class HeaderComponent {

  @ViewChild('headerForm') headerForm: NgForm;

  ...

  resetFormState() {
     this.headerForm.reset();
   }

Although this technically helps it leads to some ugly TypeScript casting - which I could work around creating a base-class ComponentWithForm , inheriting the Header/Content from it and setting the provider like

@Component({
  selector: 'app-header',
   ...
  providers: [{ provide: ComponentWithForm, useExisting: forwardRef(() => HeaderComponent) }],
})
export class HeaderComponent extends ComponentWithForm {

but I would actually like to get a direct reference to headerForm/contentForm which would save lot of hassle.

I know that it's not a great change, but If each component with a form you have

 providers:[{provide:'FORM', useExisting:forwardRef(() => YourComponent)}]

and

 @ViewChild(NgForm) form

A directive like

@Directive({
  selector: '[haveform]'
})
export class HaveFormDirective implements AfterViewInit {
 form:NgForm
 constructor(@Host() @Inject('FORM') public component:any, ){  }
 ngAfterViewInit()
 {
  this.form=this.component.form
 }
}

Allow you write

<component haveform></component>

And you can reach the "form" like

 @ViewChildren(HaveFormDirective) forms:QueryList<HaveFormDirective>
 this.forms.forEach(x=>console.log(x.form.value)

a stackblitz

It think you could use a better solution like @Input()/@Output() for parent <-> child component communication. But if u want to use @ViewChild , It will be best to start at official docs:

https://angular.io/guide/lifecycle-hooks#responding-to-view-changes

export class AfterViewComponent implements  AfterViewChecked, AfterViewInit {
  private prevHero = '';

  // Query for a VIEW child of type `ChildViewComponent`
  @ViewChild(ChildViewComponent) viewChild!: ChildViewComponent;

  ngAfterViewInit() {
    // viewChild is set after the view has been initialized
    this.logIt('AfterViewInit');
    this.doSomething();
  }

// ...
}

EDITED:

You can make a base class for all your components with form that contains resetFormState() {} method implementation:

@Directive()
export class YourFormDirective {
     resetFormState() {  //... }
}

export class HeaderComponent extends YourFormDirective {}

export class ContentComponent extends YourFormDirective {}

And then in parent, query for WithFormDirective :

@ViewChildren(WithFormDirective) componentsWithForms!: QueryList<WithFormDirective>;

So you can call it without type casting:

this.componentsWithForms.forEach(component => component.resetFormState());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM