Home > other >  Acces @ViewChild NgForm refence from child components in parent component
Acces @ViewChild NgForm refence from child components in parent component

Time:02-02

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.

CodePudding user response:

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());

CodePudding user response:

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

  • Related