Creating Composite Components in Angular

2020-03-27 17:17发布

问题:

I want to create a Angular generic composite component which is composed of bunch of input fields which could be used inside multiple components across application.

For Example: Consider a USER DETAIL component which has following Fields,

  • Email - InputText Field
  • Gender-Radio button.
  • Age Group - A Dropdown
  • Description - A Text Area.

I want to include this USER DETAIL component in multiple components as below:

From Conventional wisdom I can say, that I just include <app-userdetails> in multiple components.

But how Do I Handle the submitted FormGroupData? I mean how do I get the data from UserDetail when its included in another component like CreateEmployee?

I am not sure if I explained the above scenario properly, please point me to any web-examples or existing questions about this.

回答1:

So, to your child components you can pass form and add/remove controls to him.

Parent component template

<form (ngSubmit)="onSubmit(form)" #form="ngForm">
        <a-comp [prop1]="name" [form]=form></a-comp>

      <button type="submit">Submit</button>
 </form>

Some child component:

// a comp
@Component({
  selector: 'a-comp',
  template: `
    <input [(ngModel)]="prop1"  name="name2" #myControl="ngModel"/>
    `

})
export class AComponent {
  @Input() form: NgForm;
  @Input() prop1 = 'Angular 5';

  @ViewChild('myControl') myControl: NgModel;

  ngOnInit() {
    //console.log(this.form, this.myControl);
    this.form.addControl(this.myControl);


  }
}

CODE EXAMPLE

Passing @Input to childs isn't necessary. But child components need to know about form.

2. Easiest way. By using power of DI

Child-component. In your case UserDetails:

@Component({
  selector: 'a-comp',
  viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
  template: `
      <input [(ngModel)]="prop1"  name="prop1"/>
      <input [(ngModel)]="prop2"  name="prop2"/>
    `

})
export class AComponent {
  prop1 = 'Hello';
  prop2 = 'World';

  ngOnInit() { }
}

As you can see in @Component decorators we set viewProviders. So when requiring ControlContainer we say to useExisting: NgForm. By Angular DI system it will go up to find first parent NgForm.

ViewProviders = Defines the set of injectable objects that are visible to its view DOM children.

...

You maybe can ask: By DI mechanism it should search provider up to root, but why it doesn't go up in hirearchy and find parent NgForm?

Constuctor of ngModel:

 constructor(@Optional() @Host() parent: ControlContainer,
              @Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
              @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<AsyncValidator|AsyncValidatorFn>,
              @Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
              valueAccessors: ControlValueAccessor[]) {
...

Pay attention to @Optional() @Host() parent: ControlContainer. It's @Optional and @Host().

@Host - Specifies that an injector should retrieve a dependency from any injector until reaching the host element of the current component.

So, here @Host() decorators restrics the ngModel to search only up to host component where ngModel is allocated.

NgModel constructor

By this approach no need create @Input binding in child components to get parent NgForm and add NgModels controls manually.

StackBlitz EXAMPLE



标签: angular