-
Notifications
You must be signed in to change notification settings - Fork 59
Forms Runtime Infrastructure
Forms are meant to be the interactive applications that can capture data from the users. Therefore, forms must maintain a state (form model) that can be manipulated using the views (a.k.a. presentation layer).
The runtime of the Adaptive Form requires two-way interaction between the model and the view layer to support dynamism in the form.
There are dynamic features such updating value/properties (like visible
, enabled
, readOnly
, label
, description
, valid
, errorMessage
) of the form field. Clicking on the help icon toggles the description. clicking on edit rule icon open the rule editor in authoring, toggle of short description and long description in case short description is configured to be always shown are all such examples of this dynamic behavior.
All these features assume a particular structure hierarchy of the component.
<div class="cmp-adaptiveform-textinput"
id="<some-unique-id>"
data-cmp-is="adaptiveFormTextInput"
data-cmp-adaptiveformcontainer-path="${formstructparser.formContainerPath}"
<label/> `
<widget/>
<longDescription/>
<shortDescription/>
<errorMessage/>
<helpIcon/>
</div>
This hierarchy can also be changes in the custom component implementation and runtime libraries provide sufficient APIs to make this happen.
Note that the id and data attributes of the component should be defined in the topmost div of the hierarchy for the working of OOTB form runtime. Also,
data-cmp-is
anddata-cmp-adaptiveformcontainer-path
are required attributes for all components for the form authoring and runtime to work correctly.
Here, @aemforms/af-core is the model layer where all business logic (for rule execution, validations, etc) is there. This package is hosted on npm. JS docs for the form model can be referred here.
The view layer's base logic resides in ui.frontend webpack module. One of the important classes is FormField.js where all basic functionalities common to all form components is expected to reside. Besides that, there are some common utility functions in this module. All the relevant classes are exposed through global FormView namespace so that they can be used in individual core component clientlibs.
The individual view classes are expected to reside in respective core-component folder inside ui.af.apps package. Eg: Text-Input Core Component view library.
The view is supposed to interact with model on basis on HTML5 events received from the client (eg: onchange
, onclick
, blur
, etc).
Model is supposed to execute the business logic on basis of json schema and rules configured in the field, and call the form field's subscribe
callBack. Then the view is expected to react accordingly and reflect the state in the presentation layer if required.
All the field properties that are writable (readOnly=false) should be handled by the view layer. List of such properties can be referred to from the Adobe Adaptive Form definition.
FormField interface is part of view that is typically meant for 2 main tasks:
- dispatching change event from view to model
- subscribing to the changes in model
FormFieldBase - This can be loosely called an abstract Form Field class that provided the common functionality to all the component. The components are free to override these APIs in their implementation for their custom behaviour. This class relies on the individual component to provide the HTML5 elements corresponding to the help icon, long description, short description, error message div and the actual widget of the component.
subscribe
method is called whenever there's an update in the model.
Further there are methods to update description toggle of help icon, show validation errors, setting field props in this class, etc. The naming convention of these methods is update<propertyName>
.
The following Form Field properties are supported to be updated dynamically through rules:
- value
- visible
- enabled
- readOnly
- valid
- description
- label
- errorMessage
Eg:
updateVisible(currentValue, state)
is responsible to updating the visible
property of the form field in the view. This method receives the current value of the property and the state.
The update<propertyName>
methods can be overridden in individual component view classes to incorporate the component specific behaviour for these properties.
setModel
method is used to update the model in the event handlers attached to the view layer. This method can be overridden in the respective component view classes to update the model accordingly. This method is called only once during initialisation of the component and the view specific event listeners are registered during this initialisation.
The runtime libraries in ui.frontend module are exported in the
FormView
namespace onwindow
in order to be used within the AEM client libraries of the individual components.
ComponentView - This resides with the individual core component and has a client-library category particular to that component only. It's category name follows a convention of core.forms.components.<component-name>.<component-version>.runtime
Eg: core.forms.components.textinput.v1.runtime
.
This class typically extends from the above FormFieldBase
class.
Its responsibility is to define the BEM selectors to be used for the component elements and implement getters of those elements. These elements typically include component's widget, label, long-description-div, short-description-div, error-div, help-icon-div to name a few.
Each component requires to be initialised by the runtime so that it can participate in the dynamic interactions.
It is done by calling the setupField
method exposed in FormView.Utils
namespace. For example, the Text Input component is initialised like the following:
FormView.Utils.setupField(({element, formContainer}) => {
return new TextInput({element, formContainer})
}, TextInput.selectors.self);
The Adaptive Form runtime library is represented by the AEM Client Library core.forms.components.runtime.all
. You can find the library here.
If you want to include Adaptive Form in your custom page component, you can check our OOTB page component. Refer to customfooterlibs.html for javascript inclusion in an async mode (for performance reasons) and customheaderlibs.html for the css inclusion in the page.