Search code examples
vue.jsvuejs3devextreme

How to create DevExtreme Multi-Level Headers using a recursive Vue loop?


I'm using DevExtreme components for my Vue3 app. Based on this sample ( docs / code sample ) I want to create multi level headers based on a configuration. I think I will have to use a recursive loop for that.

First I created a component GridColumn acting as a node in a tree which is able to render itself with or without its nested children

<template>
  <!-- If there are no children use the data-field prop -->
  <dx-column
    v-if="gridColumn.children === undefined"
    :caption="gridColumn.caption"
    :data-field="gridColumn.dataField"
  />
  <!-- If there are children nest them inside -->
  <dx-column v-else :caption="gridColumn.caption">
    <grid-column
      v-for="childGridColumn in gridColumn.children"
      :key="childGridColumn.caption"
      :gridColumn="childGridColumn"
    />
  </dx-column>
</template>

<script lang="ts">
import { DxColumn } from "devextreme-vue/data-grid";
import { defineComponent } from "vue";

export default defineComponent({
  name: "grid-column",
  components: {
    "dx-column": DxColumn,
  },
  props: {
    gridColumn: Object,
  },
});
</script>

Now I can use this component inside the Home view

<template>
  <dx-data-grid :data-source="data" :show-borders="true">
    <grid-column
      v-for="columnDefinition in columnDefinitions"
      :key="columnDefinition.caption"
      :gridColumn="columnDefinition"
    />
  </dx-data-grid>
</template>

<script lang="ts">
import DxDataGrid from "devextreme-vue/data-grid";
import { defineComponent, ref } from "vue";

import GridColumn from "../components/GridColumn.vue";

export default defineComponent({
  components: {
    "dx-data-grid": DxDataGrid,
    "grid-column": GridColumn,
  },
  setup() {
    const columnDefinitions = ref([
      {
        caption: "Col 1",
        dataField: "fieldOne",
      },
      {
        caption: "Col 2",
        children: [
          {
            caption: "Col 2.1",
            dataField: "fieldTwo",
          },
          {
            caption: "Col 2.2",
            dataField: "fieldThree",
          },
        ],
      },
      {
        caption: "Col 3",
        children: [
          {
            caption: "We need to go deeper",
            children: [
              {
                caption: "... deeper ...",
                children: [
                  {
                    caption: "... almost ...",
                    children: [
                      {
                        caption: "GO FOR IT",
                        dataField: "fieldFour",
                      },
                    ],
                  },
                ],
              },
            ],
          },
        ],
      },
    ]);

    const data = ref([
      {
        fieldOne: "foo",
        fieldTwo: 2,
        fieldThree: new Date(),
        fieldFour: { x: "y" },
      },
      {
        fieldOne: "some more text",
        fieldTwo: 123,
        fieldThree: new Date(),
        fieldFour: { a: "b" },
      },
    ]);

    return { columnDefinitions, data };
  },
});
</script>

The rendered result will be

enter image description here

which is not what I wanted, the captions are wrong and the parent headers are missing. You can checkout a sandbox demo here

Just to show what I want to achieve

enter image description here

with a working sandbox demo

Does someone know how what is wrong with my implementation? And maybe there are even simpler solutions for that?


Solution

  • I didn't find a solution for the recursive element, but you can use the columns property on the data-table itself.

    Your data-table would look like this

    <dx-data-grid 
      :data-source="data" 
      :show-borders="true"
      :columns="columnDefinitions">
    </dx-data-grid>
    

    Now you only have to rename the children array to columns.

    const columnDefinitions = ref([
      {
        caption: "Col 1",
        dataField: "fieldOne",
      },
      {
        caption: "Col 2",
        columns: [
          {
            caption: "Col 2.1",
            dataField: "fieldTwo",
          },
          {
            caption: "Col 2.2",
            dataField: "fieldThree",
          },
        ],
      },
      {
        caption: "Col 3",
        columns: [
          {
            caption: "We need to go deeper",
            columns: [
              {
                caption: "... deeper ...",
                columns: [
                  {
                    caption: "... almost ...",
                    columns: [
                      {
                        caption: "GO FOR IT",
                        dataField: "fieldFour",
                      },
                    ],
                  },
                ],
              },
            ],
          },
        ],
      },
    ]);
    

    The only documentation mentioning a columns property I could find was here. It's not very explicit on how to use that with vue though.