Skip to content

Forms Runtime Infrastructure

Nitin Gupta edited this page Feb 10, 2023 · 4 revisions

Introduction

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.

Component structure

<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 and data-cmp-adaptiveformcontainer-path are required attributes for all components for the form authoring and runtime to work correctly.

Runtime libraries

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.

View Layer

FormField interface is part of view that is typically meant for 2 main tasks:

  1. dispatching change event from view to model
  2. 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.

The View subscribes for changes in the Model

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.

Updating the Model on changes in the View

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

Initialisation of a component

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);

Include Runtime Libraries in page component

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.