Search code examples
angularngrxangular-forms

Angular form design, should i use multiple forms or it's better to use a single form?


I am going to implement a modal of configuration settings, the UI looks like the attached imageenter image description here

Multiple tabs on the sidebar, the content of each tab will be a form in the right panel.

API will fetch all the settings config once when the modal is open.

There's no API for fetching data of each individual tab, and all data will be saved after pressing the 'Save' button of the modal.

API data response:

config: {
    network: {...},
    proxy: {...}
    ...
    download: {...}
}

Question:

How should I design the data management of this scenario?

  • formValues.subscribe on each detail form, and emit the event to parent modal component for syncing data?
  • Should I consider using NgRx? I don't really have experience with it. Is it a good scenario I should use NgRx?

Solution

  • if is your "parent" who create the whole form

    form=new FormGroup({
       network:new FormGroup({
         prop1:new FormControl(settingsService.network.prop1),
         prop2:new FormControl(settingsService.network.prop2),
       }),
       proxy:new FormGroup({
         prop1:new FormControl(settingsService.proxy.prop1),
         prop2:new FormControl(settingsService.proxy.prop2),
       })
       ...
    })
    

    You can pass the formGroup to your "children"

    <form [formGroup]="form">
       <config-component *ngIf="step==0" [group]="form.get('config')"></config-component>
       <proxy-component *ngIf="step==1" [group]="form.get('proxi')"></proxi-component>
        ...
    </form>
    

    Your childrens like

    @Input() group;
    
    <form [formGroup]="group">
       <input formControlName="prop1">
       <input formControlName="prop2">
    </form>
    

    You don't loose the values of the form because you has it always in "parent"

    Update. Complementary my comments using a service

    Imagine you has a service

    import { Injectable } from '@angular/core';
    
    @Injectable({
      providedIn: 'root',
    })
    export class DataService {
    
      data:any={}
      constructor() { }
    
      getData():Observable<any>{
         ...we get the data from a http or localStore or...
         ...and use pipe(tap) to store the value in "data"
         e.g.
         return of({}).pipe(tap((res)=>this.data=res));
      }
      saveData(){
         ..we use this.data to post to an http or to save in localStore...
      }
    }
    

    Ours children are like

        export class OneComponent implements OnInit  {
          form:FormGroup  
        
          constructor(private dataService:DataService){}
          ngOnInit()
          {
             //see that create the form using the data from this.dataService.data.config
             //in another component you'll use, e.g. this.dataService.data.netWork
    
            const data=this.dataService.data.config|| {prop1:null,prop2:null}
            this.form=new FormGroup(
              {
                prop1:new FormControl(data.prop1,Validators.required),
                prop2:new FormControl(data.prop2)
              })
          }
          saveData() //simple store in this.dataService.datadata.config the value of the form
                     //in another component you store, e.g. in this.dataService.datadata.netWork
          {
            this.dataService.data.config=this.form.value;
          }
        
        }
    

    Our parent like

    <button (click)="navigate(0)">Config</button>
    <button (click)="navigate(1)">NetWork</button>
    <one-component #component *ngIf="page==0">
    </one-component>
    <two-component #component *ngIf="page==1">
    </two-component>
    

    See the "reference variable", we use ViewChild to get the component

    export class AppComponent implements OnInit  {
      @ViewChild('component') component:any;
    
      constructor(public dataService:DataService){}
      page=0;
      ngOnInit()
      {
        this.dataService.getData().subscribe()
      }
      isValid(){
        const form=this.component.form as FormGroup
        if (form.valid)
             this.component.saveData()
        else
          form.markAllAsTouched();
    
        return form.valid
    
      }
      navigate(newIndex:number)
      {
        if (this.isValid())
            this.page=newIndex
      }
      saveData()
      {
         if (this.isValid())
             this.dataService.saveData()
      }
    }
    

    You can see in an ugly stackblitz