Angular Tutorial - Reusable Form Component with Multiple Controls and Validation
Validating reusable multi-input components can be difficult. To be as DRY as possible, learn how to create a multi-input component with validation.
Collecting the same information for different users required a different set of validation per each user. Since the information between forms were consistent, deciding pieces of the form that could be reused across multiple teams avoiding the styling, differences in labels, and validations proved to be difficult, particularly with multi-input forms.
These complex forms presented a challenge on how to best validate the logic for the dependent's component controls. The Angular team presently works on the generic FormGroup class which binds different controls to a particular model. Until that is released, I rolled my own which can guide the usage of the multiple control component.
In order to allow for specific validation per the multi-control component, the consuming component defines the validation and passes the ModeledFormGroup to the component as an input property.
Being DRY as possible, the team created multiple components composed of multiple form inputs. The ControlValueAccessor allowing the consuming component to set the value wouldn't work in this instance. The validation specific to each control needed the consuming component to handle validations and display the correct error message.
Love Web Development?
Angular, Google Cloud, C#, Node, NestJs?
Goals
- Create a Multi-Control Component
- Generate the Validator Service
- Consuming Component Provides Validation
You may find a list of the packages used in these examples at the bottom of the page.
Create a Multi-Control Component
Across multiple forms, first and last name are required for collection. To demonstrate, a model IFullName and a component multi-control.component.ts file were created. IFullName presents itself as a type property on the BillingAddressModel as fullName.
Free Your Developers
Nx Monorepo Starter alleviates developers from re-inventing deployments on popular Google Cloud Services, standardizes on libraries, and saves time for scaffolding projects.
View on GumroadFree Your Developers
Nx Monorepo Starter alleviates developers from re-inventing deployments on popular Google Cloud Services, standardizes on libraries, and saves time for scaffolding projects. Invalid Email Address View on Gumroad
Thanks for your interest in Nx GCP Starter. We'll be in touch shortly
Generate the Validator Service
The model for this use case nests the fullName property as an object with firstName and lastName.
Nested properties exist as string via dot notation on the fullName property. This allows the validator on the client side to crawl through the nested controls of the ModeledFormGroup and set validation errors where found.
Consuming Component Provides Validation
In the consuming component, we're collecting a user's billing address. This creates nested ModeledFormGroups.
In the multi-control-validation.component.ts a nested ModeledFormGroup for the fullName property exists on the BillingAddressModel. In the constructor, the ValidationMessageService from the @ngserveio/validation-messages library adds a validation message for emptyOrWhitespace validation. If the field is an empty string or whitespace, a message will be returned {{fieldName}} is required. The ValidationMessageService handles the string interpolation for the fieldName.

On line number 27 of the multi-control-validation.component.ts, the BillingAddressValidator validates the properties of a BillingAddressModel.
In the ngOnInit method on line 37 of multi-control-validation.component.ts, a subscription to the modelChanged observable of the addressFormGroup is created. This allows the ModeledFormGroup to watch for changes to FormControls and applies the BillingAddressValidator validators.
Summary
Reusable form components come with added complexity if validations need to exist outside of the component and the validations exist in the consumer component. The ControlValueAccessor won't suffice in this case.
In order to allow the consuming component to validate, passing a ModeledFormGroup from the consumer to the multi-control component provides the consumer flexibility to be responsible for the validation of the multi-control component.






