Custom cell template in hierarchical grid

I have created a one-level data grid and used a custom column template successfully.

Now I have created a three-level hierarchical grid following the example in https://ej2.syncfusion.com/vue/demos/#/material/grid/master-details-export.html.

I need to use custom templates in the lowest level, but the template is not rendering, so I assume I have something configured incorrectly.

I have attached a sample file with the grid code. As you will see, the main "providerGrid" has a child "locationGrid" defined and bound. The locationGrid is defined within the data() function, as in your example. And its child grid, "treatmentGrid," is defined as a variable at the top of the data() function, also as in your example.

The nested grids display correctly - but without the rendered template (editTemplate) specified in the second column of the treatmentGrid. Since the grid is being defined as a variable here, there is no way to use binding syntax. (:template), and the values must be prefixed with "this."

I have tried this three ways: 
  1. As shown in my code, using a template literal created within the data() function and referring to it in the column definition, which works fine on a single-level grid, but does not render in the lower level.
  2. By placing the same template literal code in the column definition itself. This causes the treatmentGrid to show a "No records" message.
  3. By referring to a Vue file imported into the main file. This approach also works fine on a single-level grid, but on the nested grid also causes a "No records" message.
Could you please review the file I sent and suggest how to create and use a custom column template in a nested grid?

Thanks in advance for your help.


Attachment: ProviderGrid.vue_e677953e.zip

6 Replies 1 reply marked as answer

SM Shalini Maragathavel Syncfusion Team September 29, 2020 11:56 AM UTC

Hi Tom, 

Greetings from Syncfusion Support. 

Based on your query we found that you want to bind the column template to the nested Grid. You can achieve your requirement of binding the column template to the Hierarchy Grid as demonstrated in the below code snippet,  
 
    <div> 
        <e-columns> 
          <e-column 
            headerText="Employee Image" 
            width="150" 
            textAlign="Center" 
            :template="cTemplate" 
          ></e-column> 
       ... 
        </e-columns> 
<script> 
 
var itemVue = Vue.component("itemTemplate", { 
  template: `<span>hi</span>`, 
  data() { 
    return { 
      data: {}, 
    }; 
  }, 
}); 
 
export default Vue.extend({ 
  data: () => { 
    let secondChildGrid = { 
      dataSource: customerData, 
 
      queryString: "CustomerID", 
      columns: [ 
        { 
          headerText: "Custom template", 
          textAlign: "Right", 
          template: function () { 
            return { 
              template: itemVue, 
            }; 
          }, 
          width: 75, 
        }, 
       ... 
      ], 
      childGrid: { 
        dataSource: hierarchyOrderdata, 
        queryString: "CustomerID", 
        columns: [ 
          { 
            headerText: "Custom template", 
            textAlign: "Right", 
            template: function () { 
              return { 
                template: itemVue, 
              }; 
      }, 
    }; 
 
    return { 
      parentData: employeeData.slice(0, 5), 
      cTemplate: function () { 
        return { 
          template: Vue.component("columnTemplate", { 
            template: `<div class="image"> 
                   hi 
                </div>`, 
            data: function () { 
              return { 
                data: {}, 
              }; 
            }, 
            computed: { 
              image: function () { 
                return "./" + this.data.EmployeeID + ".png"; 
              }, 
              altImage: function () { 
                return this.data.EmployeeID; 
      }, 
      childGrid: { 
        dataSource: orderDatas, 
        queryString: "EmployeeID", 
        columns: [ 
          { 
            headerText: "Custom template", 
            template: function () { 
              return { 
                template: itemVue, 
              }; 
            }, 
          }, 
          ... 
        ], 
        childGrid: secondChildGrid, 
      }, 
    }); 
</script> 
 


Please find the  below sample for more information. 

 Please get back to us if you need further assistance. 

Regards, 
Shalini M. 







Marked as answer

TM Tom McNeer October 1, 2020 08:03 PM UTC

I need to go one step further with this. I have created a three-level grid that includes custom cell templates, according to your examples. The display of everything works perfectly. However, I'm struggling to properly handle events from the custom templates in the third-level.

In this ticket, https://www.syncfusion.com/forums/158033/data-in-column-template, you helped me understand how to implement a global event bus to listen for events within the grid. That technique is working correctly, using e.target or e.element in obtaining row data for processing, in a single-level grid.

However, I have not been able to successfully reference the grid when the event is emitted by the third-level grid.

I receive the error "TypeError: Cannot read property 'getRowObjectFromUID' of undefined."

The following code is within a parent component, ProviderGrid.vue.

The top-level grid is defined as:

<ejs-grid
id="pGrid"
ref="providerGrid"
:created="gridCreated"
:childGrid="locationGrid"
:dataSource="providerData.providers"
:allowFiltering="false"
:allowExcelExport="true"
:allowPdfExport="true"
:allowPaging="true"
:pageSettings="pageSettings"
:allowTextWrap="true"
:allowSorting="false"
:enableAltRow="false"
:toolbar="toolbar"
:toolbarClick="toolbarClick"
:sortSettings="sortSettings"
:rowDataBound="rowDataBound"
>
<e-columns>
<e-column field="Provider" headerText="Provider"></e-column>
</e-columns>
</ejs-grid>

As in your examples, the second-level grid is defined and returned within the data() function as follows:

locationGrid: {
dataSource: this.providerData.locations,
queryString: 'ProviderID',
enableAltRow: false,
columns: [{ field: 'Location', headerText: 'Location' }],
childGrid: treatmentGrid,
rowDataBound: this.locRowDataBound
}


The third-level grid is defined as a variable at the top of the data() function, as in your example:

let treatmentGrid = {
ref: 'tGrid',
dataSource: this.providerData.treatments,
queryString: 'LocationID',
rowDataBound: this.treatmentRowDataBound,
enableAltRow: false,
columns: [
{
headerText: 'Treatment',
width: '300',
template: function() {
return {
template: TreatmentDescColumn
}
}
},
{
headerText: '',
width: '100',
template: function() {
return {
template: TreatmentEditColumn
}
}
},
{
headerText: 'Status',
textAlign: 'center',
width: '80',
template: function() {
return {
template: StatusEditColumn
}
}
},
{
field: 'ApprovalDate',
headerText: 'Appr. Date',
format: 'yMd',
type: 'date',
textAlign: 'center',
width: '90'
},
{
field: 'ProcedureCreator',
headerText: 'Created By',
width: '100'
},
{
headerText: 'Auth. Code (Resend)',
width: '140',
template: function() {
return {
template: ApprovalCodeColumn
}
}
},
{
field: 'ScheduledDate',
headerText: 'Sched. Date',
format: 'yMd',
type: 'date',
textAlign: 'center',
width: '90'
},
{
field: 'TreatmentDate',
headerText: 'Trtmt. Date',
format: 'yMd',
type: 'date',
textAlign: 'center',
width: '90'
},
{
field: 'TNoteDisplay',
headerText: 'Note Rcvd.',
width: '90',
textAlign: 'center'
},
{
field: 'TNoteRead',
headerText: 'Note Read',
width: '90',
textAlign: 'center'
},
{
field: 'Estimate',
headerText: 'Estimate',
textAlign: 'right',
width: '100',
format: 'C2',
type: 'number'
},
{
field: 'Billed',
headerText: 'Billed',
textAlign: 'right',
width: '100',
format: 'C2',
type: 'number'
},
{
field: 'Paid',
headerText: 'Paid',
textAlign: 'right',
width: '100',
format: 'C2',
type: 'number'
}
]
}

The error comes when events are emitted by the custom templates in the third-level. As an example, the following is handled within the created() function of the top-level grid:

this.$nuxt.$on('show-treatment-detail', this.showTreatmentDetail)

The event fires the correct local method:

showTreatmentDetail(e) {
try {
let data = this.rowData(e.target)
this.$emit('open-treatment-details', data)
} catch (error) {
console.log(error)
}
}

The local rowData function looks to the grid for the row:

rowData(target) {
let rowObj = this.$refs.providerGrid.childGrid.childGrid.ej2Instances.getRowObjectFromUID(
closest(target, '.e-row').getAttribute('data-uid')
)
return rowObj.data
}

... but this is where the error occurs. I have tried the reference above, using the ref value of the top-level grid, trying to walk down the child grids. I have also tried ...

let rowObj = this.$refs.tGrid.ej2Instances.getRowObjectFromUID(
closest(target, '.e-row').getAttribute('data-uid')
)
return rowObj.data

... attempting to access a ref set when the treatmentGrid is created ...

let treatmentGrid = {
ref: 'tGrid',

.. but this fails also. 

Could you please help me understand how to access the row correctly? FYI, the same functions work when the event comes from a single-level grid. In the example above, the e.target is the correct reference.

Thanks very much for your help.


TM Tom McNeer October 3, 2020 01:47 PM UTC

I have finally realized that my problem is that there are multiple grids created dynamically, so hard references such as I've tried won't work.

But the help I need is the same: how to "find" the correct grid in order to work with its row data.


MS Manivel Sellamuthu Syncfusion Team October 6, 2020 10:56 AM UTC

Hi Tom, 

Thanks for your update. 

Query: how to "find" the correct grid in order to work with its row data. 

We have analyzed your query and you can find the correct grid based on the target element.  

In the below code example we are listening the event in the top level Grid’s(ParentGrid) created event. The column template has been bound with click event and the same template(itemVue) is applied to all the Child grids. So, while clicking the template we can get the  corresponding row details from the grid. Please refer the below code example and sample for more information. 

<template> 
  <div class="col-lg-12 control-section"> 
    <div> 
      <ejs-grid 
        :dataSource="parentData" 
        :childGrid="childGrid" 
        :allowSorting="true" 
        :created="created" 
      > 
 
      </ejs-grid> 
    </div> 
  </div> 
</template> 
<script> 
import Vue from "vue"; 
import { 
  GridPlugin, 
  DetailRow, 
  Sort, 
  parentsUntil, 
from "@syncfusion/ej2-vue-grids"; 
 
Vue.prototype.$eventHub = new Vue(); 
Vue.use(GridPlugin); 
 
export default Vue.extend({ 
  data: () => { 
    let secondChildGrid = { 
      dataSource: customerData, 
 
      queryString: "CustomerID", 
      columns: [ 
        { 
          headerText: "Custom template", 
          textAlign: "Right", 
          template: function () { 
            return { 
              template: itemVue, 
            }; 
          }, 
          width: 75, 
        }, 
        { 
          field: "CustomerID", 
          headerText: "Customer ID", 
          textAlign: "Right", 
 
          width: 75, 
        }, 
        { field: "Address", headerText: "Address", width: 120 }, 
        { field: "Country", headerText: "Country", width: 100 }, 
      ], 
      childGrid: { 
        dataSource: hierarchyOrderdata, 
        queryString: "CustomerID", 
        columns: [ 
          { 
            headerText: "Custom template", 
            textAlign: "Right", 
            template: function () { 
              return { 
                template: itemVue, 
              }; 
            }, 
            width: 75, 
          }, 
          { 
            field: "OrderID", 
            headerText: "Customer ID", 
            textAlign: "Right", 
 
            width: 75, 
          }, 
          { field: "ShipCountry", headerText: "Country", width: 100 }, 
        ], 
      }, 
    }; 
 
    return { 
      parentData: employeeData.slice(05), 
      cTemplate: function () { 
        return { 
          template: Vue.component("columnTemplate", { 
            template: `<div class="image"> 
                   hi 
                </div>`, 
            data: function () { 
              return { 
                data: {}, 
              }; 
            }, 
            computed: { 
              image: function () { 
                return "./" + this.data.EmployeeID + ".png"; 
              }, 
              altImage: function () { 
                return this.data.EmployeeID; 
              }, 
            }, 
          }), 
        }; 
      }, 
      childGrid: { 
        dataSource: orderDatas, 
        queryString: "EmployeeID", 
        allowPaging: true, 
        pageSettings: { pageSize: 6, pageCount: 5 }, 
        columns: [ 
          { 
            headerText: "Custom template", 
            template: function () { 
              return { 
                template: itemVue, 
              }; 
            }, 
            textAlign: "Right", 
            width: 120, 
          }, 
          { 
            field: "OrderID", 
            headerText: "Order ID", 
 
            textAlign: "Right", 
            width: 120, 
          }, 
          { field: "ShipCity", headerText: "Ship City", width: 120 }, 
          { field: "Freight", headerText: "Freight", width: 120 }, 
          { field: "ShipName", headerText: "Ship Name", width: 150 }, 
        ], 
        childGrid: secondChildGrid, 
      }, 
    }; 
  }, 
  methods: { 
    created: function (args) { 
//here we are listening the event 
      this.$eventHub.$on("communicate"this.getRowDetails); 
    }, 
    getRowDetails: function (e) { 
// here we can get the row details 
      console.log(e); 
    }, 
  }, 
  provide: { 
    grid: [DetailRowSort], 
  }, 
}); 
 
var itemVue = Vue.component("itemTemplate", { 
  template: `<span v-on:click="handleClick($event)">hi</span>`, 
  data() { 
    return { 
      data: {}, 
    }; 
  }, 
  methods: { 
    handleClickfunction (args) { 
     // here we are obtaining the grid instance dynamically based on the target 
      var grid = parentsUntil(args.target, "e-grid").ej2_instances[0]; 
// getting the row details 
      var rowInfo = grid.getRowInfo(args.target); 
// emitting the row details 
      this.$eventHub.$emit("communicate", rowInfo); 
    }, 
  }, 
}); 
</script> 


Please let us know, if you need further assistance. 

Regards, 
Manivel


TM Tom McNeer October 6, 2020 01:27 PM UTC

Thank you for the very clear example. I'm always impressed with the thoroughness of your support. I think you can finally close this one out.


AG Ajith Govarthan Syncfusion Team October 7, 2020 02:09 PM UTC

Hi Tom,  

Thanks for your update. 

We are happy to hear that your issue has been resolved. 

Please get back to us if you need further assistance. 

Regards, 
Ajith G.

Loader.
Up arrow icon