TL;DR: Learn how to implement tree node actions and a context menu in a Quasar app using Syncfusion Vue TreeView and Context Menu components. Set up a Quasar project, install Syncfusion Vue packages, and customize TreeView data and menu options. Add events to handle node actions like adding, renaming, and deleting, with dynamic menu items enabled.
Quasar is a high-level Vue.js framework that simplifies the creation of responsive, feature-rich web applications. Built on Vue.js, it offers a wide range of pre-built components, utilities, and themes, enabling developers to build visually stunning and functional interfaces quickly. Its modular architecture allows for easy integration of additional functionalities, while its responsive design ensures seamless performance across various devices and screen sizes. With Quasar, developers can streamline their workflow, reduce development time, and create powerful Vue.js apps with ease, making it a popular choice for both beginners and experienced developers.
In this blog, we’ll implement the tree node actions and context menu within a Quasar app using the Syncfusion Vue components.
Let’s get started!
Prerequisites
Before getting started with Quasar, ensure the following requirements:
Set up a Quasar app
To begin, create a new Quasar app from scratch by executing the following command.
npm init quasar
Upon executing this command, you’ll be prompted with several queries, like below:
√ What would you like to build? » App with Quasar CLI, let's go! √ Project folder: ... my-app √ Pick Quasar version: » Quasar v2 (Vue 3 | latest and greatest) √ Pick script type: » Javascript √ Pick Quasar App CLI variant: » Quasar App CLI with Vite 2 (stable | v1) √ Package name: ... my-app √ Project product name: (must start with a letter if building mobile apps) ... Quasar App √ Project description: ... A Quasar Project × Install project dependencies? (recommended) » Yes, use npm
This setup allows you to tailor the Quasar project based on your needs.
Install the Syncfusion Vue packages in the Quasar app
Syncfusion Vue components are available at npmjs.com. For now, install the Syncfusion Vue packages using the following command.
npm install --save @syncfusion/ej2-vue-navigations
Implementing tree node actions and context menu in the Quasar app
Let’s implement tree node actions and context menu in the Quasar app using the Syncfusion Vue TreeView and Context Menu components, respectively.
Step 1: Import the Vue TreeView component
Begin by importing the Vue TreeView component and defining the fields and data source within the script tag of the ~/src/App.vue file.
<script setup> import { TreeViewComponent as EjsTreeview } from "@syncfusion/ej2-vue-navigations"; import { ContextMenuComponent as EjsContextmenu } from "@syncfusion/ej2-vue-navigations"; import { ref } from 'vue'; import { TreeViewComponent as EjsTreeview } from "@syncfusion/ej2-vue-navigations"; import { ContextMenuComponent as EjsContextmenu } from "@syncfusion/ej2-vue-navigations"; const treeObj = ref(null); const contextObj = ref(null); const dataSource = [ { id: '01', name: 'Local Disk (C:)', expanded: true, hasAttribute: { class: 'remove rename' }, subChild: [ { id: '01-01', name: 'Program Files', subChild: [ { id: '01-01-01', name: 'Windows NT' }, { id: '01-01-02', name: 'Windows Mail' }, { id: '01-01-03', name: 'Windows Photo Viewer' }, ] }, { id: '01-02', name: 'Users', expanded: true, subChild: [ { id: '01-02-01', name: 'Smith' }, { id: '01-02-02', name: 'Public' }, { id: '01-02-03', name: 'Admin' }, ] } ] }, { id: '02', name: 'Local Disk (D:)', hasAttribute: { class: 'rename' }, subChild: [ { id: '02-01', name: 'Personals', subChild: [ { id: '02-01-01', name: 'My photo.png' }, { id: '02-01-02', name: 'Rental document.docx' }, { id: '02-01-03', name: 'Pay slip.pdf' }, ] }, { id: '02-02', name: 'Projects', subChild: [ { id: '02-02-01', name: 'ASP Application' }, { id: '02-02-02', name: 'TypeScript Application' }, { id: '02-02-03', name: 'React Application' }, ] } ] } ]; const fields = { dataSource: dataSource, id: 'id', text: 'name', child: 'subChild', htmlAttributes: 'hasAttribute' } const menuItems = [ { text: 'Add New Item' }, { text: 'Rename Item' }, { text: 'Remove Item' } ] </script>
Step 2: Declare events for the Vue TreeView and Context Menu
Next, declare the events for the Vue TreeView and Context Menu components, as shown in the following code example.
<template> <div id="app"> <ejs-treeview ref='treeObj' id="treeview" :fields="fields" :nodeClicked='nodeclicked'> <ejs-contextmenu ref='contextObj' id="contentmenutree" target='#treeview' :items='menuItems' :beforeOpen='beforeopen' :select='menuclick'></ejs-contextmenu> </ejs-treeview> </div> </template> <script setup> const nodeclicked = function (args) { if (args.event.which === 3) { var treeObj = treeObj.value.ej2_instances[0]; treeObj.selectedNodes = [args.node.getAttribute('data-uid')]; } } const menuclick = function (args) { var treevalidate = treeObj.value.ej2_instances[0]; var targetNodeId = treevalidate.selectedNodes[0]; if (args.item.text == "Add New Item") { var nodeId = "tree_" + this.index; var item = { id: nodeId, name: "New Folder" }; treevalidate.addNodes([item], targetNodeId, null); this.index++; treevalidate.fields.dataSource.push(item); treevalidate.beginEdit(nodeId); } else if (args.item.text == "Remove Item") { treevalidate.removeNodes([targetNodeId]); } else if (args.item.text == "Rename Item") { treevalidate.beginEdit(targetNodeId); } } const beforeopen = function (args) { var treevalidate = treeObj.value.ej2_instances[0]; var targetNodeId = treevalidate.selectedNodes[0]; var targetNode = document.querySelector('[data-uid="' + targetNodeId + '"]'); var contentmenutree = contextObj.value.ej2_instances[0]; if (targetNode.classList.contains('remove')) { contentmenutree.enableItems(['Remove Item'], false); } else { contentmenutree.enableItems(['Remove Item'], true); } if (targetNode.classList.contains('rename')) { contentmenutree.enableItems(['Rename Item'], false); } else { contentmenutree.enableItems(['Rename Item'], true); } } </script>
Step 3: Customize the component styles
Finally, customize the appearance of the components with the required styles.
<style> @import "@syncfusion/ej2-base/styles/material3.css"; @import "@syncfusion/ej2-inputs/styles/material3.css"; @import "@syncfusion/ej2-vue-navigations/styles/material3.css"; #app { display: block; max-width: 400px; max-height: 320px; margin: auto; overflow: auto; border: 1px solid #dddddd; border-radius: 3px; margin: 0 auto; } #contentmenutree { padding: 0px; font-size: inherit; } </style>
Here’s the summarized code for the above steps in the ~/src/App.vue file.
<template> <div id="app"> <ejs-treeview ref='treeObj' id="treeview" :fields="fields" :nodeClicked='nodeclicked'> <ejs-contextmenu ref='contextObj' id="contentmenutree" target='#treeview' :items='menuItems' :beforeOpen='beforeopen' :select='menuclick'></ejs-contextmenu> </ejs-treeview> </div> </template> <script setup> import { ref } from 'vue'; import { TreeViewComponent as EjsTreeview } from "@syncfusion/ej2-vue-navigations"; import { ContextMenuComponent as EjsContextmenu } from "@syncfusion/ej2-vue-navigations"; const treeObj = ref(null); const contextObj = ref(null); const dataSource = [ { id: '01', name: 'Local Disk (C:)', expanded: true, hasAttribute: { class: 'remove rename' }, subChild: [ { id: '01-01', name: 'Program Files', subChild: [ { id: '01-01-01', name: 'Windows NT' }, { id: '01-01-02', name: 'Windows Mail' }, { id: '01-01-03', name: 'Windows Photo Viewer' }, ] }, { id: '01-02', name: 'Users', expanded: true, subChild: [ { id: '01-02-01', name: 'Smith' }, { id: '01-02-02', name: 'Public' }, { id: '01-02-03', name: 'Admin' }, ] } ] }, { id: '02', name: 'Local Disk (D:)', hasAttribute: { class: 'rename' }, subChild: [ { id: '02-01', name: 'Personals', subChild: [ { id: '02-01-01', name: 'My photo.png' }, { id: '02-01-02', name: 'Rental document.docx' }, { id: '02-01-03', name: 'Pay slip.pdf' }, ] }, { id: '02-02', name: 'Projects', subChild: [ { id: '02-02-01', name: 'ASP Application' }, { id: '02-02-02', name: 'TypeScript Application' }, { id: '02-02-03', name: 'React Application' }, ] } ] } ]; const fields = { dataSource: dataSource, id: 'id', text: 'name', child: 'subChild', htmlAttributes: 'hasAttribute' } const menuItems = [ { text: 'Add New Item' }, { text: 'Rename Item' }, { text: 'Remove Item' } ] const nodeclicked = function (args) { if (args.event.which === 3) { var treeObj = treeObj.value.ej2_instances[0]; treeObj.selectedNodes = [args.node.getAttribute('data-uid')]; } } const menuclick = function (args) { var treevalidate = treeObj.value.ej2_instances[0]; var targetNodeId = treevalidate.selectedNodes[0]; if (args.item.text == "Add New Item") { var nodeId = "tree_" + this.index; var item = { id: nodeId, name: "New Folder" }; treevalidate.addNodes([item], targetNodeId, null); this.index++; treevalidate.fields.dataSource.push(item); treevalidate.beginEdit(nodeId); } else if (args.item.text == "Remove Item") { treevalidate.removeNodes([targetNodeId]); } else if (args.item.text == "Rename Item") { treevalidate.beginEdit(targetNodeId); } } const beforeopen = function (args) { var treevalidate = treeObj.value.ej2_instances[0]; var targetNodeId = treevalidate.selectedNodes[0]; var targetNode = document.querySelector('[data-uid="' + targetNodeId + '"]'); var contentmenutree = contextObj.value.ej2_instances[0]; if (targetNode.classList.contains('remove')) { contentmenutree.enableItems(['Remove Item'], false); } else { contentmenutree.enableItems(['Remove Item'], true); } if (targetNode.classList.contains('rename')) { contentmenutree.enableItems(['Rename Item'], false); } else { contentmenutree.enableItems(['Rename Item'], true); } } </script> <style> @import "@syncfusion/ej2-base/styles/material3.css"; @import "@syncfusion/ej2-vue-navigations/styles/material3.css"; @import "@syncfusion/ej2-inputs/styles/material3.css"; #app { display: block; max-width: 400px; max-height: 320px; margin: auto; overflow: auto; border: 1px solid #dddddd; border-radius: 3px; margin: 0 auto; } #contentmenutree { padding: 0px; font-size: inherit; } </style>
Run the project
To run the project, execute the following command:
npm run dev
Then, navigate to http://localhost:9000/ in your preferred browser to view the rendered Syncfusion Vue components within your Quasar project.
The output should look like the following image.
Integrate Syncfusion Vue components into your web apps today and watch them shine like never before.
Conclusion
Thank you for reading this blog! We explored how to integrate Syncfusion Vue components into the Quasar framework. Following these steps, you can also utilize other Syncfusion Vue components in your Quasar apps. We encourage you to try this guide and share your feedback in the comments below!
The latest version of Essential Studio is available on the license and downloads page for existing customers. If you are not a Syncfusion customer, you can start a 30-day free trial to explore our available features.
If you have any questions, feel free to reach out via our support forum, support portal, and feedback portal. We’re always here to assist you!