This is part two of a multipart article about dynamic form population in Alfresco Share. We will build upon the first article Alfresco Share Customization - How to Populate Form Data Dynamically - Part 1: Repository Webscript and Content Model which covers the Alfresco repository customizations necessary to build the Alfresco Share customizations in this article. This project is also built upon the project used in my previous article about adding a new page to Share and giving it a look and feel that matches Alfresco share, Alfresco Share Customization - Regions, Components and Share Look and Feel.

Overview

First we will need to customize the forms for both our new product type and product datalist (from Part 1). There is a new JavaScript validation handler and we will extend theform.head.get.ftl to load the new JS file. Finally in order to create a new product type we will modify the flash uploader controller, flash-upload.get.js.

Overriding Alfresco's Core Files

We have two core files to override, /config/web-extension/site-webscripts/org/alfresco/components/form/form.get.head.ftl and /config/web-extension/site-webscripts/org/alfresco/components/upload/flash-upload.get.js. We will be changing form.get.head.ftl to include our new JavaScript form validation handler. We only need to add a single line:
<@script type="text/javascript" src="${page.url.context}/res/js/formValidation.js"></@script>
This line can be added just after the other script tags in the file. I copied this file from Alfresco's webscript located in <strong>/share/WEB-INF/classes/alfresco/site-webscripts/org/alfresco/components/form</strong>. The file <strong>formValidation.js</strong> does not exist yet but we are about to create it. In order to upload content that is of our new product type, we need to make a minor change to the flash uploader to allow a selection list of content types. I had to change <strong>flash-upload.get.js</strong> and here is how I did it:<pre>/** * Custom content types */ function getContentTypes() { // TODO: Data webscript call to return list of available // types var contentTypes = [ { id : "dm:product", value : "dm_product" }, { id : "cm:content", value : "cm_content" } ]; return contentTypes; } model.contentTypes = getContentTypes();</pre>The contents of this file are used as an initialization for the flash upload component in Alfresco Share. The <code>contentTypes array contains objects that represent the content types that the flash uploader displays to the user when uploading content. If the array is larger than one then a selection list is displayed, if the array is exactly one then no option is given. The first value in the array is the default value. The value is an internationalized string that lives in a properties file. The dialog should look like this:Flash File Upload Dialog

Forms

There are three forms that we need to add for this project, one for the new product content type and two for the product datalist. These forms are added to /config/web-extension/share-config-custom.xml. Here is the config for the product content type:
<!-- the custom metadata from the demo content model for the dm:product type --> <config evaluator="node-type" condition="dm:product"> <forms> <form> <field-visibility> <show id="dm:productNumber" /> <show id="dm:productName" /> <show id="dm:productManufacturer" /> </field-visibility> <appearance> <field id="dm:productNumber" label-id="form.productNumber" mandatory="true"> <constraint-handlers> <constraint type="MANDATORY" validation-handler="Alfresco.forms.validation.populateProduct" event="keyup" /> </constraint-handlers> </field> </appearance> </form> </forms> </config>
This form will only display the three properties of the product content type and not any of the inherited properties of cm:content type. The productNumber field needs to be mandatory in order for the "mandatory" validation handler to fire on the form. The <code>validation-handlerattribute is the JavaScript function to call when the DOM event specified by the event attribute occurs. Since this is a textfield "keyup" makes sense, whenever a key is released in the textfield the <code>Alfresco.forms.validation.populateProduct function will be called. We need to add two new forms to edit the new product datalist. The first form is for editing the datalist items and the second is for creating the datalist items. The create form needs to use the "../data-lists/forms/dataitem.ftl" template.
<!-- the custom metadata from the demo content model for the dm:productList type (datalist) --> <config evaluator="node-type" condition="dm:productList"> <forms> <!-- Edit item form --> <form> <field-visibility> <show id="dm:productListNumber" /> <show id="dm:productListName" /> <show id="dm:productListManufacturer" /> </field-visibility> </form> </forms> </config> <!-- the custom metadata from the demo content model for the dm:productList type (datalist) --> <config evaluator="model-type" condition="dm:productList"> <forms> <!-- Create item form --> <form> <field-visibility> <show id="dm:productListNumber" /> <show id="dm:productListName" /> <show id="dm:productListManufacturer" /> </field-visibility> <create-form template="../data-lists/forms/dataitem.ftl" /> </form> </forms> </config>
When you create a new datalist item it will look like this:Create Datalist Item

Form Validation Handler JavaScript

The form validation handler script, which is called to enforce the "mandatory" attribute of the form field, is contained in /web/js/formValidation.js. Anything that is placed in the /webdirectory will be accessible client-side, in this example it will be deployed to /share/js. To create this file I started with Alfresco's Alfresco.forms.validation.mandatory function in<strong>/share/js/forms-runtime.js</strong>. This will give you the function signature and a good starting point. I threw out the "mandatory" handling code, but you may want to include it and add the lookup code into it. Here is what my function looks like:<pre>/** * Populate product validation handler based on another field, tests that the given * field has a value and queries via AJAX for associated product information. * * @method populateProduct * @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 * @param message * {string} Message to display when validation fails, maybe null * @static */ Alfresco.forms.validation.populateProduct = function populateProduct(field, args, event, form, silent, message) { if (Alfresco.logger.isDebugEnabled()) Alfresco.logger.debug("Updating product metadata from field '" + field.id + "'"); // Call this function on successful AJAX call // update the fields of the product based on the product number query var updateFields = function(res) { var result = eval('(' + res.serverResponse.responseText + ')'); if (result.name != null) { field.form.prop_dm_productName.value = result.name; } if (result.manufacturer != null) { field.form.prop_dm_productManufacturer.value = result.manufacturer; } }; // if our product number is not empty, do an AJAX query to get the product // for this product number // on failure, fail silently if (field.form.prop_dm_productNumber.value != null && field.form.prop_dm_productNumber.value != "") { Alfresco.util.Ajax.jsonGet( { url : Alfresco.constants.PROXY_URI + "tribloom/product/" + field.form.prop_dm_productNumber.value, successCallback : { fn : updateFields, scope : this }, failureCallback : { fn : function() {}, scope : this } }); } return true; };</pre>This code works by checking to see if the product number form field,<code>field.form.prop_dm_productNumber.value, is not null and has a value and then passing that value to an AJAX call to our repository webscript specified by the url parameter to <code>Alfresco.util.Ajax.jsonGet function, which is part of Alfresco core. The AJAX function also takes two callback functions as parameters, one for success and one for failure. On failure we don't want to do anything since there will often be incomplete and non-matching values in the product number form field as a user types in the value, so we pass an empty function, function() {}. On success we want to update the fields so we have defined the<code>updateFields function to do this. The updateFields function simply evaluates the JSON response from the repository webscript and sets the values of the product name and product manufacturer fields based on the results.

Wrap Up

To make this functionality work, we need to add a few items to our datalist. Create a new site if you don't already have one and navigate to the "Data Lists" tab. Create a new datalist, it doesn't matter what the name is, and add a few items.Create a Product Lookup ListProduct Lookup DatalistNow you can upload some new content in the "Document Library" tab. When you upload the content be sure that "Product" is the type of content you will upload. After you upload the content, edit the metadata from either the content details or the document library list view. In the product number field, enter in the same product number from one of the datalist items that you just created. If you did everything correctly, after you key the last character of the product number the product name and product manufacturer fields will populate with the information from your datalist item.Edit Product MetadataThe way that this project is written, the product number field is marked as mandatory with the "*" but the form does not enforce that the product number has a value to submit the form. This is because we are have replaced the mandatory form validation handler and removed the code that enforces that a value be entered in the field.

Here is a link to the sample Alfresco Share Customization Eclipse project for populating dynamic form data in Share.

Demo-Share-4


Loading Conversation