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.