-->

Dynamic selectone in alfresco share

2019-07-05 17:36发布

问题:

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>

回答1:

I had a similar task before. First you need to define a custom template in your configuration xml

<config evaluator="node-type" condition="my:type">
    <forms>
        <form>
            <field-visibility>
                <show id="cm:name" />
                <show id="my:options" />
                <show id="cm:created" />
                <show id="cm:creator" />
                <show id="cm:modified" />
                <show id="cm:modifier" />
            </field-visibility>
            <appearance>
                <field id="my:options">
                    <control template="/org/alfresco/components/form/controls/custom/custom-options.ftl" />
                </field>
            </appearance>
        </form>
    </forms>
</config>

What happens here is that the form engine will look for custom-options.ftl to render my:options for type my:type. custom-options.ftl will contain the html needed to display your data and of course the call to javascript class that will load your list from your webscript. So it looks like this

<#assign controlId = fieldHtmlId + "-cntrl">
<script type="text/javascript">//<![CDATA[
// Here you could call your webscript and load your list
</script>

<div id="${controlId}" class="form-field">
 <label for="${fieldHtmlId}">${msg("form.control.my-options.label")}:<#if field.mandatory><span class="mandatory-indicator">${msg("form.required.fields.marker")}</span></#if></label>
 <select id="${fieldHtmlId}" name="${field.name}" tabindex="0"
       <#if field.description??>title="${field.description}"</#if>
       <#if field.control.params.size??>size="${field.control.params.size}"</#if> 
       <#if field.control.params.styleClass??>class="${field.control.params.styleClass}"</#if>
       <#if field.control.params.style??>style="${field.control.params.style}"</#if>>
 </select>
 <@formLib.renderFieldHelp field=field />
</div>


回答2:

You can call webscript like this:

<script type="text/javascript">//<![CDATA[
    var updateOptions = function(res){
        var result = eval('(' + res.serverResponse.responseText + ')');
        if(result.Options.length > 0 ) { // Options - returned JSON object
            // do something with JSON data
        }
    }

Alfresco.util.Ajax.jsonGet({
    url : Alfresco.constants.PROXY_URI + "/getOptions/{uniqueCode}"+ (new Date().getTime()),
    successCallback : {
        fn : updateOptions,
        scope : this
    },
    failureCallback : {
        fn : function() {},
        scope : this
    }
});
//]]></script>