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.

Angular Tutorial - Share Validations between NestJs and Angular
Validating models across NestJs and Angular could lead to many differences. This explains how we can leverage the power of Typescript to create a cross platform validation library.

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.

Goals

  1. Create a Multi-Control Component
  2. Generate the Validator Service
  3. 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 Gumroad

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.    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.

Angular Materail Tutorial - Create a Reusable Form Field Component
Create a Reusable Form Field Component using Angular Material

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.

@ngserveio/material-forms
Find more at libraries and examples at [NgServe.io](https://ngserve.io).
@ngserveio/you-good
A client and backend agnostic validation library created by [NgServe.io](https://ngserve.io).
@ngserveio/form-services
Find more at libraries and examples at [NgServe.io](https://ngserve.io).
@ngserveio/utilities
Find more at libraries and examples at [NgServe.io](https://ngserve.io).
@ngserveio/validation-messages
Find more at libraries and examples at [NgServe.io](https://ngserve.io).