Customizing Indentation and Icons with a TreeGrid in Vue
Background:
There may be times when the default styling does not meet your applications needs, and you may want to adjust the style of your columns so you can more easily understand the hierarchy. This can be achieved with Wijmo by following this article.
Steps to Complete:
- Add Hierarchical Data in the Component
- Customize Indentation with
formatItem - Add CSS for Custom Icons
Getting Started:
Add Hierarchical Data in the Component
Vue stores the dataset inside the component's data() function. The TreeGrid automatically displays nested items as expandable rows because they appear inside the children array.
data() {
return {
data: [
{
name: "Electronics",
sales: 120000,
children: [
{ name: "Phones", sales: 55000 },
{ name: "Laptops", sales: 65000 }
]
},
{
name: "Home",
sales: 95000,
children: [
{ name: "Furniture", sales: 50000 },
{ name: "Kitchen", sales: 45000 }
]
}
]
};
}
Customize Indentation with formatItem
row.leveldetermines indentation-
Leaf nodes get extra padding so icons line up
- We apply style changes directly to the rendered cell
methods: {
onFormatItem(s, e) {
const col = e.getColumn();
const row = e.getRow();
if (col.binding === "name") {
let padding = (row.level || 0) * 14;
if (!row.hasChildren) {
padding += 21;
}
e.cell.style.paddingLeft = padding + "px";
}
}
}
Add CSS for Custom Icons
.wj-glyph-right { background-image: url('data:image/png;base64,…'); }
.wj-glyph-down-right { background-image: url('data:image/png;base64,…'); }
You can of course accomplish more of the styling through the formatItem event if you prefer, this is just one way of adding customization.
I hope you found this article helpful and if you want to access the example application, I will have the files below.
Happy coding!
Files for reference:
// App.vue
<template>
<div style="padding: 20px;">
<h2>Vue TreeGrid with Custom Indentation & Icons</h2>
<wj-flex-grid
:itemsSource="data"
childItemsPath="children"
:autoGenerateColumns="false"
:formatItem="onFormatItem"
>
<wj-flex-grid-column binding="name" header="Name"></wj-flex-grid-column>
<wj-flex-grid-column binding="sales" header="Sales"></wj-flex-grid-column>
</wj-flex-grid>
</div>
</template>
<script>
import { defineComponent } from "vue";
import "@mescius/wijmo.styles/wijmo.css";
import { FlexGrid } from "@mescius/wijmo.vue3.grid";
export default defineComponent({
components: {
WjFlexGrid: FlexGrid
},
data() {
return {
data: [
{
name: "Electronics",
sales: 120000,
children: [
{ name: "Phones", sales: 55000 },
{ name: "Laptops", sales: 65000 }
]
},
{
name: "Home",
sales: 95000,
children: [
{ name: "Furniture", sales: 50000 },
{ name: "Kitchen", sales: 45000 }
]
}
]
};
},
methods: {
onFormatItem(s, e) {
const col = e.getColumn();
const row = e.getRow();
if (col.binding === "name") {
let padding = (row.level || 0) * 14;
if (!row.hasChildren) {
padding += 21;
}
e.cell.style.paddingLeft = padding + "px";
}
}
}
});
</script>
<style>
.wj-flexgrid .wj-cell:has(.wj-glyph-down-right) {
display: flex;
}
.wj-flexgrid .wj-cell:has(.wj-glyph-right) {
display: flex;
align-items: center;
}
.wj-glyph-down-right {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAABm0lEQVR4nL2Ty0rDQBSGo0+gvoAbO9PW9gGE6tbqS4jgFQVBH6A7qy/QRZLqRlwI7UxAtK1IN2qrdCm+gVovoLtaTXrkjJcmmcSmgg4cCJPJl//M/x9F+c+VUlK9ejgX00JsEgufca9rkBbdG9Ao39AIu9UpB0cRfqNTls7E9/sDwfQQH9Mov5dAct2pITbaGUZ4MwBMFJ5V/aCfbQZRJinNeLWvU775Cxh8FEs7YOic24BsxPAFSO8Iqzvc36Isbj+wM3IID5dPUJyvSLDCzBk8Xj3DbqLg2FcjxvA3MEvyE/aXpcUqtMwWmA0TDqZP2/vzVTCbFlhmC0oLVZdBRrJtCDGSbiXHKxfiQwGdOoHiXEXA8EfltZqkXKVsvG1IOBfzuqvyak0A3homWK+WqKPlc897Ve0tixETEyAf/FIqYEveMI2wa1CgxxUblvZzFZUWZmWDbC6vSznEcGJIu84gYfXtQdbnOS04Rt2Mnk7ZS5bkE54wOzSQUsLqHWGKo32W9jIKDcA7823zp4XuYxww+KKGeFRy86/XO/G5aIX/VUs6AAAAAElFTkSuQmCC');
background-repeat: no-repeat;
width: 20px;
height: 20px;
}
.wj-glyph-right {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAABZ0lEQVR4nNWTQS8DQRTHh4+BIzMiLkgEH8SBL+AblEX6PcwQm27dZGbbTSUOraYXB9yEUAQHDq20iYpD9cnbEnZ3Ztpr/8lL9vD2l/f+83+EDKx2xg/HOFObnMmyYPIe6+fb2Z/Ij/YNAgJDnMptztSHYAq0RWULwdhLbEqT9LCg6sAIihWn0rNCcbL4T8FqBdy5wAKWjs2zyJq55TK0P7/g9aIO7qwBSmVL66lgaivevDedg8fSC6BsUD7pbyTXDV8w2RyBnhugVJU0/qkHk08R6FkN3JkolDN1p1kZs6YH7k75UA2eQ2DtsgGZ+UK8p6qb8MQEu/WfQlj9qgHeQkEXn6JuQscKu25qYaL70qkEEJ++ewF/jfmVCnTaHXi7aYK3eGQK97tgakSbRd2Ux2unkF3Sw4zT/QrPSFCV7ff0BJOZnvccQnHS2PqJNalM9YSRf0JP8QIwtJizsKgscqrWjZ4NhL4BeNMpwMOfx2cAAAAASUVORK5CYII=');
background-repeat: no-repeat;
width: 20px;
height: 20px;
}
</style>
