|
|
@@ -1 +1,83 @@ |
|
|
|
# Components Creating Own Form Controls |
|
|
|
|
|
|
|
When a component becomes sufficiently complex, or the developer wishes to be able to reuse parts of it elsewhere, the component can be refactored into sub-components. One approach to binding such sub-components to their containing parent component is to pass the parent component's `FormGroup` in as an `@Input` parameter. Additionally, the data that each child sub-component needs is passed into an `@Input`. The children create their own `FormControls` as needed, and add them to the parent `FormGroup` provided to them. This approach greatly simplifies the code and template of the parent component: |
|
|
|
|
|
|
|
```typescript |
|
|
|
export class AppComponent implements OnInit, OnDestroy { |
|
|
|
contact: Contact; |
|
|
|
form: FormGroup; |
|
|
|
|
|
|
|
private subscription: Subscription |
|
|
|
|
|
|
|
constructor(private fb: FormBuilder, private service: ContactService) { |
|
|
|
this.form = this.fb.group({}); |
|
|
|
} |
|
|
|
|
|
|
|
public ngOnInit() { |
|
|
|
this.subscription = this.service.loadContact().subscribe((data: Contact) => { |
|
|
|
this.contact = data; |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
```html |
|
|
|
<form [formGroup]="form"> |
|
|
|
<nested-forms-name |
|
|
|
[name]="contact.name" |
|
|
|
[parent]="form" |
|
|
|
></nested-forms-name> |
|
|
|
<nested-forms-address-list |
|
|
|
[addresses]="contact.addresses" |
|
|
|
[parent]="form" |
|
|
|
></nested-forms-address-list> |
|
|
|
</form> |
|
|
|
``` |
|
|
|
|
|
|
|
The `nested-forms-name` component is responsible for creating the form controls binding to the Contact's name, and the `nested-forms-address-list` component is responsible for iterating over the Contact's addresses and binding to them using the `nested-forms-address` (singular) component. For example, the `nested-forms-name` would be implented as so: |
|
|
|
|
|
|
|
```typescript |
|
|
|
@Component({ |
|
|
|
selector: 'nested-forms-name', |
|
|
|
templateUrl: './name.component.html', |
|
|
|
styleUrls: ['./name.component.css'] |
|
|
|
}) |
|
|
|
export class NameComponent implements OnInit { |
|
|
|
|
|
|
|
@Input() name: Name; |
|
|
|
@Input() parent: FormGroup; |
|
|
|
|
|
|
|
group: FormGroup; |
|
|
|
|
|
|
|
constructor(private fb: FormBuilder) { |
|
|
|
} |
|
|
|
|
|
|
|
ngOnInit() { |
|
|
|
this.group = this.fb.group({ |
|
|
|
firstName: new FormControl(this.name ? this.name.firstName : ''), |
|
|
|
lastName: new FormControl(this.name ? this.name.lastName : ''), |
|
|
|
middleName: new FormControl(this.name ? this.name.middleName : ''), |
|
|
|
prefix: new FormControl(this.name ? this.name.prefix : ''), |
|
|
|
suffix: new FormControl(this.name ? this.name.suffix : ''), |
|
|
|
}); |
|
|
|
|
|
|
|
if (this.parent) { |
|
|
|
this.parent.addControl('name', this.group); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
Calling `this.parent.addControl(....)` is what ensures that the controls created in the child component are made part of the over-all form. |
|
|
|
|
|
|
|
## Pros |
|
|
|
|
|
|
|
- The parent component is easy to understand and maintain |
|
|
|
- Each child component encapsulates its form controls and template |
|
|
|
- The child components can easily be re-used in other "parent" components |
|
|
|
|
|
|
|
## Cons |
|
|
|
|
|
|
|
- The creation of the form controls is tightly coupled with the templates |
|
|
|
- Since each child component encapsulates its form controls, the overall shape of the form data is not always clear |
|
|
|
|