Search code examples
javascriptag-gridag-grid-angular

Creating a hierarchical/indented dropdown


I know what I am asking is capable using pure HTML; however, I need this capability within an ag-Grid cell.

I have a new requirement for my ag-Grid Angular application. The stakeholder would like me to display a hierarchical LOVs (list of values) dropdown inside of an ag-Grid cell. I am using a cellEditorSelector with a valueFormat to build up different type of cellEditors; i.e. agRichSelectCellEditor, datePicker, no editor (just a free form input field).

Here is an example screenshot of a hierarchical dropdown from my stakeholder:

Example of Stakeholder's LOV/dropdown

I tried to create a POC using an existing dropdown within my application:

Proof of Concept

I was hoping that I did not have to place hyphens to denote the levels (i.e. -, --). I wanted to place tab (‘\t’ for level one and ‘\t\t’ for level two, etc…) escape sequences.

I then tried adding hyphens with a font color of white to no avail with the following code:

return {
component: 'agRichSelectCellEditor',
params: {
    values: valueArray,
    formatValue: function(value) {
        return '-----'.fontcolor('white') + value;
    },
}

};

What was displayed for the LOV was something like this:

LOV with hyphens, but trimmed tabbed space

Here is the array object I created to work with. Level1 has a tab (\t) after the one hyphen and level2 has two tabs (\t\t) after two hyphens and are shown accordingly. For some reason the tabbed spaces are being removed at the time they are displayed by the grid; hence, the usage of the hyphens since they do not get trimmed.

Array object with hyphens and tabbed space

If I create the array object without the hyphens the tabs are still preserved:

Array object with only tabbed space


Solution

  • I was able to come up with a solution for my proof of concept. Note, I use 'level1' and 'level2' as static level indicators. Eventually, my array object will contain the actual level so that it is not restricted to two levels.

    In my grid component I created the following ag-Grid component reference:

    this.components = { levelRenderer: levelRenderer } 
    

    as well as, the levelRenderer code:

     // cell renderer class
    function levelRenderer() {
    }
    
    // init method gets the details of the cell to be renderer
    levelRenderer.prototype.init = function (params) {
      this.eGui = document.createElement('select');
      const hiddenLOVsElement: HTMLInputElement = document.getElementById('hiddenLOVs') as HTMLInputElement;
      this.hiddenLOVs = hiddenLOVsElement;
      const hiddenLOVsArray: any[] = this.hiddenLOVs.value.split(',');
    
      for (let i = 0; i < hiddenLOVsArray.length; i++) {
        const option = document.createElement('option');
        option.value = hiddenLOVsArray[i];
        option.text = hiddenLOVsArray[i];
    
        const levelValue = option.value.substr(0, 6);
        console.log('levelValue: ', levelValue);
        switch (levelValue) {
          case 'level1':
            option.innerHTML = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' + hiddenLOVsArray[i];
            break;
          case 'level2':
            option.innerHTML = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
              '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' + hiddenLOVsArray[i];
            break;
          default:
            option.innerHTML = hiddenLOVsArray[i];
        }
    
        this.eGui.appendChild(option);
      }
    
    };
    
    levelRenderer.prototype.getGui = function() {
        return this.eGui;
    };
    

    Here is how I call it from my cellEditorSelector:

    return {
        component: 'levelRenderer',
        }
    };
    

    Note, the values I use are the results from a web service call (via a utility class) which returns a JSON object. From the JSON object I build an array named, valueArray. I hide that array inside the DOM:

    <input id='hiddenLOVs' type='hidden' [value]="this.hiddenLOVs"/>
    
    
    const hiddenListOfValues: HTMLInputElement = document.getElementById('hiddenLOVs') as HTMLInputElement;
    hiddenListOfValues.value = JSON.parse(JSON.stringify(valueArray));
    

    Here is the final result of my LOV:

    Indented LOV dropdown

    Note, needed to add a getValue function:

    levelRenderer.prototype.getValue = function() {
      console.log('getValue');
      return this.eGui.value;
    };