Cell Template with Grouping

When Grouping is enabled in the Kendo UI native Vue Grid, the Cell template needs to define a bit more complex structure. That is needed in order to address all the possible cell scenarios, which would be present when data is grouped.

<div id="vueapp" class="vue-app">
    <Grid ref="grid"
          :cell-render="cellTemplate"
          :style="{height: '520px'}"
          :data-items="gridData"
          :resizable="true"
          :reorderable="true"
          :sortable="true"
          :pageable="gridPageable"
          :groupable="true"
          :group= "group"
          :take="take"
          :skip="skip"
          :expand-field="'expanded'"
          :columns="columns"
          @datastatechange="dataStateChange"
          @expandchange="expandChange">
    </Grid>
</div>
import Vue from 'vue';
import { process } from '@progress/kendo-data-query';
import { Grid } from '@progress/kendo-vue-grid';

function getNestedValue(fieldName, dataItem) {
    const path = fieldName.split('.');
    let data = dataItem;
    path.forEach((p) => {
        data = data ? data[p] : undefined;
    });

    return data;
}

const componentInstance = {
    props: {
        field: String,
        dataItem: Object,
        format: String,
        className: String,
        columnIndex: Number,
        columnsCount: Number,
        rowType: String,
        level: Number,
        expanded: Boolean,
        editor: String,
    },
    computed: {
      renderArrow(){
        var returnValue = this.columnIndex === undefined ||this. level === undefined ||
         this.columnIndex < this.level ||this.columnsCount === undefined ||
          this.rowType !== 'groupHeader' || this.dataItem[this.field] === undefined;
        return returnValue && this.dataItem[this.field];
      },
      renderCell(){
        return this.field !== undefined && this.rowType !== 'groupHeader';
      }
    },
    template: `
    <td v-if="renderCell" :class="className">
    <b>{{ getNestedValue(field, dataItem)}}</b></td>
    <td v-else-if="renderArrow" key="'g' + columnIndex" :class="'k-group-cell'" ></td>
        <td v-else-if="columnIndex <= level" key='g-colspan'
                 :colSpan="columnsCount - columnIndex">
                    <p class="k-reset">
                        <a
                            @click="onClick"
                            tabIndex="-1"
                            href="#"
                            :class="expanded ? 'k-i-collapse k-icon' : 'k-i-expand k-icon'"
                        />
                        {{dataItem[field]}}
                    </p>
                </td>`,
    methods: {
        onClick(e) {
            // @ts-ignore
            this.$emit('click', e, this.dataItem, this.expanded);
        },
        getNestedValue: getNestedValue
    }
};

Vue.component('Grid', Grid);

new Vue({
    el: '#vueapp',
    data: function () {
        return {
            cellTemplate: componentInstance,
            gridPageable: { pageSizes: true },
            products: this.createRandomData(1000),
            gridData: [],
            skip: 0,
            take: 10,
            group: [ { field: 'UnitsInStock' } ],
            expandedItems: [],
            columns: [
                { field: 'ProductID', filterable: false, title: 'ID', width: '50px' },
                { field: 'ProductName', title: 'Product Name' },
                { field: 'UnitPrice', filter: 'numeric', title: 'Unit Price' },
                { field: 'UnitsInStock', title: 'Units In Stock' }
            ]
        };
    },
    created: function() {
        this.getData();
    },
    methods: {
        getNestedValue: getNestedValue,
        getData: function () {
            this.gridData = process(this.products, {take: this.take, skip: this.skip, group: this.group});
        },
        createAppState: function(dataState) {
            this.group = dataState.group;
            this.take = dataState.take;
            this.skip = dataState.skip;
            this.getData();
        },
        dataStateChange: function (event) {
            this.createAppState(event.data);
        },
        expandChange: function (event) {
            Vue.set(event.dataItem, event.target.$props.expandField, event.value);
        },
        createRandomData(count) {
            const productNames = ['Chai', 'Chang', 'Syrup', 'Apple', 'Orange', 'Banana', 'Lemon', 'Pineapple', 'Tea', 'Milk'];
            const unitPrices = [12.5, 10.1, 5.3, 7, 22.53, 16.22, 20, 50, 100, 120];
            const units = [2, 7, 12, 25,  67, 233, 123, 53, 67, 89];

            return Array(count).fill({}).map((_, idx) => ({
                ProductID: idx + 1,
                ProductName: productNames[Math.floor(Math.random() * productNames.length)],
                UnitPrice: unitPrices[Math.floor(Math.random() * unitPrices.length)],
                UnitsInStock: units[Math.floor(Math.random() * units.length)]
            }));
        }
    }
});

In this article