Forms Temp2

Document created by resplin Employee on Jun 6, 2015Last modified by alfresco-archivist on Aug 31, 2016
Version 2Show Document
  • View in full screen mode

Obsolete Pages{{Obsolete}}

The official documentation is at: http://docs.alfresco.com



3.0Draft Pages

DRAFT DRAFT DRAFT

NOTE: This document is a 'work in progress' and does not represent any commitment yet to v3.0.


Intro


The Alfresco web client presents 'forms' (data view & entry dialogs) throughout its user interface (DM & WCM).  In v2.X, these 'forms' are implemented in multiple ways, from hand-coding JSF to declaring an XSD.

The V3.0 web client is being built on a web framework that is designed to enable the development of all kinds of web sites (by the wider community). 'Forms' play an important part of web sites and as such the framework needs to provide a convention for implementing them.  This document proposes that convention.


Review of 'Forms' in v2.X


  • Used for
    1. property sheets (view & edit)
    2. structured (xml) content (view & edit)
    3. content-centric dialogs (e.g. task view & edit)
    4. ad-hoc dialogs (e.g. login, create user)
  • Implemented via
    1. Hand-coded JSF backing beans / JSP tags
    2. Property Sheet XML configuration
    3. Annotated XSD (via Chiba, an XForm implementation)
  • Repository touch points
    1. Node property/association read & write
    2. XML document read & write
    3. Service invocation

v3.0 'Forms' Requirements


M=Must, S=Should, C=Could, W=Wont


  • M Provide a single 'forms' platform (maximize community leverage)
  • M Consistent Forms User Experience across the board
  • M Web Runtime
  • M Support developer community via programmatic form development
  • M Backward compatibility for v2.X Property Sheet Configuration
  • M Forms as re-usable Web UI Components
  • M Open hooks for Data Retrieval and Submission (e.g. REST)
  • S Backward compatibility for v2.X Annotated XSD
  • S Support full form processing model e.g. input types, validation, binding, dependencies, events, ... (as defined by XForms specification)
  • S Support XForms definition language
  • C GUI tool for form development
  • C Desktop Runtime
  • W Exclusively tied to XML input & output

v3.0 'Forms' Approach


The aim of the proposal is to break the solution into a number of architectural components which may be selectively chosen to satisfy a particular requirement. We don't want to repeat the monolithic approach of xforms.js in v2.X, which limits forms to a single use-case.

The components are:


Forms Runtime (M)


The runtime is responsible for the execution of a form.  For Alfresco v3.0, this is very likely to be browser-hosted & AJAX oriented, potentially with a web app tier back-end.  It manages all input, validation (client or call-back), field dependencies, events, server data query, submission etc. From a development perspective, a form is defined via markup, event handling and API calls. i.e. developer friendly, not business analyst friendly.  The API is most likely to be client-side Javascript.

The capabilities of the forms runtime should be driven by the XForms processing model, but how much is implemented is really driven by the requirements of the 3.0 wireframes & property sheets.  The chosen JS library 'YUI' has some basic forms capability which can be built upon.

Examples of forms run-time we should aim for are:


The runtime should also support any data input & output schema although the likely candidates are XML & JSON. Also, repository interaction should be via REST, allowing for any 'data/service' back-end.

It's important to note that the runtime must be independent of any declarative Form Definition Language i.e. even if we didn't define a declarative form definition language,  it must still be possible to implement a form with markup and the runtime API.  This allows us to implement our regular form-based UI using good old programmatic techniques.  It also allows us to mix form/non-form UI in the same dialog.


'Property Sheet' Form Definition Language (M)


Alfresco V2.X provides a declarative language for defining read-only and read- write property sheets which are view and edit forms tied to properties and associations of a node in the repository.

Property Sheet XML is really just a simple Form Definition Language.  As such we must support the generation of appropriate Forms Runtime markup and API calls, including appropriate data query and submission requests (for interacting with node meta-data) directly from Property Sheet XML. Generation is likely to occur at run-time or potentially be an aggregation of pre-generated form snippets.  The generator implementation could be freemarker template based. However it's implemented, it must be possible to build a UI component (that complies with our web framework model) that takes a node ref and renders the appropriate property sheet form using the generator.


Advanced 'Property Sheet' Form Definition Language (C)


Depending upon how sophisticated the Forms Runtime becomes, we can extend the current Property Sheet Configuration to include repeating groups, dependencies, data call-backs etc.


Property Sheet Form (M)


A Web UI Component which renders a form for viewing or editing the properties/associations of a node driven from Property Sheet Form Definition Language (a series of configuration XML files tied to types/aspect as today).


'Annotated XSD' Form Definition Language (S)


Alfresco V2.X provides a sophisticated forms interface for viewing and editing XML documents.  The form is generated directly from an XSD.  Some UI aspects are controlled via XSD annotations.

As with Property Sheet XML, Alfresco Annotated XSD is really just another fairly simple Form Definition Language.  As such, we must support the generation of appropriate Forms Runtime markup and API calls, including appropriate data query and submission requests (for interacting with xml content) directly from Annotated XSD.  Again, it must be possible to build a UI component (that complies with our web framework model) that takes a node ref/property ref to XML content and renders the appropriate XML form using this generator.


XML Content Form (S)


A Web UI Component which renders a form for viewing or editing the XML content of a node (in DM or WCM) driven from Annotated XSD Form Definition Language.


WCM/DM XML Forms Upgrade (C)


When we support 'XML Content' Forms.


XForm Definition Language (C)


What about XForms?  Well, with the above three components it's not strictly required.  XForms is a language, not an implementation.  As a language, it's still not mainstream, even after several years.  Developments such as Forms Lite are interesting as it seems more directly targeted at the today's developer who's building web sites with Client-side/Ajax type libraries.

As with other Form Definition Languages, if we support XForms, we support the generation of appropriate Forms Runtime markup and API calls from an XForm definition whose data source and submission is XML based.  With the generator we can build XForm-based UI Components.


v3.0 'Forms' Design


This section provides an overview of the proposed implementation of forms for 3.0, the diagram below shows a brainstorm of the high level features that may be available.

Forms brainstorm.png


Runtime


The Forms Runtime consists of a small lightweight JavaScript library. An unobtrusive JavaScript pattern will be followed whereby behaviour is added to the HTML form elements upon page load.

The forms runtime provides the equivalent functionality and more of the existing JSF based property sheet component, meaning the following capabilities are available:


  • Mandatory property handling (OK button is disabled until all supplied)
  • Validation (enforceable at submission, as the user types or when a field loses focus)
    • Regular expression matching
    • String length
    • Email address
    • Is number
    • Numeric range
    • Date range
    • List of values
    • Masks
  • Repeating fields (for handling multi-valued properties)

API


At the center of the forms runtime is the Alfresco.forms.Form object, it's constructors are shown below.



  /**
    * Constructor for a form.
    *
    * @param {String} formId The HTML id of the form to be managed
    * @return {Alfresco.forms.Form} The new Form instance
    * @constructor
    */
   Alfresco.forms.Form = function(formId)

The Form object has several methods available to setup the behaviour of the form.



  /**
    * Sets up the required event handlers and prepares the form for use.
    * NOTE: This must be called after all other setup methods.
    *
    * @method init
    */
   init: function()

  /**
    * Enables or disabled validation when the form is submitted.
    *
    * @method setValidateOnSubmit
    * @param validate {boolean} true to validate on submission, false
    *        to avoid validation
    */
   setValidateOnSubmit: function(validate)

  /**
    * Sets whether all fields are validated when the form is submitted.
    *
    * @method setValidateAllOnSubmit
    * @param validate {boolean} true to validate all fields on submission, false
    *        to stop after the first validation failure
    */
   setValidateAllOnSubmit: function(validateAll)

  /**
    * Sets the list of ids and/or elements being used to submit the form.
    * By default the forms runtime will look for and use the first
    * input field of type submit found in the form being managed.
    *
    * @method setSubmitElements
    * @param submitElements {object | object[]} Single object or array of objects
    */
   setSubmitElements: function(submitElements)

  /**
    * Sets the container where errors will be displayed.
    *
    * @method setErrorContainer
    * @param position {string} String representing where errors should
    *                 be displayed. If the value is not 'alert' it's presumed the
    *                 string is the id of an HTML object to be used as the error
    *                 container
    */
   setErrorContainer: function(container)

  /**
    * Sets whether the submit elements dynamically update
    * their state depending on the current values in the form.
    * The visibility of errors can be controlled via the
    * showErrors parameter.
    *
    * @method setShowSubmitStateDynamically
    * @param showState {boolean} true to have the elements update dynamically
    * @param showErrors {boolean} true to show any validation errors that occur
    */
   setShowSubmitStateDynamically: function(showState, showErrors)

  /**
    * Enables or disables whether the form submits via an AJAX call.
    *
    * @method enableAJAXSubmit
    * @param ajaxSubmit {boolean} true to submit using AJAX, false to submit
    *        using the browser's default behaviour
    * @param callbacks {object} Optional object representing callback handlers
    *        or messages to use, for example
    *        {
    *           successCallback: yourHandlerObject,
    *           failureCallback: yourHandlerObject,
    *           successMessage: yourMessage,
    *           failureMessage: yourMessage
    *        }
    *        Callback handler objects are of the form:
    *        {
    *           fn: function, // The handler to call when the event fires.
    *           obj: object, // An object to pass back to the handler.
    *           scope: object // The object to use for the scope of the handler.
    *        }
    */
   setAJAXSubmit: function(ajaxSubmit, callbacks)

  /**
    * Enables or disables submitting the form data in JSON format.
    * Setting the enctype attribute of the form to 'application/json'
    * in Firefox will achieve the same result.
    *
    * @method setSubmitAsJSON
    * @param submitAsJSON {boolean} true to submit the form data as JSON,
    *        false to submit one of the standard types 'multipart/form-data'
    *        or 'application/x-www-form-urlencoded' depending on the enctype
    *        attribute on the form
    */
   setSubmitAsJSON: function(submitAsJSON)



The Form object has several methods available to define the behaviour for individual fields.



  /**
    * Adds validation for a specific field on the form.
    *
    * @method addValidation
    * @param fieldId {string} Id of the field the validation is for
    * @param validationHandler {function} Function to call to handle the
    *        actual validation
    * @param validationArgs {object} Optional object representing the
    *        arguments to pass to the validation handler function
    * @param when {string} Name of the event the validation should fire on
    *        can be any event applicable for the field for example on a text
    *        field 'blur' can be used to fire the validation handler as the
    *        user leaves the field. If null, the validation is only called
    *        upon form submission.
    */
   addValidation: function(fieldId, validationHandler, validationArgs, when)

  /**
    * Retrieves the label text for a field
    *
    * @method getFieldLabel
    * @param fieldId {string} The id of the field to get the label for
    * @return {string} The label for the field or the fieldId if a label could not be found
    */
   getFieldLabel: function(fieldId)

  /**
    * Sets a field as being repeatable, this adds a 'plus' sign after the field
    * thus allowing multiple values to be entered.
    *
    * @method setRepeatable
    * @param fieldId {string} Id of the field the validation is for
    * @param containerId {string} Id of the element representing the
    *        field 'prototype' i.e. the item that will get cloned.
    */
   setRepeatable: function(fieldId, containerId)

There are several validation handlers provided out of the box to cover the most common usage scenarios. All validation handlers have the same signature as shown below:



   /**
     * xxxxxx validation handler.
     *
     * @method mandatory
     * @param field {object} The element representing the field the validation is for
     * @param args {object} Object containing arguments for validation
     * @param event {object} The event that caused this handler to be called, maybe null
     * @param form {object} The forms runtime class instance the field is being managed by
     * @param silent {boolean} Determines whether the user should be informed upon failure
     * @static
     */
Alfresco.forms.validation.xxxxxx = function xxxxxx(field, args, event, form, silent)

The handlers supplied out of the box are detailed below.



  /**
    * Mandatory validation handler, tests that the given field has a value.
    *
    * @method mandatory
    * @param field {object} The element representing the field the validation is for
    * @param args {object} Not used
    * @param event {object} The event that caused this handler to be called, maybe null
    * @param form {object} The forms runtime class instance the field is being managed by
    * @param silent {boolean} Determines whether the user should be informed upon failure
    * @static
    */
   Alfresco.forms.validation.mandatory = function mandatory(field, args, event, form, silent)

  /**
    * Length validation handler, tests that the given field's value has either
    * a minimum and/or maximum length.
    *
    * @method length
    * @param field {object} The element representing the field the validation is for
    * @param args {object} Object representing the minimum and maximum length, for example
    *        {
    *           min: 3;
    *           max: 10;
    *        }
    * @param event {object} The event that caused this handler to be called, maybe null
    * @param form {object} The forms runtime class instance the field is being managed by
    * @param silent {boolean} Determines whether the user should be informed upon failure
    * @static
    */
   Alfresco.forms.validation.length = function length(field, args, event, form, silent)

  /**
    * Number validation handler, tests that the given field's value is a number.
    *
    * @method number
    * @param field {object} The element representing the field the validation is for
    * @param args {object} Not used
    * @param event {object} The event that caused this handler to be called, maybe null
    * @param form {object} The forms runtime class instance the field is being managed by
    * @param silent {boolean} Determines whether the user should be informed upon failure
    * @static
    */
   Alfresco.forms.validation.number = function number(field, args, event, form, silent)

  /**
    * Number range validation handler, tests that the given field's value has either
    * a minimum and/or maximum value.
    *
    * @method numberRange
    * @param field {object} The element representing the field the validation is for
    * @param args {object} Object representing the minimum and maximum value, for example
    *        {
    *           min: 18;
    *           max: 30;
    *        }
    * @param event {object} The event that caused this handler to be called, maybe null
    * @param form {object} The forms runtime class instance the field is being managed by
    * @param silent {boolean} Determines whether the user should be informed upon failure
    * @static
    */
   Alfresco.forms.validation.numberRange = function numberRange(field, args, event, form, silent)

  /**
    * Regular expression validation handler, tests that the given field's value matches
    * the supplied regular expression.
    *
    * @method regexMatch
    * @param field {object} The element representing the field the validation is for
    * @param args {object} Object representing the expression, for example to validate
    *        a field represents an email address:
    *        {
    *           pattern: /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/}
    *        }
    * @param event {object} The event that caused this handler to be called, maybe null
    * @param form {object} The forms runtime class instance the field is being managed by
    * @param silent {boolean} Determines whether the user should be informed upon failure
    * @static
    */
   Alfresco.forms.validation.regexMatch = function regexMatch(field, args, event, form, silent)

   /**
    * Node name validation handler, tests that the given field's value is a valid
    * name for a node in the repository.
    *
    * @method nodeName
    * @param field {object} The element representing the field the validation is for
    * @param args {object} Not used
    * @param event {object} The event that caused this handler to be called, maybe null
    * @param form {object} The forms runtime class instance the field is being managed by
    * @param silent {boolean} Determines whether the user should be informed upon failure
    * @static
    */
   Alfresco.forms.validation.nodeName = function number(field, args, event, form, silent)

   /**
    * Email validation handler, tests that the given field's value is a valid
    * email address.
    *
    * @method email
    * @param field {object} The element representing the field the validation is for
    * @param args {object} Not used
    * @param event {object} The event that caused this handler to be called, maybe null
    * @param form {object} The forms runtime class instance the field is being managed by
    * @param silent {boolean} Determines whether the user should be informed upon failure
    * @static
    */
   Alfresco.forms.validation.email = function number(field, args, event, form, silent)

Examples


The form shown below is a simple standard HTML form that a designer may have produced to represent the data that needs collecting for a new user registration process. This form will be used to demonstrate the capabilities of the forms runtime, each example below augments the previous one to build up to a complete example highlighting all features.



<context>/some-consumption-service' method='post'>
   <fieldset>
      <legend>Login details</legend>
      <label for='username'>Username</label>
      <input id='username' name='username' type='text' size='40' />
     


      <label for='pwd'>Password</label>
      <input id='pwd' name='pwd' type='password' size='40' />
     


      <label for='email'>Email Address</label>
      <input id='email' name='email' type='text' size='40' />
   </fieldset>

   <fieldset>
      <legend>Personal information</legend>
      <label for='name'>Name</label>
      <input id='name' name='name' type='text' size='40' />
     


      <label for='age'>Age</label>
      <input id='age' name='age' type='text' size='5' />
     


      <label for='gender'>Gender</label>
      <input id='gender' name='gender' type='radio' value='male' /><label>Male</label>
      <input id='gender' name='gender' type='radio' value='female' /><label>Female</label>
     


      <label for='country'>Country</label>
      <select id='country' name='country'>
         <option value=''>Please select...</option>
         <option value='france'>France</option>
         <option value='germany'>Germany</option>
         <option value='spain'>Spain</option>
         <option value='uk'>United Kingdom</option>
         <option value='us'>United States</option>
      </select>
     


      <label for='interests'>Interests</label>
      <input id='interests' name='interests' type='text' size='40' />
   </fieldset>

   <input id='submit' type='submit' value='Submit'>
   <input value='Clear' type='reset'>
</form>

The first step is to add the required JavaScript to the page to represent the form, placed AFTER the form definition.



<script type='text/javascript' src='${url.context}/js/forms-runtime.js'></script>
<script language='JavaScript' >
   var regform = new Alfresco.forms.Form('regform');
</script>

For our fictitious registration process the username needs to be more than 3 characters in length, but less than 10. To do this a validation handler can be added to the form.



regform.addValidation('username', Alfresco.forms.validation.length, {min: 3, max: 10}, 'blur');

This will ensure that the username is between 3 and 10 characters in length before the form can be submitted. The 'blur' parameter also ensures that the username is validated as the user leaves the field.

The age, country and gender fields are all mandatory, to ensure they are filled in the mandatory validation can be added.



regform.addValidation('age', Alfresco.forms.validation.mandatory);
regform.addValidation('country', Alfresco.forms.validation.mandatory);
regform.addValidation('gender', Alfresco.forms.validation.mandatory);

Another requirement of the registration process is that only over 18s can register, to ensure this happens a number range validation can be added to the 'age' field. As with the username field above the validation is run at submission time and when the user leaves the field. If the age needs to be checked as the user types 'keyup' can be used instead of 'blur'.



regform.addValidation('age', Alfresco.forms.validation.numberRange, {min: 18}, 'blur');

If the age didn't matter a number would still be required so the number validation could be used instead.



regform.addValidation('age', Alfresco.forms.validation.number, null, 'blur');

To make sure a valid email address is entered a regexMatch validation can be applied, once again the entry is validated as the user leaves the field.



regform.addValidation('email', Alfresco.forms.validation.regexMatch, {pattern: /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/}, 'blur');

Alternatively, the email validation handler can be used.



regform.addValidation('email', Alfresco.forms.validation.email, null, 'blur');

Form level features can also be controlled, for example, if a form designer decided the submit button should only be enabled once all fields contain valid values the setShowSubmitStateDynamically() method can be used. Some care has to be taken however when using this feature, all fields that have validation should register an event otherwise the submit button will never become enabled!



regform.setShowSubmitStateDynamically(true, false);

If the dynamic state feature is being above it may be desirable in some circumstances to remove the validation performed at submission time, this can be achieved with the setValidateOnSubmit() method.



regform.setValidateOnSubmit(false);

A common way to submit forms in web 2.0 apps is to use AJAX and JSON, to make our form submit it's data in JSON format using AJAX the setAJAXSubmit and setSubmitAsJSON methods, respectively.



regform.setAJAXSubmit(true);
regform.setSubmitAsJSON(true);

The form does not have to be limited to the standard HTML controls, for example, if the form should collect the date of birth instead of the age the YUI calendar control could be used, in theory any 3rd party control could be used, the only requirement is that an 'input' element is present to represent the data for the control. Using the example above the 'age' field can be changed to a hidden input contol and a 'div' added to represent the calendar control. Follow the examples on the YUI calendar page to see how to setup the control. All that is needed is an event handler to capture the calendar 'selectEvent' event and update the value of the 'age' hidden field. The mandatory validation rule can then be applied to the 'age' field and be used to ensure a date is entered.



<label for='age'>Date Of Birth</label>
<input id='age' type='hidden' name='age' />

...
<script type='text/javascript'>
function updateDate(type, args, obj)
{
   ...
   var age = document.getElementById('age');
   age.value = day + '/' + month + '/' + year;
}
...      
calendar.selectEvent.subscribe(updateDate, calendar, true);
...
regform.addValidation('age', Alfresco.forms.validation.mandatory);
...
</script>

In the future we may also support decorating the HTML directly with extra attributes to define the field. This may done following the Forms Lite approach or use the new proposal in HTML5 for 'data' attributes (http://www.w3.org/html/wg/html5/#embedding).

Furthermore, methods may be provided in the future to define a field or collection of fields via a JSON data structure.


Definition Language


TBD (Configuration for property sheets in 3.0)


Component


TBD (Freemarker based templates to generate view and edit property sheets)


Services


TBD (Back-end services required to support component)


Controls


TBD (Freemarker template snippets that represent controls used by a property sheet or other areas of the UI)


Generators


TBD (From property sheet config, xsd and xform and possibly JSON in future)

Attachments

Outcomes