在Angular 7中处理大型反应形式

Max*_*icu 5 javascript typescript angular-material angular

我正在尝试找到一种更好的方法来处理复杂的角度形式。表格真的很大,我需要找到一种降低复杂性的方法。

这是表单结构的示例:

{
    "fieldA" : ...,
    "fieldB" : ...,
    "fieldC" : ...,
    "profile": {
        "username": ...,
        "email": ...,
        "firstName": ...,
        "lastName": ...,
        ...
    },
    "settings": {
        "enableEmailNotification": ...,
        "notificationsEmail": ..., // required when enableEmailNotification
        ...
    },
    ...
}
Run Code Online (Sandbox Code Playgroud)

在某些情况下,验证器会随时更改,例如,当时enableEmailNotification=true,组件会将Required验证器添加到notificationsEmail

以下是研究的选项:

选项#0-经典

github上的样本

这种方法使用一种形式和一个组件。

优点:

  • 很多代码,但是非常简单

缺点:

  • 所有逻辑都放在一个地方。就我而言,此组件变得太大且难以阅读或维护
  • 用户界面也变得足够大

选项#1-将FormGroup传递给子组件

github上的样本

这种方法将formGroup其作为@Input()属性发送到内部组件。

优点:

  • 缩小部分视图

缺点:

  • 表单创建和验证规则仍在父组件上
  • 仅缩小视图
  • 验证逻辑在根组件中创建,但在子组件中显示错误

选项#2-创建自定义ControlValueAccessor

github上的样本

基于本文,我们可以创建自定义ControlValueAccessor,它将返回表单一部分的对象。

优点:

  • 将表格拆分为多种形式。形式可以分成较小的独立部分。

缺点:

  • 为表单值保留JS对象。看起来不太好

bry*_*n60 5

我对于大型复杂表单的策略是拥有一个包装组件和子组件。每个子组件都有自己的表单服务,包装器有一个主表单服务,其中注入了子表单服务,请考虑这一点

@Component({
  selector: 'form-wrapper',
  template: `
    <form [formGroup]="form" (submit)="save()">
      <sub-form-a></sub-form-a>
      <sub-form-b></sub-form-b>
      <input type="submit" value="Submit Form">
    </form>
  `,
  providers: [MasterFormService, FormAService, FormBService]
})
export class FormWrapper {
  constructor(private formService: MasterFormService) { }
  save() {
    // whatever save actions here
  }
}

@Component({ // form b compoent is basically the same
  selector: 'sub-form-a',
  template: `
    ... whatever template belongs to form a ..
  `
})
export class FormAComponent {
  form: FormGroup
  constructor(private formService: FormAService) {
    this.form = this.formService.form;
    // form a specific actions
  }
}

@Injectable()
export class MasterFormService {
  form: FormGroup;
  constructor(private fb: FormBuilder, formAService: FormAService, formBService: FormBService) {
    this.form = this.fb.group({
      groupA: this.formAService.form,
      groupB: this.formBService.form,
    });
  }
}

@Injectable() // formB service is basically the same
export class FormAService {
  form: FormGroup;
  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      .. whatever fields belong to form a ..
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

此方法创建高度可重用的子表单,并允许您模块化/隔离表单逻辑和模板。我经常发现子表单通常属于多个地方,因此它使我的代码非常干燥。特别是在您的示例中,您可以轻松地在应用程序的其他位置重用设置表单和配置文件表单组件。一两次我什至再次嵌套这个结构以形成极其复杂的形式。

缺点是结构可能看起来很复杂,但你很快就会习惯它。


Jef*_*aal 4

就我个人而言,对于大型复杂表单,我希望将表单逻辑保留在一个组件中,因此很容易链接可观察量,但使用几个服务作为帮助程序。既用于初始化表单,又用于处理操作(返回新表单值、验证、授权等)

  • 逻辑/跟踪位于FormComponent
  • 表单的初始化位于FormInitService
  • 操作在一个 pr 多个中处理FormActionService(s)

表单组件

export class FormComponent implements OnInit {
  constructor(
    private formActionService: FormActionService,
    private formInitService: FormInitService
  ) { }

  ngOnInit() {
    this.form = this.FormInitService.getForm();
    this._trackFieldA();
    this._trackProfile();
  }

  // Track a single field
  private _trackFieldA() {
    this.form.controls.fieldA.valueChanges.pipe(
      distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))
    ).subscribe(fieldA => {
      console.log('Field A Changed');
      this.formActionService.doSomething();
    });
  }

  // Track a group
  // Use ['controls'] for nested controls to skip typechecking errors
  private _trackProfile() {
    combineLatest(
      this.form.controls.profile['controls'].username.valueChanges,
      this.form.controls.profile['controls'].email.valueChanges,
    ).pipe(
      distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))
    ).subscribe(profile => {
      console.log('Some profile field changed');
      this.formActionService.doSomething();
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

表单初始化服务

export class FormInitService {
  constructor(
    private formBuilder: FormBuilder
  ) { }

  public getForm(): FormGroup {
    return this.formBuilder.group({
      fieldA: 'Some init value',
      fieldB: 'Some init value',
      profile: this.formBuilder.group({
        username: 'Some init value',
        email: 'Some init value',
        ...
      }),
      ...
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

表单操作服务

export class FormActionService {
  public doSomething(): any | void {
    console.log('Something')
  }
}
Run Code Online (Sandbox Code Playgroud)

FormComponent 和模板中仍然有相当多的代码,但它确实很容易阅读和维护。拆分为多个组件通常会变得非常混乱,特别是在团队中工作时,或者需要进行一些(巨大的)重构时。