For a form in Alfresco share, I want a dropdown box that is filled with custom options depending on the value of a field earlier up in the form.
My form would have at least two fields. The first one a textbox, where a unique code must be entered. When that is done, the second one, a select box, must load it's options using the entered code.
The data backing this requirement is stored in a Data list. I have also made it available through a webscript (along the lines of /getOptions/{uniqueCode
, returning a JSON array of the valid options.
Now, I am a bit stuck on how to build the part of the form that will watch for status changes on the code textfield, and reload the dropdown box. I can think of some javascript, but I don't even know where to start changing/adding files.
I've looked through the FDK
, where I found the selectone ftl. Unfortunately, this supports only fixed options.
My implementation based on my chosen answer
This is very similar to what I was already doing, I had hoped to be able to do this on the server side, without including the extra round-trip. So far, this is the best I have though.
share-config-custom.xml
I define the form here, and point the property I want to be my selectone to my own custom field template. I pass a parameter ds
to it, dataSource
, which holds the path to my webscript.
<config evaluator="node-type" condition="my:contentType">
<forms>
<form>
<field-visibility>
<show id="my:code" />
<show id="my:description" />
</field-visibility>
<appearance>
<set id="general" appearance="bordered-panel" label="General" />
<field id="my:description" set="general">
<control template="/org/alfresco/components/form/controls/customSelectone.ftl">
<control-param name="ds">/alfresco/service/mark/cache/options</control-param>
</control>
</field>
</appearance>
</form>
</forms>
customSelectone.ftl
My custom ftl has three major steps. First, it receives the ftl parameter I passed from share config custom and assigns it to a local variable. Then it places a html <select>
box as a field, and finally, it executes a call to my webscript for the possible options.
Parameter
<#if field.control.params.ds?exists><#assign ds=field.control.params.ds><#else><#assign ds=''></#if>
html
<style type="text/css">
#${fieldHtmlId}-AutoComplete {
width:${width}; /* set width here or else widget will expand to fit its container */
padding-bottom:2em;
}
</style>
<div class="form-field">
<#-- view form -->
<#if form.mode == "view">
<div class="viewmode-field">
<#if field.mandatory && !(field.value?is_number) && field.value == "">
<span class="incomplete-warning"><img src="${url.context}/components/form/images/warning-16.png" title="${msg("form.field.incomplete")}" /><span>
</#if>
<span class="viewmode-label">${field.label?html}:</span>
<span class="viewmode-value">${field.value?html}</span>
</div>
<#else>
<#-- alternative: if form.mode == "edit" -->
<#-- Create/edit form -->
<label for="${fieldHtmlId}">${field.label?html}:<#if field.mandatory><span class="mandatory-indicator">${msg("form.required.fields.marker")}</span></#if></label>
<div id="${fieldHtmlId}-AutoComplete">
<#-- Label to hold error messages from the javascript -->
<p style="color:red" id="${fieldHtmlId}-scriptError"></p>
<select id="${fieldHtmlId}" name="${field.name}"
<#if field.control.params.styleClass?exists>class="${field.control.params.styleClass}"</#if>
<#if field.description?exists>title="${field.description}"</#if>
<#if field.control.params.size?exists>size="${field.control.params.size}"</#if>
<#if field.disabled>disabled="true"</#if> >
<#-- Add the field's current value if it has one as an option -->
<option>${field.value}</option>
</select>
<div id="${fieldHtmlId}-Container"></div>
</div>
</div>
Javascript
<script type="text/javascript">//<![CDATA[
(function()
{
<#-- This references the code field from the form model. For this, the -->
<#-- share config must be set to show the field for this form. -->
<#if form.fields.prop_my_code??>
var code = "${form.fields.prop_my_code.value}";
<#else>
var code = 0;
</#if>
// get code
if(code === null || code === "") {
document.getElementById('${fieldHtmlId}-scriptError').innerHTML = 'No description available.';
return;
}
// Create webscript connection using yui connection manager
// Note that a much more elegant way to call webscripts using Alfresco.util is
// available in the answers here.
var AjaxConnectionManager = {
handleSuccess:function(o) {
console.log('response: '+o.responseText);
this.processResult(o);
},
handleFailure:function(o) {
var selectBox = document.getElementById('${fieldHtmlId}');
var i;
document.getElementById('${fieldHtmlId}-scriptError').innerHTML = 'Descriptions not available.';
},
startRequest:function() {
console.log('webscript call to ${ds} with params code='+code);
YAHOO.util.Connect.asyncRequest('GET', "${ds}?typecode="+code, callback, null);
},
processResult:function(o) {
var selectBox = document.getElementById('${fieldHtmlId}');
var jso = JSON.parse(o.responseText);
var types = jso.types;
console.log('adding '+types.length+' types to selectbox '+selectBox);
var i;
for(i=0;i<types.length;i++) {
// If the current field's value is equal to this value, don't add it.
if(types[i] === null || types[i] === '${field.value}') {
continue;
}
selectBox.add(new Option(types[i], types[i]));
}
}
}
// Define callback methods
var callback = {
success:AjaxConnectionManager.handleSuccess,
failure:AjaxConnectionManager.handleFailure,
scope: AjaxConnectionManager
};
// Call webscript
AjaxConnectionManager.startRequest();
})();
//]]></script>
<#-- This closes the form.mode != "create" condition, so the js is only executed when in edit/create mode. -->
</#if>