Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
470 views
in Technique[技术] by (71.8m points)

angular - Create a reusable FormGroup

I am aware of creating custom controls as components, but I can't figure out how to create custom groups.

The same we can do this by implementing ControlValueAccessor and using a custom component like <my-cmp formControlName="foo"></my-cmp>, how can we achieve this effect for a group?

<my-cmp formGroupName="aGroup"></my-cmp>

Two very common use-cases would be (a) separting a long form into steps, each step in a separate component and (b) encapsulating a group of fields which appear across multiple forms, such as address (group of country, state, city, address, building number) or date of birth (year, month, date).


Example usage (not actual working code)

Parent has the following form built with FormBuilder:

// parent model
form = this.fb.group({
  username: '',
  fullName: '',
  password: '',
  address: this.fb.group({
    country: '',
    state: '',
    city: '',
    street: '',
    building: '',
  })
})

Parent template (inaccessible and non-semantic for brevity):

<!-- parent template -->
<form [groupName]="form">
  <input formControlName="username">
  <input formControlName="fullName">
  <input formControlName="password">
  <address-form-group formGroup="address"></address-form-group>
</form>

Now this AddressFormGroupComponent knows how to handle a group which has these specific controls inside of it.

<!-- child template -->
<input formControlName="country">
<input formControlName="state">
<input formControlName="city">
<input formControlName="street">
<input formControlName="building">
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The piece I was missing was mentioned in rusev's answer, and that is injecting the ControlContainer.

Turns out that if you place formGroupName on a component, and if that component injects ControlContainer, you get a reference to the container which contains that form. It's easy from here.

We create a sub-form component.

@Component({
  selector: 'sub-form',
  template: `
    <ng-container [formGroup]="controlContainer.control">
      <input type=text formControlName=foo>
      <input type=text formControlName=bar>
    </ng-container>
  `,
})
export class SubFormComponent {
  constructor(public controlContainer: ControlContainer) {
  }
}

Notice how we need a wrapper for the inputs. We don't want a form because this will already be inside a form. So we use an ng-container. This will be striped away from the final DOM so there's no unnecessary element.

Now we can just use this component.

@Component({
  selector: 'my-app',
  template: `
    <form [formGroup]=form>
      <sub-form formGroupName=group></sub-form>
      <input type=text formControlName=baz>
    </form>
  `,
})
export class AppComponent  {
  form = this.fb.group({
    group: this.fb.group({
      foo: 'foo',
      bar: 'bar',
    }),
    baz: 'baz',
  })

  constructor(private fb: FormBuilder) {}
}

You can see a live demo on StackBlitz.


This is an improvement over rusev's answer in a few aspects:

  • no custom groupName input; instead we use the formGroupName provided by Angular
  • no need for @SkipSelf decorator, since we're not injecting the parent control, but the one we need
  • no awkward group.control.get(groupName) which is going to the parent in order to grab itself.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...