Conga Product Documentation

Welcome to the new doc site. Some of your old bookmarks will no longer work. Please use the search bar to find your desired topic.

Show Page Sections

External Value Selector

Each product configuration is represented as a tree of forms, each one containing either a list of form properties or a list of subforms. Each form property represents a question to which the client can, or must, answer. Depending on the value type, the configuration administrator can parametrize the way values will be displayed to the client. A list of predefined value selectors is present – starting from simple edit fields and combo box, including some advanced value selectors, which represent a list of graphical objects and which work with JavaScript.

Note: For more information related the standard value selectors please refer to CPQ Configurator Customization Guide

This chapter is dedicated to the creation of a special kind of external components, called external value selectors. The main purpose of value selectors is to give to the user a way to answer to form property questions, to collect answers to these questions and to send them to the server.

Each value selector has two separate views – collapsed and expanded.

Collapsed View example:

Box with title “Caravan models” is the first (and only) form box currently displayed inside the configuration box. In the form box there are four form property boxes. The first form property box, named “Select range”, is the form box property in which the external value selector is shown.

On this figure all form property boxes are collapsed. So the collapsed external value selector can be seen – red border on the figure.



Expanded View example:

The user can expand the form property “Select range” by clicking on the arrow image on the right angle of the form property box. The expanded view of the external value selector becomes visible –

marked with red border in the following figure:



To achieve two separate views for the value selector, the form property inserts two separate tiles (like separate instances) of the external value selector component – one for collapsed and another one for expanded view.

The developer can ask the Configurator UI Service Layer for its collapsed/expanded state and decide what information to prepare for the view and what to display in the JSP. It’s up to the external developer to split the processing depending on the collapsed/expanded state in both controller and JSP.

In this example, the following separation depending on the state is present:

  • Collapsed view - contains a simple combo box. When a new element is selected, submit to the server is done.
  • Expanded view - the external value selector contains two buttons (left and right arrow images), which are used to change the currently selected element. All possible elements are shown as images in the list of images below the buttons. The selected element is highlighted via a special style. If no selection is yet given, the default value is taken from the engine.

Warning: Recommendations:

  • A value selector must support the valuation type of the form property:
    • Simple valuation type – only one answer can be given to a form property.
    • List valuation type – several answers can be given to a form property.
    • If you want to use a value selector that supports a rich graphical layout, the formProperty of your model must contain the corresponding RMO (Rich Media Object)

The external value selector implemented in this chapter is designed to be used with simple valuation Form Properties.

Setting the development environment up



The following libraries are needed:

  • Internal – supplied by PROS:
    • externalComponentsFramework.jar
      • ConfiguratorUI service layer
    • ConfiguratorEngine.jar
      • Configurator Engine layer
    • business.jar
      • configuration engine public business objects
    • framework.jar
      • utility library
  • External – third parties libraries:
    • struts.jar
      • Struts web GUI framework
    • javax.servlet.jar
      • servlet API, used by Struts
    • log4j.jar
      • logging facility
    • commons-beanutils.jar
      • Apache commons bean utilities
    • jboss-aop-jdk50-client.jar
      • AOP facilities
        Note: There is as additional folder inside "pages" – folder "js" to hold the JavaScript file for the external selector.

Creating the Controller

HOW TO CREATE THE BEAN FOR THE JSP?

The controller of the external value component will create a single DynaBean, which will be passed to the JSP by using the tile’s component context. The dynabean is saved under the property name “valueSelectorBean”.

It contains several properties:

  • selectedValueId – the ID of the currently selected value. The JSP uses this ID to put the selected CSS class name as class attribute to the current selection
  • qId – the CPE of the form property in which the value selector is included
  • collapsed – flag indicating the collapsed/expanded state of the component
  • formPropertyId – the unique id of the form property tile, which contains the value selector. It’s used in the JavaScript initialization of the value selector
  • commentDivId - each form property can be parametrized in the model to have an associated comment. If so, then the form property box inserts not only the two value selector tile (expanded and collapsed), but also a text area for the comment. Its HTML ID is given to the value selector since it’s needed in the JavaScript initialization.
  • tileId - unique id associated to the value selector; It’s used as the HTML ID of the

<

div>, containing the value selector’s JSP file - description – description of the external value selector; displayed as title - imageLeft – the source for left arrow image - imageRight – the source for right arrow image - initialCPE – the id of the selected value - initialDescription – the description of the selected value - values – collection of beans, each holding properties for value’s id, description, RMO image source, RMO description, and style or css class in case this is the selected value;

Tip: Since the collapsed view is much simpler than the expanded one, the code is branched depending on the component state. For example, resources for images, as well as description translations, are only extracted from the engine in case of expanded view generation.

publicclass ExternalValueSelectorController extends ExternalControllerAbs {

/**

*{@inheritDoc}

*/

protectedvoid process(ComponentContext tileContext,

HttpServletRequest request, HttpServletResponse response,

ServletContext servletContext) {h

ConfiguratorUIService ConfiguratorService = (ConfiguratorUIService)request.getSession()

.getAttribute(ConfiguratorUIService.CONFIGURATOR_UI_SERVICE;

DynaBean valueSelectorBean = new LazyDynaBean();

boolean isCollapsed = ConfiguratorService.isCollapsed(tileContext);

valueSelectorBean.set("collapsed", isCollapsed);

addSettingsParameters(tileContext, ConfiguratorService,

valueSelectorBean);

if(!isCollapsed)

// only needed in expanded view

addResources(ConfiguratorService, tileContext, valueSelectorBean);

List<DynaBean> valuesList = getFormPropertyElements(tileContext,

ConfiguratorService, valueSelectorBean, isCollapsed);

valueSelectorBean.set("values", valuesList);

tileContext.putAttribute("valueSelectorBean", valueSelectorBean);

}

//TODO : Add helper functions here

}

First the Configurator UI Service Layer instance is taken from the session. It is used in the addSettingsParameters () function to extract translations:

privatevoid addSettingsParameters(ComponentContext tileContext, ConfiguratorUIService ConfiguratorService, DynaBean selectorBean)privatevoid addSettingsParameters(ComponentContext tileContext,

ConfiguratorUIService ConfiguratorService, DynaBean selectorBean) {

selectorBean.set("qId",

ConfiguratorService.getComponentObjId(tileContext));

selectorBean.set("commentDivId",

ConfiguratorService.getCommentId(tileContext));

selectorBean.set("formPropertyId",

ConfiguratorService.getFormPropertyId(tileContext));

selectorBean.set("tileId", ConfiguratorService.getTileId(tileContext));

}

These are settings common for the two views. Their usage will be described in details in the JSP implementation part.

Next the resources and translations for expanded view are added to the bean – only in case the current tile is in expanded mode.

Note: For more information, please refer to: How to get string literals translations?

privatevoid addResources(ConfiguratorUIService confService, ComponentContext

tileContext, DynaBean selectorBean) {

Properties externalVSProperties = confService.getComponentProperties(tileContext);

String titleKey = externalVSProperties.getProperty("title");

selectorBean.set("title", confService.getTranslation(titleKey));

String imageKeyRight = externalVSProperties.getProperty("imageKeyRight");

selectorBean.set("imageRight", confService.getGraphicResource(imageKeyRight));

String imageKeyLeft = externalVSProperties.getProperty("imageKeyLeft");

selectorBean.set("imageLeft",

confService.getGraphicResource(imageKeyLeft));

}

The main functionality is separated in the function getFormPropertyElements(), which creates a list of dynabeans. Each bean describes a single value – its Id, image and description.

private List<DynaBean> getFormPropertyElements(ComponentContext tileContext,

ConfiguratorUIService ConfiguratorService,

DynaBean valueSelectorBean,

boolean isCollapsed) {

List<DynaBean> resultList = new ArrayList<DynaBean>();

ConfiguratorClient confEngine =

ConfiguratorService.getEngine();

FormProperty<?, ?> value = null;

try {

String cpe = ConfiguratorService.getComponentObjId(tileContext);

value = (FormProperty<?, ?>)

confEngine.getObjectByCPE(CPEBuilder.stringToCPE(cpe));

List<?> values = ((ExtensionalDomain<?>) value.getValueField()

.getDomain()).getContent();

boolean isFirst = true;

String selectedId = getSelectedValueId(ConfiguratorService,

tileContext);

for (Object obj : values) {

if (obj instanceof ObjectValue) {

ObjectValue propertyValue = (ObjectValue) obj;

DynaBean result = createObjectValueBean(propertyValue,

confEngine, selectedId, isFirst, valueSelectorBean, isCollapsed);

resultList.add(result);

isFirst = false;

}

}

} catch (CameleonException e) {

thrownew RuntimeException(e);

}

return resultList;

}

In the function above, the Configurator engine instance is obtained through the Configurator Service UI layer instance. The Configurator Service UI layer instance is also used to get the CPE of the form property, for which the current value selector tile is inserted. Then the selected value for this form property is taken from the engine – in the helper function getAnswer().The function below is called for each value extracted from the engine:

private DynaBean createObjectValueBean(ObjectValue propertyValue,

ConfiguratorClientSvc confEngine, String selectedId, boolean isFirst,

DynaBean valueSelectorBean, boolean isCollapsed)

throws CameleonException {

DynaBean valueBean = new LazyDynaBean();

PK valuePK = propertyValue.getValue();

PK valueID = valuePK.getId();

valueBean.set("valueID", valueID.toString());

BusinessValue val = getValue(valuePK, confEngine);

String descr = val.getDescription();

valueBean.set("itemDescription", descr);

RichMediaObject rmo = null;

rmo = val.getRichMediaObject();

if (!isCollapsed && rmo != null) {

List<MMOImage> mmoImageList = rmo.getImages();

valueBean.set("mmoImage", mmoImageList

.size() > 0 ? mmoImageList.get(0)

.getCurrentImage() : "");

List<MMOText> mmoTextList = rmo.getTexts();

valueBean.set("mmoText",

(mmoTextList.size() > 0 && mmoTextList.get(0)

.getText() != null) ? mmoTextList

.get(0).getText()

: "");

}

String valueIdStr = valueID.toString();

boolean selected = valueIdStr.equals(selectedId) || (selectedId == null&&

isFirst);

if (!isCollapsed && selected) {

valueSelectorBean.set("initialCPE", valuePK.getId());

valueSelectorBean.set("initialDescription", descr);

}

if(isCollapsed)

valueBean.set("selected", Boolean.valueOf(selected));

else

valueBean.set("class", selected ? "extImageItemSelected" :

"extImageItemNotSelected");

return valueBean;

}

HOW TO IMPLEMENT THE REFRESH MECHANISM?

No registration for tile refresh is needed for value selectors since the form properties, in which they are present, are registered and will refresh automatically.

Warning: Value selectors must always be refreshed as part of the form property boxes, in which they reside.

Creating the value selector JavaScript



The form property box’s buttons are unaware of the representation of the data in the value selectors – for example the collapsed view can be an input field or a combo box. So the ‘Ok’ button cannot know how to take the ID of the currently selected value in order to submit it since the value selector representation varies and the value must be extracted in an implementation-specific way.

In order to deal with this issue, a contract is created. Each value selector must create an object (JavaScript class, subclassing ValueSelectorAbs class). Then the form property box can ask

the ValueSelectorAbs object to return the selected values, entered quantities and etc. The form property will search for the given value selector’s ValueSelectorAbs object inside a global object, called “valueSelectors”. This global object is created by the first form property JSP inserted in the page so each value selector can use it freely.

The name of the property in the valueSelectors object, under which the form property will expect to find the ValueSelectorAbs object for given value selector, is the tile ID of the form property itself.

This ID is put in the context of each value selector and can be extracted with a service of

the Configurator UI Service Layer.

Since each form property has two selectors, it’s needed to differentiate between

the ValueSelectorAbs objects for the collapsed and for the expanded view. Indeed, if the collapsed view is currently shown to the client, this view will be used to submit data when the user clicks on “Ok”. The way to distinguish the two ValueSelectorAbs objects is to create two separate properties under the global valueSelectors object – one for expanded view and one for collapsed view.

Note: The naming policy is as follows:

valueSelectors[formPropertyTileId] – holds the value selector object for expanded view

valueSelectors[formPropertyTileId + "_collapsed"] – holds the value selector object for collapsed view

In order for this naming contract to be achieved, each value selector can ask the service layer for the ID of the form property in which it is included. This way the value selector can create its value selector object under the correct property of the valueSelectors object.

These value selector instances are also used to synchronize values between both views. For example, if the user changes the selection in expanded view and, without submitting the new value to the server, the user switched to collapsed view, the newly selected value must be set into the collapsed view. This is achieved by a function, attached to the button collapsing the value selector. This button not only hides the expanded view and makes visible the collapsed view but it also gets the currently selected value from the expanded view and sets it as current in the collapsed view.

COLLAPSED VIEW

Creation of the corresponding property of the valueSelectors global object is under the responsibility of the JSP file. But the type of create object must be defined in a JavaScript file.

In the current example, the collapsed view is a simple one – a combo box.

A ValueSelectorAbs object dealing with combo box is already present in the ConfiguratorUI and can be reused. It corresponds to the JavaScript

class CollapsedSimpleValueSelector from ConfiguratorUI.war/js/: valueSelectors.js.

It is initialized with the html ID of the selected elements (as generated by the external value selector

JSP itself), and with the ID of the comment text area (as given from the form property itself). Once created, this object will supply a method, which can extract the selected value ID from the HTML, select an element with its HTML ID which is given to its constructor.

The same applies for the other required functions –getValue, setValue, etc.

EXPANDED VIEW

The situation is different with the expanded value selector. Since its HTML markup is new to the project, there is no ready ValueSelectorAbs subclass, which can deal with it. Such a class has to be developed. Development starts with the creation of a JS file under the project folder “devel/js” - named externalValueSelector.js:

/* External Value Selector Demo */

ExternalValueSelectorDemo = function(inputId, qtyId, qId, commentDivId, tileId){

this.init(qId, commentDivId);

this.tileId = tileId;

this.input = document.getElementById(inputId);

this.initialValue = this.input.value;

this.qty = document.getElementById(qtyId);

if (this.qty) {

this.initialQty = this.qty.value;

}

}

ExternalValueSelectorDemo.prototype = new ValueSelectorAbs;

ExternalValueSelectorDemo.prototype.getUrlParams = function() {

var result = "selector=external&answer[0]=" +

encodeURIComponent(this.input.value);

if (this.qty) {

result = result + "&qty[0]=" + this.qty.value;

}

return result;

}

ExternalValueSelectorDemo.prototype.setInitialValue = function() {

this.input.value = this.initialValue;

}

ExternalValueSelectorDemo.prototype.setValue = function(arguments) {

this.input.value = arguments['value'];

if (this.qty) {

this.qty.value = arguments['quantity'];

}

}

ExternalValueSelectorDemo.prototype.getValue = function() {

var qtyVal = '';

if (this.qty) {

qtyVal = this.qty.value;

}

return {'value': this.input.value,

'quantity': qtyVal};

}

The main purpose of each value selector class is to supply methods for getUrlParams, getValue, setValue, getValueSelectorControls.

ValueSelectorAbs has a constructor with two parameters – qId (string representation of the form property’s CPE) and the commend HTML ID.

Warning: It’s mandatory to call the constructor of the base class as first line of the new subclass’ constructor:

this.init(qId, commentDivId);

It is also mandatory to assign an instance of the base class to the prototype property of the new value selector class as shown:

ExternalValueSelectorDemo.prototype = new ValueSelectorAbs;

When calling a method (or a property) of the new value selector’s instance and no method is defined,

the prototype object will be checked to see if such method (or property) is present there. If yes – it will be called.

getUrlParams function

The purpose of this function is to supply a string containing URL parameters – concatenated to one another with the “&” sign. Each value selector must put at least one parameter – “answer[0]=”.

Tip: If the value selector is of type that allows more than one values to be selected, all values must be supplied as follows:

“answer[0]=val0&answer[1]=val1&answer[2]=val2”.

Here val0, val1 and val2 are the IDs of the values the user has selected or the values themselves if they are expected to be directly filled by the user. The naming policy, used for parameter names is the same as for indexed JavaBean properties.

Tip: If quantity can be entered for the values, then they must also be appended to the string: ”qty[0]=2&qty[1]=1&qty[2]=1”

The function getUrlParams() is called when the value selector is asked to submit its value – the function submit() is called. This function is defined in the ValueSelectorAbs. It’s responsible for creating the Ajax request by using the URL parameters, returned by the value selector

object’s getUrlParams function. It also adds the text in the comment field if such is present. So no special treatment is needed for the comment.

Example: "answer[0]=AC%2FBVAL%2FANTARES%20LUXE"

getValue/setValue functions

When a switch is done between collapsed and expanded view, the currently selected value in the visible view must be set in the other view.

For example, let assume that the collapsed view is displayed with the first value selected by default. The user clicks on the expand button in order to select from the list of images that the external value selector’s expanded view shows. Then the user selects the second value but doesn’t click on “Ok”.

Instead he clicks on the collapse button to return to the collapsed view. It’s needed to change the selected value in the collapsed view – to set it to the second value as it was selected in the expanded view.

The following figure shows how the synchronization mechanism is put in place to guarantee the consistency between collapsed and expanded view:



The getValue function should return such a value that can be used in the corresponding setValue function.

GetValueSelectorControls, focusSelector and blurSelector functions

This function is part of the keyboard navigation framework of the ConfiguratorUI. The essence of the framework is to allow the user to iterate over each control on the page with the keyboard only.

Tab and Shift+Tab are used for switching to the next/previous control. The function receives no parameters and must return an array of objects. Each of them is an html node, which can receive the focus. When Tab is pressed for example, the framework will iterate in the collection and will call another method from the ValueSelectorAbs class – focusSelector. The focusSelector function will be called for each control returned by the external selector’s getValueSelectorControls function. It can be overridden by the external component developer in order to make some specific selection action – like changing the CSS class name of the selected element.

On the same time, the blurSelector() function is called on the previously selected control. Here again the external component developer can override it in order, for example, to remove the CSS class name, marking the value as current.

Registering in the keyboard navigation framework

HOW TO USE STANDARD KEYBOARD EVENT MANAGER?

The standard keyboard event management consists in navigating between “focusable” HTML object with Tab and Shift+Tab events. Mechanism is explained in GetValueSelectorControls, focusSelector and blurSelector functions

If the external component needs to handle different keys – like left and right arrow to select images, functions must be developed which to execute on these events. And the functions need to be registered in the ConfiguratorUI framework.

HOW TO CREATE A CUSTOM KEYBOARD EVENT MANAGER?

If the standard keyboard event management solution is not convenient, it is possible to develop a custom keyboard event manager. getValueSelectorControls, focusSelector and blurSelector functions must be overridden in order to register their own event manager.



The developer of the external value selector has to create a class having the following canvas:

  • Creation of a JS file under the project folder “devel/js” - named ExternalValueSelectorDemoEvent.js:
  • The main purpose of the initialize function is to store any useful attribute returned by the focusSelector function and to initialize the event management attributesrequiredby the framework.
  • The processEvent function receives the event when a key is pressed and it is the developer’s responsibility to implement the behavior which is expected for a given event.
  • Check the implementation example of externalValueSelectorDemoEvent.js.

    var ExternalValueSelectorDemoEvent = Class.create(); ExternalValueSelectorDemoEvent.prototype = {

    /**

    *Constructor

    *@paramtileId:string

    *@paramselector:valueSelectorObject

    */

    initialize: function(tilesId, selector) {

    /**Parameters need to be stored as class attributes */

    this.initEventManagement();

    },

    /**

    *Initializetheeventmanagement:

    *enabletheeventsupport

    */

    initEventManagement: function(){

    this.isSupportEventProcessing = true;

    this.toggle = true;

    },

    /**

    * Function called when key pressed

    * Process here the custom behaviour

    * @paramevent:Event currently processed

    */

    processEvent: function(event){

    }

    };

HOW TO REGISTER THE CUSTOM KEYBOARD EVENT MANAGER?

When the focus is on the ExternalValueSelector (either via a user action or automatically delegated by another action from the ConfiguratorUI), the focusSelector method is called.

As soon as the developer creates his event manager, he has to instantiate and pushes him in the ConfiguratorUI keyboard navigation framework. This step has to be written in the focusSelector function.



ExternalValueSelectorDemo.prototype.focusSelector = function(valueSelectorControl) {

varmyKeyboardEventManager = newExternalValueSelectorDemoEvent(this.tilesId, this.formPropertyId, this.maxElt);

EventManager.pushFocusedComponent(myKeyboardEventManager);

}

Pushing the event manager allows to store the ExternalValueSelectorDemoEvent instance at the first position of the event managerframework stack.

When a key is pressed, the ConfiguratorUI retrieves the first event manager instance of the stack and executes the related processEvent function.



HOW TO UNREGISTER THE CUSTOM KEYBOARD EVENT MANAGER?

In order to keep the stability of ConfiguratorUI keyboard navigation, the developer needs to disable his customized keyboard navigation when the focus leaves the externalValueSelector.

Like for the registering process, when the focus leaves the externalValueSelector, the blurSelector function is called. At this time, the developer has to remove the event manager from the ConfiguratorUI navigation framework stack.



ExternalValueSelectorDemo.prototype.blurSelector = function(valueSelectorControl)

{

var eventManager = EventManager.getFocusedComponent();

if((eventManager != null) && (eventManager instanceof ExternalValueSelectorDemoEvent)){

EventManager.popFocusedComponent();

}

}

The developer needs to ensure that the object he is picking from the stack is an instance of the class he has created.

Creating the View

HOW TO DESIGN THE JSP?

The file extComponentSample.jsp is copied in the project.

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<%@ taglib uri="/tags/struts-tiles" prefix="tiles"%>

<%@ taglib uri="/tags/struts-logic" prefix="logic"%>

<%@ taglib uri="/tags/struts-bean" prefix="bean"%>

<%-- Error handling --%>

<%@ include file="/WEB-INF/tiles/handleError.inc"%>

<%-- Error handling --%>

<tiles:importAttribute name="exception" ignore="true"/>

<%-- Display any content only if there is no exception raised from Controller class --%>

<logic:empty name="exception">

<tiles:useAttribute name="valueSelectorBean"/>

<logic:equal name="valueSelectorBean" property="collapsed" value="true">

<%-- TODO: Add collapsed view here --%>

</logic:equal>

<logic:notEqual name="valueSelectorBean" property="collapsed"

value="true">

<%-- TODO: Add expanded view here --%>

</logic:notEqual>

<script type="text/javascript">

function init<bean:write name="valueSelectorBean"

property="tileId"/>() {

<logic:equal name="valueSelectorBean" property="collapsed"

value="true">

<%-- TODO: Add initialization of collapsed view --%>

</logic:equal>

<logic:notEqual name="valueSelectorBean" property="collapsed"

value="true">

<%-- TODO: Add initialization of expanded view --%>

</logic:notEqual>

// Indicate than the value selector is loaded

syncValueSelector = true;

}

initValueSelector(init<bean:write name="valueSelectorBean"

property="tileId"/>);

</script>

</logic:empty>

The JSP consists of two separate sections – one for rendering the HTML in case of collapsed view, another rendering the HTML in case of expanded view. The file also contains two JavaScript sections.

The JS files needed to handle the valueSelector tile have to be stored under the ‘js’ directory. They will be automatically imported when the JBOSS starts.

The JavaScript section defines an initialization function and passes its name to the initValueSelector() framework function.

Tip: In order for this function to have a unique name, the name includes the tile’s id:

init<bean:write name="valueSelectorBean" property="tileId"/>

An example function name is “inittile47dt1213892801859”.

The function name is passed to the framework using the initValueSelector function so that the

function can check if the tile is loaded during a full page rendering or during an Ajax call. If the tile is part of a full page rendering, then the initialization function is attached to the “onload” event of the page. If this is an Ajax request, the function is called immediately.

The syncValueSelector is a variable that permit to ConfiguratorUI to know if the value selector is loaded. This variable is useful for internal focus delegation management.

syncValueSelector = true;

Collapsed view

Since the value selector is intended for simple valuation type of form properties, i.e. which expect a single value to be selected, the collapsed view is a simple combo box:

<select class="externalInputs"

id="<bean:writename="valueSelectorBean"property="tileId"/>_select"

onchange="valueSelectors['<bean:writename="valueSelectorBean"

property="formPropertyId"/>_collapsed'].submit()"

onFocus="valueSelectorOnEnter('<bean:writename="valueSelectorBean"

property="formPropertyId"/>', this, 'focus')">

<logic:iterate name="valueSelectorBean" property="values" id="value">

<option value="<bean:writename="value"property="valueID"/>"

<logic:equalname="value"property="selected"value="true">

selected="selected"

</logic:equal>

>

<bean:write name="value" property="itemDescription"/>

</option>

</logic:iterate>

</select>

One HTML select element is created per option values in the form property. Inside each option a check is done to see if this is the currently selected value. If so, the selected=”selected” attribute is put on this option.

A unique HTML ID is generated for the select element – the external value selectors tile ID, to which the string “_select” is concatenated. Example: “tile37dt1214203769843_select”. This ID will be used

in the JavaScript initialization.

Two event listeners are given in the select definition – onchange and onfocus. The onchange event is designed to submit the value selector. This is done using the ValueSelectorAbs instance. It is accessed as property on the global valueSelector object, which property is the form property HTML ID – the place in which it is created during the JavaScript initialization section.

The submit() function must be called. The onfocus event listener is part of the ConfiguratorUI framework. Each value selector notifies the framework that it gains the focus by calling the function valueSelectorOnEnter.

Aside from the markup, the collapsed view also includes JavaScript initialization code. The body of the function init<tileId>, which is generated in order to initialize the value selector, is separated into two parts. In the case of collapsed view, the following code is needed (example):

valueSelectors["<bean:write name="valueSelectorBean" property="formPropertyId"/>_collapsed"] =

new CollapsedSimpleValueSelector(

"<bean:write name="valueSelectorBean" property="tileId"/>_select",

"",

"<bean:write name="valueSelectorBean" property="qId"/>",

"<bean:write name="valueSelectorBean" property="commentDivId"/>"

);

initValueSelector(init<bean:write name="valueSelectorBean" property="tileId"/>);

A predefined value selector JavaScript class is used – the class CollapsedSimpleValueSelector. It’s constructed with four parameters:

  • HTML ID of the select element from which to extract the selected ID;
  • HTML ID of the quantity input field – empty in the current value selector since it doesn’t supply a way to enter quantity and no such input is present; if quantity input was rendered, its HTML ID would be presented here. Possible ID for the quantity field is tilesId with concatenated "_qty" for example;
  • question Id – the string representation of the form property’s CPE;
  • HTML ID of the comment text area – this ID is supplied to the external component by the form property which inserts the tile; it is taken with the help of the service in the Configurator UI service layer;

The newly created CollapsedSimpleValueSelector object is put inside the property with name (formPropertyId + “_collapsed”) – here is where the form property will search for the JS object in case it needs to call some method on it.

Expanded view

When expanded, the external value selector is represented as a list of images and two buttons for changing the current selection:

<div class="externalDescription">

<bean:write name="valueSelectorBean" property="title"/>

</div>

<div>

<table border="0">

<tr>

<td>

<img

src="<bean:writename="valueSelectorBean"property="imageLeft"/>"

selector="selector"

onclick="selectPrevElement('<bean:writename="valueSelectorBean"property="tileId"/>')

;

valueSelectorOnEnter('<bean:writename="valueSelectorBean"property="formPropertyId"/>'

, this, 'focus')"/>

<img

src="<bean:writename="valueSelectorBean"property="imageRight"/>"

selector="selector"

onclick="selectNextElement('<bean:writename="valueSelectorBean"property="tileId"/>')

;

valueSelectorOnEnter('<bean:writename="valueSelectorBean"property="formPropertyId"/>'

, this, 'focus')"/>

</td>

<td>

<input type="hidden"

id="<bean:writename="valueSelectorBean"property="tileId"/>_input"

size="50" class="externalInputs"

value="<bean:writename="valueSelectorBean"property="initialCPE"/>"readonly="readonly "/>

<input type="text"

id="<bean:writename="valueSelectorBean"property="tileId"/>_description"

size="50" class="externalInputs"

value="<bean:writename="valueSelectorBean"property="initialDescription"/>"

readonly="readonly"

onFocus="valueSelectorOnEnter('<bean:writename="valueSelectorBean"property="formProp ertyId"/>', this, 'focus')"/>

</td>

</tr>

</table>

<table id='<bean:writename="valueSelectorBean"property="tileId"/>_table'>

<tr>

<logic:iterate name="valueSelectorBean" property="values" id="value">

<td class="<bean:writename="value"property="class"/>">

<img src="<bean:writename="value"property="mmoImage"/>"/>

<input type="hidden" value="<bean:writename="value"property="valueID"/>"/>

<input type="hidden" value="<bean:writename="value"property="itemDescription"/>"/>

</td>

</logic:iterate>

</tr>

</table>

</div>

Expanded view consists of two tables – one for buttons and inputs, and another one hosting all images for values. There is one hidden input, which holds the selected value’s ID, while the visible

one holds the display name of the value but it isn’t allowed to be modified. The way to change the value is by clicking on one of the images in the second table.Both images use JavaScript functions, executed when the user clicks on them – selectNextElement() and selectPrevElement (). These functions are responsible for finding the currently selected table cell in the table of values and marking as selected the previous/next table cell. They also update the description input and put the new value ID in the hidden input. After the execution of the appropriate selection function in

the onclick event of the arrow image, valueSelectorOnEnter() is called as it was done in the collapsed view.

Then iteration is done over each value and a cell in the table of values is created for it. The cell contains an image and two hidden fields. The first field is the ID of the value. This value ID will be submitted as “answer[0]” parameter if the value is selected. The second field contains the value description.

HOW TO SET A MANUAL FOCUS ON AN EXTERNAL VALUE SELECTOR?

Focus mechanism

Putting the focus on a value selector has to be obvious for the end-user. It is used to symbolize which object is the current one. The next action for example changes the position of the focus from the current value selector to the next one.

The JS function used to put the focus on a valueSelector is the valueSelectorOnEnter() function. Mechanism of the function:

  • First, the focus is set on the parent form property. The form property becomes current.
  • Then, the focus is propagated to the value selector. It means that the focusSelector () method of the valueSelector is called and it executes the appropriate behavior (focus an HTML element, create and register a keyboard navigation manager…).

Case to be managed by the developer

There is a kind of end user action that the ConfiguratorUI cannot manage. The developer has to consider the fact that if a user clicks/selects an element contained in his externalValueSelector and in the meantime the focus is not currently on the parent form property, he needs to manually change

the focus before performing any action.



Let’s check the following example:

  1. FP2 is currently focused.
  2. User clicks on the leftArrow element
  3. The standard behaviour would be that as the user has clicked on leftArrow which is embedded in FP1, then FP1 becomes the current formProperty and the focus is removed from FP2 and put on FP1.

The developer has to identify which elements allow to restore the focus and to determine the current formProperty/valueSelector. The following piece of code shows how it is managed for the externalValueSelector sample.

In the following example of the Expanded, the two buttons left and right arrows (represented by the tags) are the elements which have be chosen to put the focus on the form Property.

<td>

<img

src="<bean:writename="valueSelectorBean"property="imageLeft"/>"

selector="selector" onclick="selectPrevElement('<bean:writename="valueSelectorBean"property="tileId"/>'); valueSelectorOnEnter('<bean:writename="valueSelectorBean"property="formPropertyId"/>'

, this, 'focus')"/>

<img

src="<bean:writename="valueSelectorBean"property="imageRight"/>"

selector="selector"

onclick="selectNextElement('<bean:writename="valueSelectorBean"property="tileId"/>')

;

valueSelectorOnEnter('<bean:writename="valueSelectorBean"property="formPropertyId"/>'

, this, 'focus')"/>

</td>

Two important things here; Calling the valueSelectorOnEnterfunction and setting the selector=”selector” tag attribute.

The valueSelectorOnEnter function allows to set the focus on the parent form property. Then, the form property delegates the focus to the value selector.

valueSelectorOnEnter('<bean:writename="valueSelectorBean"property="formPropertyId"/> ', this, 'focus')"

The HTML generated is: valueSelectorOnEnter ('tile56', this, 'focus')

ValueSelectorOnEnter parameters:

  • First parameter represents the tileId of the current form property given by the ConfiguratorUI service layer.
  • Second parameter is the current HTML element. This element is passed to

    the focusSelector function if needed. This element (passed as a parameter) must have an attribute selector=”selector”.

  • Third parameter is the type of event we want to apply: ‘focus’.

If the selector attribute is missing, the focusSelector method of the valueSelector is not executed and then neither the focus nor the keyboard management are restored for the valueSelector.

HOW TO ASSEMBLY THE TILE?

The external value selector’s tile-defs.xml is:

<?xml version="1.0" encoding="ISO-8859-1" ?>

<tiles-definitions>

<!-- Tiles for the External Value Selector Demo -->

<definition name=".externalValueSelector_simple"

path="/externalComponents/valueSelector/jsp/externalValueSelector.jsp"

controllerClass="com.cameleon.demo.tiles.ExternalValueSelectorController">

</definition>

</tiles-definitions>

Tip: The tile’s name has a postfix “_simple”, which will be explained in details in the following chapter.

Packaging and deploying the component

Note: The advanced external component is packaged and deployed in the same way as the basic external component: Packaging and deploying the component

The only difference with the basic component is the name of the page’s folder under which the public resources of the component are located – The directory is:

cameleonUI.war/externalComponents/externalValueSelectorDemo.

For additional JS script, all JS files contained in the JS directory are automatically imported in the head of the ConfiguratorUI pages during the external components deployment.

Running the new component

In the ConfiguratorUI.xml model, the external value selector can be parametrized:

<externalValueSelector name="RollingValueSelector"tile=".externalValueSelector"

cssName="vtExtValSel">

<param name="imageKeyLeft" value="extValSel.imageKeyLeftUrl"/>

<param name="imageKeyRight" value="extValSel.imageKeyRightUrl"/>

<param name="title" value="extValSel.title"/>

</externalValueSelector>

The value of the ‘tile’ attribute should match the ‘name’ attribute in the definition tag of the tiles-defs.xml, which is supplied with the external value selector. Each selector can have different views depending on the valuation of the form property. To achieve this, the ConfiguratorUI expects that if an external component with name “.externalValueSelector” is parameterized for a given value selector type (rolling selector in the example above), then the following tiles are expected as part of the component:

  • “.externalValueSelector_simple” – this tile will be used if the value selector type is of type “RollingValueSelector” and valuation type of the current form property being displayed is SIMPLE_VALUE;
  • “.externalValueSelector_list” - this tile will be used if the value selector type is of type “RollingValueSelector” and valuation type of the current form property being displayed is LIST_VALUE;
  • “.externalValueSelector_matrix” - this tile will be used if the value selector type is of type “RollingValueSelector” and valuation type of the current form property being displayed is MATRIX_VALUE;
    Tip: The external component should provide all the three tiles: simple, list and matrix.

Indeed, if a form property is from valuation type which does not have a corresponding external value selector, an exception will be raised.

The “cssName” is used to specify the CSS file and Class to be associated with the tile:

<layout>

<localizedLayout lang="EN,FR">

<!-- ... -->

<componentStyleSheet>

<!-- ... -->

<css name="vtExtValSel">externalComponents/valueSelector/css/externalValueSelector.css</cs s>

</componentStyleSheet>

</localizedLayout>

</layout>

The value of each translation parameter must correspond to a key in the custom translations section of the XML, to which key a translation is mapped:

<translations>

<localizedTranslations lang="EN,US,IT,ES,GE">

<genTranslations>

<!-- translations used on multiple places -->

</genTranslations>

<customTranslations>

<translation name="extValSel.title">External Value Selector</translation>

</customTranslations>

</localizedTranslations>

</translations>

Each resource parameter value must correspond to a key in the localized resources section in the XML:

<resources>

<localizedResources lang="FR,EN,US,IT,ES,GE">

<resource name="extValSel.imageKeyLeftUrl">externalComponents/valueSelector/images/previous_off

.gif</resource>

<resource

name="extValSel.imageKeyRightUrl">externalComponents/valueSelector/images/next_off.g if</resource>

</localizedResources>

</resources>

Translations for external components should be put in the customTranslations section of the XML since they’re specific for external components only. Each translation should have unique key within the custom translations section – for example, it could start with the name of the component.