nested-forms/apps/static-factory-methods/README.md

3.2 KiB

Parent Component Creates Form; Child Components Define Structure

Another approach for refactoring a component into child sub-components where the parent component is responsible for creating the entre Reactive Form would be to define static factory methods within each child component rather than within a full-fledged service. As with the Parent Component Creates Form and Passes Form Controls Into Child Components (Global Form) approach, the appropriate form controls would be passed into the children.

In many ways, this approach is a hybrid between the Parent Form and Global Form approaches.

export class AppComponent implements OnInit, OnDestroy {
  contact: Contact;
  form: FormGroup;

  private subscription: Subscription;

  constructor(private service: ContactService, private fb: FormBuilder) {}

  public ngOnInit() {
    this.subscription = this.service
      .loadContact()
      .subscribe((data: Contact) => {
        this.contact = data;
        this.form = this.fb.group({
          name: NameComponent.buildForm(data.name),
          addresses: AddressListComponent.buildForm(data.addresses),
        });
      });
  }
}

The HTML templating will be identical to the Global Form approach.

Static Form Builder Methods

Rather than having a separate factory service, this approach uses static methods on each of the child sub-classes. This approach intentionally couples the logic for creating a sub-form structure with the component that would display it, keeping the logic in one place rather than separating it between components and an otherwise unrelated service. The rule-of-thumb in this approach is that the component which needs to display the form to a user will best know what the structure of that form needs to be.

Name Component

  static buildForm(name: Name): FormGroup {
    return new FormGroup({
      firstName: new FormControl(name ? name.firstName : ''),
      lastName: new FormControl(name ? name.lastName : ''),
      middleName: new FormControl(name ? name.middleName : ''),
      prefix: new FormControl(name ? name.prefix : ''),
      suffix: new FormControl(name ? name.suffix : ''),
    });
  }

Address List Component

  static buildForm(addresses: Address[]): FormArray {
    const list: FormArray = new FormArray([]);

    if (addresses) {
      addresses.forEach(addr => {
        list.push(AddressComponent.buildForm(addr));
      });
    }

    return list;
  }

Address Component

  static buildForm(addr: Address): FormGroup {
    return new FormGroup({
      line1: new FormControl(addr ? addr.line1 : ''),
      line2: new FormControl(addr ? addr.line2 : ''),
      city: new FormControl(addr ? addr.city : ''),
      state: new FormControl(addr ? addr.state : ''),
      postalCode: new FormControl(addr ? addr.postalCode : ''),
    });
  }

Pros

  • The child components encapsulate the form controls and their display, while keeping the form creation logic separate from the actual template rendering
  • The child components can easily be re-used

Cons

  • The overall shape of the form from the parent component's perspective is not always clear