Combining Gridstack.js with Vue 3 components

I'm currently working on setting up a gridstack.js dashboard using Vue 3 and I am looking to have the grid stack items incorporate dynamic vue 3 components.

The issue arises where these grid stack items can only accept HTML content. Even though the documentation suggests that you can include Vue components as content, the provided examples pertain to Vue 2, making it challenging for me to implement this in Vue 3.

Below is the code snippet I am working with:

<template>
    <div class="p-6 h-full flex flex-col">

        <header class="flex flex-row items-center justify-between mb-6">
            <div>
                <h1 class="text-3xl font-bold">
                    Workbench
                </h1>
                <p class="leading-6 text-gray-600 text-sm mt-2">
                    {{ info }}
                </p>
            </div>
            <div class="flex flex-row items-center">
                <button type="button" @click="addPanel()">Add Panel</button>
            </div>
        </header>

        <div class="flex-1">
            <section class="grid-stack"></section>
        </div>

    </div>
</template>

<script setup>

    import { ref, onMounted, defineComponent, createApp } from "vue"

    import TestPanel from "./../components/panels/TestPanel.vue"

    let grid = null;

    const items = [
        { x: 0, y: 0, h: 4, w: 6 },
        { x: 7, y: 0, h: 4, w: 6 },
        { x: 0, y: 5, h: 4, w: 4 },
        { x: 4, y: 5, h: 4, w: 4 },
        { x: 8, y: 5, h: 4, w: 4 },
    ];

    onMounted(() => {

        grid = GridStack.init({
            // float: true,
            cellHeight: "70px",
            minRow: 1,
        });

        grid.load(items)

    });

    function addPanel() {

        const div = document.createElement("div")
        div.id = Math.random().toString(24).substring(8)

        const componentInstance = defineComponent({
            extends: TestPanel, data() {
                return {
                    test: "this is a test"
                }
            }
        })

        const app = createApp(componentInstance)

        app.mount(div)

        let widget = grid.addWidget({
            x: 0,
            y: 0,
            w: 6,
            h: 3,
            content: div.outerHTML,
        })

        app.mount(div.id)
    }

</script>

<style>
    .grid-stack-item-content {
        background-color: #18BC9C;
    }
</style>

Although this approach successfully loads the vue component within a stack grid item, the reactivity of the component is lost.

If anyone could provide assistance, it would be greatly appreciated. Thank you in advance!

Answer №1

I took a unique approach that may not align with the intentions of the gridstack creators, but here is my solution:

<template>
  <button @click="addNewWidget()">Add Widget</button> {{ info }}

  <section class="grid-stack">
    <div 
      v-for="(component, key, index) in components" 
      :key="'component'+index" 
      :gs-id="key" 
      class="grid-stack-item"
      :gs-x="component.gridPos.x" 
      :gs-y="component.gridPos.y" 
      :gs-h="component.gridPos.h" 
      :gs-w="component.gridPos.w"
      gs-auto-position="true"
    >
      <div class="grid-stack-item-content">
        <component :is="component.name" v-bind="component.props" />
      </div>
    </div>
  </section>
</template>

<script>
import { ref, onMounted, reactive, nextTick } from 'vue';
import 'gridstack/dist/gridstack.min.css';
import { GridStack } from 'gridstack';
import YourCustomComponent1 from '../YourCustomComponent1.vue';
import YourCustomComponent2 from '../YourCustomComponent2.vue';
import YourCustomComponent3 from '../YourCustomComponent3.vue';

export default {
  name: "WidgetGrid",
  setup() {
    let info = ref("");
    let grid = null;

    let components = reactive({
      yourCustomComponent1: {
        name: "YourCustomComponent1", props: {}, gridPos: { x: 0, y: 1, w: 4, h: 5 }
      },
      yourCustomComponent2: {
        name: "YourCustomComponent2", props: {}, gridPos: { x: 0, y: 1, w: 2, h: 5 }
      },
    });

    onMounted(() => {
      grid = GridStack.init({
        float: true,
        cellHeight: "70px",
        minRow: 1,
      });

      grid.on("dragstop", (event, element) => {
        console.log("move event!", event, element);
		const node = element.gridstackNode;
		info.value = `you just dragged node #${node.id} to ${node.x},${node.y} – good job!`;
      });
    });

    function addNewWidget() {
      components.yourCustomComponent3= {
		name: "YourCustomComponent3", props: {}, gridPos: { x: 0, y: 1, w: 2, h: 5 }
	  };
	  nextTick(() => {
	    console.log(grid);
		let compEl = document.querySelector('[gs-id="yourCustomComponent3"]');
		console.log(compEl);
		grid.makeWidget(compEl);
      });
	  console.warn("i will only work once, fix my inputs to reuse me");
	}

	return {
	  info,
	  components,
	};
  },
  components: {
    YourCustomComponent1,
    YourCustomComponent2,
  },
}
</script>

<style>
.grid-stack {
  background-color: #FAFAFF;
  border-style: dashed;
}

.grid-stack-item {
  color: #2c3e50;
  text-align: center;
  border-style: solid;
  overflow: auto;
  z-index: 50;
}
</style>

In my scenario, the absence of a div with the grid-stack-item-content class surrounding the component caused the widgets to be fixed in place. I also included an add-new-widget function as an example of how to incorporate a new widget into the grid. Utilizing reactive() ensures Vue triggers a page re-render. After rendering, the component must be registered as a grid item using grid.makeWidget. This process requires the component's DOM element, obtainable post-renders through nextTick.

Answer №2

You can integrate your own component in Vue3 by following these steps:

<div class="grid-container" :style="{ 'background-color': colorCode }">
    <custom-widget v-for="widget in widgetList" :widgetData="widget" :key="widget.id" />
</div>

First, import your custom component:

import CustomWidget from "src/components/CustomGridComponent.vue";

Next, add the component to the export section of your script:

export default {
  name: 'GridContainer',
  components: {
    CustomWidget
  },
  data() {
   ...
  },
  ...
}

That's it! Your final result will resemble something like this:

https://example.com/custom-component-preview.png

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Implementing validation and displaying fields with v-model in vue.js

Looking at this code snippet: <button type="button @click="editing=true">Edit</button> <form v-show="editing" @submit="onSubmit"> <textarea v-model="text"></textarea> </form> <div> Current value: {{text}} </ ...

Is there a method to prevent data loss on page refresh by persisting data stored in a database?

I'm currently developing a commenting feature for a blog using the latest version of NextJs. The text input collects data and sends it to the 'Vercel' hosted database. I am able to successfully fetch the data from the frontend as expected. ...

Utilize jQuery's addClass Method when Submitting a Form Using Ajax

When the form is submitted, I would like to add a class and display a loading animation before executing the AJAX request. However, when setting async to false in the AJAX call, the AJAX request will be executed first before displaying the loading animatio ...

The data visualization tool Highchart is struggling to load

As I try to integrate highcharts into my website, I encounter an unexpected error stating TypeError: $(...).highcharts is not a function. Below is the code snippet in question: @scripts = {<script src="@routes.Assets.at("javascripts/tracknplan.js")" ty ...

Creating distinct short identifiers across various servers

Utilizing the shortid package for creating unique room IDs has proven effective when used on a single server. However, concerns arise regarding the uniqueness of IDs generated when utilized across multiple servers. Is there a method to ensure unique ID g ...

Tomcat hosting a dynamic duo: Spring Boot and React!

Exploring the world of Spring Boot application development with a React client using Gradle is an exciting journey for me as I navigate through these new technologies. My current progress includes successfully creating a WAR file that encompasses several i ...

What is the method for getting js_xlsx to include all empty headers while saving the file?

In the midst of developing a Meteor App, I've incorporated the Node.js package known as "js_xlsx" from "SheetJS", produced by "SheetJSDev". This tool enables me to convert an Excel sheet uploaded into JSON on the backend. The intention is to store thi ...

The onchange functionality is not functioning as expected

I've added an onchange event to the select box, but it doesn't seem to be working. Any suggestions would be greatly appreciated. Thank you in advance. HTML [<select id="notifyBy" ng-change="selectchange()" style="border:none" class="formtex ...

Images obscure dropdown menu

The issue I am experiencing with my blog is that the dropdown menu is appearing behind the image slider on the main page. You can see it here: Can anyone offer guidance on how to prevent this from happening so that the dropdown menu is not obscured? Just ...

Having trouble getting the group hover animation to function properly in Tailwind CSS

Just starting out with tailwind css and running into a little issue. The hover animation I'm trying to apply isn't working as expected in this case. Instead of seeing the desired animated background when hovering over the group, it seems the back ...

What is the best way to achieve a sleek and seamless scrolling effect on a webpage?

Is there a way to improve the scrolling effect on my website using jQuery? I find that the default scrolling behavior in most browsers is jumpy and I'm hoping to achieve a more smooth and polished look. ...

Having trouble with the backspace key on mobile devices?

function createAdditionalDiv() { let innerBox = document.createElement('div') innerBox.contentEditable = "true" innerBox.id = totalBoxes++; innerBox.className = "mainBox" ...

Running the command "npm install [package]" does not automatically update the package.json file

I'm currently utilizing Laravel and experimenting with angular-ui-sortable and angular-utils-pagination. I have successfully installed them using npm, however, I am facing difficulties in updating the package.json file for angular-utils-pagination. ...

Retrieving content dynamically using ajax

As I load comments via ajax, I start with 5 by default and allow the user to request more. My query is centered around the best approach. What is the optimal location to construct the HTML elements meant for display on the page? Would it be better to cr ...

Warning: ComponentMounts has been renamed. Proceed with caution

I'm encountering a persistent warning in my application and I'm struggling to resolve it. Despite running npx react-codemod rename-unsafe-lifecycles as suggested, the error persists and troubleshooting is proving to be challenging. The specific w ...

Issue with innerHTML functionality within a div element

I'm currently troubleshooting an issue with a website that has suddenly stopped functioning properly. Unfortunately, I don't have access to the server at the moment, and I need to quickly understand how the system works. Essentially, there is a ...

Utilizing Angular PrimeNG's range datepicker, you can select a date range spanning from January 31st to December 1st, 2023 within a reactive form. Take it a step further by calculating

Here is some HTML code: <div class="row"> <div class="col-4"> <p-calendar label="startDate" formControlName="startDate" [minDate]="daMaxRange" ...

Give priority to executing jQuery Ajax before running JavaScript

Is there a way to ensure that alert(1) runs first in this scenario: $.post('example.php', function() { alert(1); }) alert(2); alert(3); alert(4); The jQuery ajax call appears to run asynchronously. This means that Jav ...

Unable to toggle Bootstrap 5 tabs in a Nunjucks template - the issue persists

I have been following the bootstrap documentation for tabs which can be found at this link After referencing the documentation, I replicated the sample implementation in my code as shown below: --- title: Portfolio description: Portfolio --- {% exten ...

Execution failure of the passport.authenticate callback

I am currently working on developing a backend using nodejs v8.7.0. For authentication, I am implementing passport and local passport. Previously, everything was running smoothly, but now I am facing an issue. Here is my code: My strategy: var passport = ...