Angular 表单设计,我应该使用多个表单还是使用一个表单更好?

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

我要实现一个配置设置模式,UI看起来像附图

侧边栏上的多个标签,每个标签的内容将是右侧面板中的一个表格。

API 将在模式打开时获取所有设置配置。

没有API获取每个单独标签的数据,所有数据将在按下模态的'Save'按钮后保存。

API数据响应:

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

问题:

这个场景的数据管理应该怎么设计?

俗话说“千刀万剐”,这里同样适用。 Angular 给你工具,把架构留给你。

边栏上的多个选项卡意味着您可能会使用 master-detail 布局。

您可以使用多个表单,正如您已经注意到的那样,您必须同步它。

但是,我建议使用一种表单,它会覆盖整个细节布局,然后您将表单实例传递给细节组件。

所以想法是您将在父组件中有一个 FormGroupSave 按钮也是如此。你基本上用空 FormGroup.

初始化它
form = fb.group({});

然后您将其作为 @Input 参数传递给详细视图组件,然后在 ngOnInit() 上添加 FormControl

@Input() form: FormGroup;
ngOnInit() {
    this.form.addControl('enableLogging', new FormControl(this.settingsService.config.enableLogging));
    // other form controls
}

settingsService.config 是您的 source-of-truth,即从您的 api 响应返回的配置。

单一表格会给您带来两个优势:

  1. 您不必费心同步每个表单的数据并进行整合。
  2. 因为你有一个保存按钮,它会保存所有数据,而不是在每个选项卡上保存,在父组件中有一个单一的表单,意味着你可以简单地 post 它与 this.form.value 点击保存按钮。

如果 是您的“parent” 创建整个表单的人

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),
   })
   ...
})

您可以将 formGroup 传递给您的“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>

你children喜欢

@Input() group;

<form [formGroup]="group">
   <input formControlName="prop1">
   <input formControlName="prop2">
</form>

您不会丢失表单的值,因为它始终位于“parent”

更新。使用服务补充我的评论

假设您有一项服务

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...
  }
}

我们的children就像

    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;
      }
    
    }

我们parent喜欢

<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>

看到“引用变量”,我们使用ViewChild获取组件

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

您可以在an ugly stackblitz

中看到