Split Complex Forms in Angular with ControlContainer

Developing complex forms can become a challenging task, especially when we need to deal with a lot of validation and component reuse. In Angular, we have Reactive Forms that provide several tools to deal with these situations, and within this package, …

Developing complex forms can become a challenging task, especially when we need to deal with a lot of validation and component reuse. In Angular, we have Reactive Forms that provide several tools to deal with these situations, and within this package, we have the ControlContainer, a class that helps to better manage our forms.

Nested Forms and Child Components

A classic example of its use is when we have a form with many controls and they can be reused elsewhere. Registration of people will ask for common data such as first name, last name, and date of birth. And they can come from different records such as customers and suppliers.

export class CustomerFormComponent implements OnInit {
form: FormGroup;
constructor(private formBuilder: FormBuilder) {}
  ngOnInit() {
this.form = this.formBuilder.group({
name: ‘’,
lastName: ‘’,
birthDate: ‘’,
country: ‘’,
cpf: ‘’,
});
}
}
export class ProviderFormComponent implements OnInit {
form: FormGroup;
constructor(private formBuilder: FormBuilder) {}
  ngOnInit() {
this.form = this.formBuilder.group({
name: ‘’,
lastName: ‘’,
birthDate: ‘’,
rg: ‘’,
});
}
}

It would be nice to have a component with all this data in common so that we can use it in multiple forms. But how are we going to do this when the FormGroup declaration will be inside the parent component?

Injecting the ControlContainer

The ControlContainer class will play that role brilliantly. By injecting it into a child component, we retrieve the FormGroup instance from the parent component. We can then create a component that has the template of the people registration controls and reuse it in the components of customers and suppliers.

@Component({
selector: ‘app-person-form’,
template: `
<div [formGroup]=”form”>
<input placeholder=”Name” formControlName=”name” />
<input placeholder=”Surname” formControlName=”lastName” />
<input placeholder=”Date of birth” formControlName=”birthDate” />
</div>
`,
})
export class PersonFormComponent implements OnInit {
form: FormGroup;
constructor(private controlContainer: ControlContainer) {}
ngOnInit() {
this.form = this.controlContainer.control as FormGroup;
}
}

Now just call the child component in the parent components:

@Component({
selector: ‘app-customer-form’,
template: `
<h3>Customer Form</h3>
<form [formGroup]=”form”>
<app-person-form></app-person-form>
<input placeholder=”City” formControlName=”country” />
<input placeholder=”CPF” formControlName=”cpf” />
</form>
`,
})
export class CustomerFormComponent implements OnInit …
@Component({
selector: ‘app-provider-form’,
template: `
<h3>Provider Form</h3>
<form [formGroup]=”form”>
<app-person-form></app-person-form>
<input placeholder=”RG” formControlName=”rg” />
</form>
`,
})
export class ProviderFormComponent implements OnInit …

Use in policies

Now we will see a scenario where we need to add two more fields to the supplier form. A checkbox to inform if the supplier has a driver’s license and input to inform the driver’s license number. This input will only be mandatory if the checkbox is checked. If the checkbox is unchecked then the input will be disabled.

Thinking that this rule might repeat itself for other fields, we can create a reusable directive by injecting the ControlContainer.

@Directive({
selector: ‘[appEnableOnCheck]’,
})
export class EnableOnCheckDirective {
@Input(‘appEnableOnCheck’) checkboxName: string;
@Input(‘formControlName’) inputName: string;
destroy$ = new Subject<void>();
constructor(private controlContainer: ControlContainer) {}
ngOnInit(): void {
this.getControl(this.checkboxName)
.valueChanges.pipe(takeUntil(this.destroy$))
.subscribe((checked: boolean) => {
if (checked) {
this.getControl(this.inputName).enable();
} else {
this.getControl(this.inputName).disable();
this.getControl(this.inputName).setValue(‘’);
}
});
}
ngOnDestroy(): void {
this.destroy$.next();
}
private getControl(controlName: string): FormControl {
return this.controlContainer.control.get(controlName) as FormControl;
}
}

See how interesting the dynamics of this directive are. First, we capture the names of the controls that interest us by Input (in this case it is the checkbox and the CNH field).

Since we know we have the form reference with the ControlContainer, we can then listen for our checkbox value changes with the observable valueChanges. Inside the subscribe just make the condition: If it is checked we enable the input, otherwise we disable the input and clear its value.

In the component’s HTML template we call the directive inside the control.

@Component({
selector: ‘app-provider-form’,
template: `
<h3>Provider Form</h3>
<form [formGroup]=”form”>
<app-person-form></app-person-form>
<input placeholder=”RG” formControlName=”rg” />

<input type=”checkbox” formControlName=”hasCnh” />
<input placeholder=”CNH” formControlName=”cnh” appEnableOnCheck=”hasCnh” />
<pre>VALID: {{ form.valid }}</pre>
</form>
`,
})
export class ProviderFormComponent implements OnInit {
form: FormGroup;
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
this.form = this.formBuilder.group({
name: ‘’,
lastName: ‘’,
birthDate: ‘’,
rg: ‘’,
hasCnh: false,
cnh: new FormControl({ value: ‘’, disabled: true }, Validators.required),
});
}
}

The policy selector receives the name of the control that we are going to hear the changes (checkbox hasCnh). The control that will be changed (cnh) is captured by the formControlName selector. Simple and reusable!

Conclusion

We have endless possibilities for building complex and reactive forms using the ControlContainer. With it, we can easily divide forms into multiple layers and separate responsibilities into other components and policies.

If you liked this blog post, consider following me for more such stuff. Also, feel free to add your thoughts! Or you can buy me a coffee!

Turn Complex Forms into Reusable Micro Frontends with Components

Microfrontends are a great way to speed up and scale app development, with independent deployments, decoupled codebases, and autonomous teams.

Bit offers a great developer experience for building component-driven Micro frontends. Build components, collaborate, and compose applications that scale. Our GitHub has over 14.5k stars!

Give Bit a try →

An independently source-controlled and shared “card” component (on the right, its dependency graph, auto-generated by Bit)

Learn more


Split Complex Forms in Angular with ControlContainer was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.


Print Share Comment Cite Upload Translate
APA
Talha Khaild | Sciencx (2024-03-29T10:56:17+00:00) » Split Complex Forms in Angular with ControlContainer. Retrieved from https://www.scien.cx/2022/01/07/split-complex-forms-in-angular-with-controlcontainer/.
MLA
" » Split Complex Forms in Angular with ControlContainer." Talha Khaild | Sciencx - Friday January 7, 2022, https://www.scien.cx/2022/01/07/split-complex-forms-in-angular-with-controlcontainer/
HARVARD
Talha Khaild | Sciencx Friday January 7, 2022 » Split Complex Forms in Angular with ControlContainer., viewed 2024-03-29T10:56:17+00:00,<https://www.scien.cx/2022/01/07/split-complex-forms-in-angular-with-controlcontainer/>
VANCOUVER
Talha Khaild | Sciencx - » Split Complex Forms in Angular with ControlContainer. [Internet]. [Accessed 2024-03-29T10:56:17+00:00]. Available from: https://www.scien.cx/2022/01/07/split-complex-forms-in-angular-with-controlcontainer/
CHICAGO
" » Split Complex Forms in Angular with ControlContainer." Talha Khaild | Sciencx - Accessed 2024-03-29T10:56:17+00:00. https://www.scien.cx/2022/01/07/split-complex-forms-in-angular-with-controlcontainer/
IEEE
" » Split Complex Forms in Angular with ControlContainer." Talha Khaild | Sciencx [Online]. Available: https://www.scien.cx/2022/01/07/split-complex-forms-in-angular-with-controlcontainer/. [Accessed: 2024-03-29T10:56:17+00:00]
rf:citation
» Split Complex Forms in Angular with ControlContainer | Talha Khaild | Sciencx | https://www.scien.cx/2022/01/07/split-complex-forms-in-angular-with-controlcontainer/ | 2024-03-29T10:56:17+00:00
https://github.com/addpipe/simple-recorderjs-demo