This documentation will help you with some best practices to use when building Angular reactive forms components that have a parent/input component structure (such as container/presenter).
Note that this structure is simply an implementation of the ControlValueAccessor
pattern, but we have chosen to guide you with an example implemented in the showcase application.
A parent/input component architecture was put in place to ensure the best reusability and sharing of components.
formGroup
/ formArray
/ formControl
object as a two-way data binding object between the parent component and the input component.From now on we will refer to input component form object as the formGroup
or formArray
or formControl
created in the input component.
In a simple case, the purpose of the data exchange is to display the inline errors, check the form validity, and emit the form value.
Data exchange in a complex case has the same purpose as the simple case, plus the display of a message panel containing the form errors and the ability to submit the form from the input component or page.
validate
function, the error object model which will be propagated to the parent component. See the form error documentation for details.We prefer to use the formControl
rather than ngModel
because we can easily listen to the valueChanges
or statusChanges
of the input component form.
Another constraint is that it's easier to identify the parent component context for the CMS (See Component Structure for details about the component context).
Here, a component refers to a parent component or an input component.
In this case, all we need to do is to implement a form, display the inline errors, check the form validity, and do something with the form value.
In the input component:
The parent component:
The difference from the default implementation of the forms in Angular is that we have to emit the form value from the parent component to the input component, using an @Output event. Another difference might be related to the custom validators, which we suggest to be created in the parent component because they can be related to the business logic. (Please have a look at the dedicated section on the forms validators.).
In addition to the simple case, if we need an error message panel, which can be displayed anywhere in the page, or to submit the form outside the component, we must follow the more complex implementation described below.
The form created in the input component and the default value of the form control in the parent component should have the same contract. The contract of a form is an interface that defines the names of the form controls and the type of value that should be handled by each control.
Below is an example of a component creation based on a form used to introduce data for a PersonalInfo
object.
Define the contract object:
Below is an example of a model used to create a form that introduces data for a PersonalInfo
object.
/** Model used to create Personal Info form */
export interface PersonalInfo {
/** Name */
name: string;
/** Date of birth */
dateOfBirth: string;
}
Parent component class:
You can find the implementation of a parent component class in the showcase application.
Input component class:
formGroup
/formArray
/formControl
object.You can find the implementation of an input component class in the showcase application.
Submit and Intercommunication:
We have to handle specific cases for form submission and communication between input component/parent component/page. For the submit action, we have to support two cases:
The page triggers the submit action. The parent component receives the signal, executes the submit logic, and emits an event when the submit logic is finished.
This is useful when you have multiple forms on a page and you want to trigger the submit for all in the same time.
This section is explained in details in the Otter form submit and intercommunication documentation.
You can create basic or custom validators in your application, depending on the use cases. You can find details on this in the Otter form validation documentation.