cancel
Showing results for 
Search instead for 
Did you mean: 

Cascading Dropdown in alfresco share

joshijimit
Champ on-the-rise
Champ on-the-rise
Hi All,

I have a requirement in my project to implement cascading dropdown in alfresco share.
I have followed the below link of jean barmash's blog and implemented cascading dropdown in alfresco explorer.

http://blogs.alfresco.com/wp/jbarmash/2008/08/18/implementing-cascading-dropdowns-for-editing-proper...

I have also got success to show the dropdown fields in alfresco share by wrapping it up in custom aspect.
but the problem now is custom component generator(TextFieldGenerator) is not being utilize in alfresco share as alfresco uses "Forms" to edit metadata.

I am finding the workaround to make it possible.
Any help from anyone would be of great help.

Regards,
Jimit
1 ACCEPTED ANSWER

scouil
Star Contributor
Star Contributor
As expected, defining the onchange event in javascript fixes it.
I ended up refactoring a bit my code and spliting it into 2 files:

/org/alfresco/components/form/controls/cascading-dropdowns.ftl :

<#include "/org/alfresco/components/form/controls/common/utils.inc.ftl" />
<#include "/org/alfresco/components/form/controls/common/cascadingDropdownUtils.inc.ftl" />

<!– current value –>
<#assign fieldValue=field.value>

<!– handle default value for empty current value –>
<#if fieldValue?string == "" && field.control.params.defaultValueContextProperty??>
   <#if context.properties[field.control.params.defaultValueContextProperty]??>
      <#assign fieldValue = context.properties[field.control.params.defaultValueContextProperty]>
   <#elseif args[field.control.params.defaultValueContextProperty]??>
      <#assign fieldValue = args[field.control.params.defaultValueContextProperty]>
   </#if>
</#if>

<div class="form-field">
   <!– readonly mode. No action required, display the composed value as is –>
   <#if form.mode == "view">
      <div class="viewmode-field">
         <#if field.mandatory && !(fieldValue?is_number) && fieldValue?string == "">
            <span class="incomplete-warning"><img src="${url.context}/res/components/form/images/warning-16.png" title="${msg("form.field.incomplete" )}" /><span>
         </#if>
         <span class="viewmode-label">${field.label?html}:</span>
         <#if fieldValue?string == "">
            <#assign valueToShow=msg("form.control.novalue" )>
         <#else>
            <#assign valueToShow=fieldValue>
            <#if field.control.params.options?? && field.control.params.options != "">
               <#list field.control.params.options?split(optionSeparator) as nameValue>
                  <#if nameValue?index_of(labelSeparator) == -1>
                     <#if nameValue == fieldValue?string || (fieldValue?is_number && fieldValue?c == nameValue)>
                        <#assign valueToShow=nameValue>
                        <#break>
                     </#if>
                  <#else>
                     <#assign choice=nameValue?split(labelSeparator)>
                     <#if choice[0] == fieldValue?string || (fieldValue?is_number && fieldValue?c == choice[0])>
                        <#assign valueToShow=msgValue(choice[1])>
                        <#break>
                     </#if>
                  </#if>
               </#list>
            </#if>
         </#if>
         <span class="viewmode-value">${valueToShow?html}</span>
      </div>
   <#else>
     <!– Editable mode. –>
     <!– let's keep a single label for both fields –>
      <label for="${fieldHtmlId}">${field.label?html}:<#if field.mandatory><span class="mandatory-indicator">${msg("form.required.fields.marker" )}</span></#if></label>
      <#if field.control.params.options?? && field.control.params.options != "">
       <!– first select for the parent value –>
         <select id="parent-${fieldHtmlId}" 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>
               <#if field.disabled  && !(field.control.params.forceEditable?? && field.control.params.forceEditable == "true" )>disabled="true"</#if>>
         </select>
      
       <!– second select for the child value –>
         <select id="child-${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>
               <#if field.disabled  && !(field.control.params.forceEditable?? && field.control.params.forceEditable == "true" )>disabled="true"</#if>>
         </select>
      
       <!– hidden field containing the final value (e.g. the one sent to the server and stored in database) –>
       <input type="hidden" id="${fieldHtmlId}" name="${field.name}" />
      
         <@formLib.renderFieldHelp field=field />
      <#else>
         <div id="${fieldHtmlId}" class="missing-options">${msg("form.control.selectone.missing-options" )}</div>
      </#if>
   </#if>
</div>

<!– IMPORTANT: this script must execute after the fields are created –>
<script type="text/javascript">//<![CDATA[
   setTimeout(function(){populateSelects("${field.control.params.options}", "${fieldValue}", "parent-${fieldHtmlId}", "child-${fieldHtmlId}", "${fieldHtmlId}");}, 1000);
//]]></script>


and /org/alfresco/components/form/controls/common/cascadingDropdownUtils.inc.ftl :

<!– defines how the option list elements are separated when passed to the client –>
<#if field.control.params.optionSeparator??>
   <#assign optionSeparator=field.control.params.optionSeparator>
<#else>
   <#assign optionSeparator=",">
</#if>

<!– defines how the label list elements are separated when passed to the client –>
<#if field.control.params.labelSeparator??>
   <#assign labelSeparator=field.control.params.labelSeparator>
<#else>
   <#assign labelSeparator="|">
</#if>

<script type="text/javascript">//<![CDATA[
<!– defines how the option list elements are separated when passed to the client –>
<#if field.control.params.optionSeparator??>
   <#assign optionSeparator=field.control.params.optionSeparator>
<#else>
   <#assign optionSeparator=",">
</#if>

<!– defines how the label list elements are separated when passed to the client –>
<#if field.control.params.labelSeparator??>
   <#assign labelSeparator=field.control.params.labelSeparator>
<#else>
   <#assign labelSeparator="|">
</#if>

<script type="text/javascript">//<![CDATA[
   /* string used to separate the parent from the child */
   var OPTION_SEPARATOR = "/";

   /**
   * Functions used to split and synchronize the data between the 2 lists.
   **/
   
   /**
   *   @param stringValues: string containing the available values for the given field
   *   @returns a javascript object containing those values
   **/
   function getValsArrayFromString(stringValues) {
      //array with the options
      var valsArray = stringValues.split("${optionSeparator}");
      //remove the labels
      for (var i = 0; i < valsArray.length; i++) {
         if(valsArray.indexOf("${labelSeparator}") != -1) {
            valsArray = valsArray.substr(0,valsArray.indexOf("${labelSeparator}"));
         }
      }
      return valsArray;
   }

   /**
   * Extracts the parent value from the full value of the option
   **/
   function getParentValue(fullValue) {
      return fullValue.substr(0,fullValue.indexOf(OPTION_SEPARATOR));
   }

   /**
   * Extracts the child value from the full value of the option
   **/
   function getChildValue(fullValue) {
      return fullValue.substr(fullValue.indexOf(OPTION_SEPARATOR) + 1);
   }

   /**
   * Parent value have been changed.
   * Reset the current total value, child value and updates the available child values
   **/
   function onChangeParentValue(valsArray, parentId, childId, totalId) {
      var parentValue = document.getElementById(parentId).value;
      var childSelect = document.getElementById(childId);
      
      /* empty the child select */
      while(childSelect.firstChild) {
         childSelect.removeChild(childSelect.firstChild);
      }
      
      /* fill the child select with the new values */
      for (var i = 0; i < valsArray.length; i++) {
         if ( getParentValue(valsArray) == parentValue) {
            var childValue = getChildValue(valsArray);
            var childOption = document.createElement("option");
            childOption.value = childValue;
            childOption.innerHTML = childValue;
            childSelect.appendChild(childOption);
         }
      }
   }

   /**
   * Child value have been changed.
   * Update the total value
   **/
   function onChangeChildValue(parentId, childId, totalId) {
      var parentValue = document.getElementById(parentId).value;
      var childValue = document.getElementById(childId).value;
      document.getElementById(totalId).value = parentValue + OPTION_SEPARATOR + childValue;
   }

   /**
   * Initial filling of the parent values available
   * TODO: handle default value
   **/
   function populateSelects(stringValues, initialValue, parentId, childId, totalId) {
      /* get an array from the string values */
      var valsArray = getValsArrayFromString(stringValues);
      var parentSelect = document.getElementById(parentId);
      /* we only want to display each parent value once. This object keeps track of the already added ones */
      var uniqueParentValues = {};
      
      /* populate parent select */
      for (var i = 0; i < valsArray.length; i++) {
         var realV = getParentValue(valsArray);
         if(!uniqueParentValues.hasOwnProperty(realV)) {
            uniqueParentValues[realV] = true;
            
            /* create new option for the parent select */
            var parOption = document.createElement("option");
            parOption.value = realV;
            parOption.innerHTML = realV;
            if(realV == getParentValue(initialValue)) {
               parOption.selected = "selected";
            }
            parentSelect.appendChild(parOption);
         }
      }
      parentSelect.onchange = function(){onChangeParentValue(valsArray, parentId, childId, totalId);};
      
      /* populate child select */
      onChangeParentValue(valsArray, parentId, childId, totalId);
      
      /* sets the correct child value */
      var childSelect = document.getElementById(childId);
      childSelect.value = getChildValue(initialValue);
      childSelect.onchange = function(){onChangeChildValue(parentId, childId, totalId);};
      onChangeChildValue(parentId, childId, totalId);
   }
//]]></script>


IE advanced search now works as expected.

View answer in original post

28 REPLIES 28

joshijimit
Champ on-the-rise
Champ on-the-rise
Hi Alfresco team,

Can you please share some ideas on how could I be able to implement cascading dropdown in alfresco share??
What I did so far is, I have added two dropdowns in share-config-custom.xml like below
————————————————————————————————————————————–
<field id="ccdSmiley Tongueroducta" label="My Product">
                <control template="controls/selectone.ftl">                  
                </control>
              
</field>
<field id="ccd:trima" label="My Dependent Product">
                <control template="controls/selectone.ftl">                  
                </control>
  </field>
————————————————————————————————————————————–

And I have filled "My Product" dropdown from custom constraint class assigned in content model. and also register ONCHANGE event on both the dropdowns
by adding ONCHANGE event in "controls/selectone.ftl" file like below. I can pass the selected value in dropdown to this function as well.
————————————————————————————————————————————–
<select id="${fieldHtmlId}" name="${field.name}" tabindex="0" ONCHANGE="fetchDependentList(${field.name},this.options[this.selectedIndex].value,this.options[this.selectedIndex].value);"
     ……….Rest of the code………..
————————————————————————————————————————————–

But Now the problem is, Shall I call WebService from java script and get the JSON response and fill the dependent dropdown?

Or should I change my approach or modify something in order to achieve it??
Your help would be of grt help here as I haven't found anyone who has implemented the kind of fuctionality..

Regards,
Jimit

pavaniakella
Champ in-the-making
Champ in-the-making
Hi Jimit,

I am also trying to implement dynamic list drop down in share datalist. I am not able to get the drop down refreshed. That means it is just picking up the values that are there at the server start. In Alfresco wit is refreshed using component generator..But I don't know any implementation in share.. If you know any information on how to make the values refreshed. Please help me.

Regards,
Pavani

mikeh
Star Contributor
Star Contributor

Shall I call WebService from java script and get the JSON response and fill the dependent dropdown?
Yes, that seems like a sensible approach.

Thanks,
Mike

croc
Champ in-the-making
Champ in-the-making
Hi All,

Does anyone know where I can find th source code for this post.

I checked on the site below but there are files
http://blogs.alfresco.com/wp/jbarmash/2008/08/18/implementing-cascading-dropdowns-for-editing-proper...

Thanks,
Croc

joshijimit
Champ on-the-rise
Champ on-the-rise
Hi  All,

I have the source code of the above article..
If anyone wants it plese send me a PM.

Also I have made cascading dropdown works in alfrsco share but its hard to explain everything on the forum right now a I am preparing a document. Will soon update this forum with the solution.
meanwhile if anyone interested to achieve the same can contact me through PM.
Regards,
Jimit

Please guide me "how to create multiple dropdown for single property"

I'm not sure what your requirement is there, but could you look below at the solution where I render cascading dropdown based on a single property?
It might just help you getting started with yours.

umaistudio
Champ in-the-making
Champ in-the-making
Actually I created the custom aspects using becpg model designer, So if I change the type of constraint while saving it shows error.

umaistudio
Champ in-the-making
Champ in-the-making
Hello please guide me to get dynamic drop down for metadata…