Open Zen33 opened 1 year ago
playing with the demo I could also find myself in a situation where 2 widgets overlap. A widget get's behind another and when I move the one at the top, the one beneath moves, 1 sec after, on the same spot I dropped my first widget
I do not recommend v-for usage - see my Angular Copomnent explaination why for loops are only for very very simple cases IMO...
maybe someone can create a Vue version of my angular components and examples.
I would love to have a high quality wrapper for Vue (and React) as I've now created one for Angular (what I use at work) - clearly keeping gridstack neutral (plain TS) as frameworks come and go....
I don't know Vue, but for more advanced things (multiple grids drag&drop, nested grids, dragging from toolbar to add/remove items) is it best to let gridstack do all the DOM manipulation as trying to sync between framework and GS becomes complex quickly. This is what I've done in the Angular wrapper - gridstack calls back using addFRemoveCB
to have correct Angular component created instead of <div class="gridstack-item">
for example, but all dom dragging/reparenting/removing is done by gs. Content is created using framework.
The current React & Vue use the for loop which quickly falls appart IMO (I have the same for Angular but discourage for only the simplest things (display a grid from some data, with little modification by user)
Sharing my vue (vue3) solution in case it's helpful, and I'm also interested to see how other folks have rolled their own.
This gives full control to Gridstack to handle creating and removing items, I hook into the added
event to programmatically add Vue 3 components to the content. So far this implementation works well with other Gridstack of features: responsive grid, drag in widgets (acceptWidgets
), etc.
<script setup lang="ts">
import { h, onMounted, render, watchEffect } from 'vue'
let grid: GridStack | null = null;
onMounted(() => {
grid = GridStack.init({
margin: 12,
cellHeight: 100,
float: true,
disableOneColumnMode: true,
acceptWidgets: true,
minRow: 1,
})
grid.on('added', function(event: Event, items: GridStackNode[]) {
for (const item of items) {
const itemEl = item.el as HTMLElement
const itemElContent = itemEl.querySelector('.grid-stack-item-content') as HTMLElement
const widgetId = item.id
if (typeof widgetId === 'undefined') {
continue
}
// dynamically render a vue component, and append it to the grid stack item content
// https://vuejs.org/guide/extras/render-function.html
const widgetNode = h(SpaceWidget, { widgetId })
render(widgetNode, itemElContent)
}
});
grid.on('removed', function(event, items) {
for (const item of items) {
const itemEl = item.el
const itemElContent = itemEl.querySelector('.grid-stack-item-content')
// Unmount the vue node from the item element
// Calling render with null will allow vue to clean up the DOM, and trigger lifecycle hooks
render(null, itemElContent)
}
});
watchEffect(() => {
grid?.load(<gridstack settings>)
})
});
</script>
<template>
<div class="grid-stack grow shrink-0 w-full h-full"></div>
</template>
| This gives full control to Gridstack to handle creating and removing items
that's great and it would be great if you could add an running vue example like the other ones already posted...
Also you could use GridStack.addRemoveCB
to get called to create the dom elements without waiting for the added event to happen (base class could create the default which it currently doesn't). Question is do you need to also do something special when items are deleted for your components ? Angular does.
that's great and it would be great if you could add an running vue example like the other ones already posted...
I'll open a PR today with new demos using GridStack.addRemoveCB
and the event based way I shared above. If that's not what you meant, let me know!
Question is do you need to also do something special when items are deleted for your components ? Angular does.
Yes! Good catch, thank you. I've updated my own code to handle remove, and will include it in the demos. The code above has also been edited to show remove flow.
right, so instead of using added/removed events, we might want to use GridStack.addRemoveCB but in your case (unlike the Angualr wrapper I include) you want the 2 divs and only then do you add your component inside the content div. I was thinking we could extract the div creation out and have GS use it, or your code use it before adding Vue component inside content. that would require a new lib, but I can extract it out...
@thalida I am trying to adapt your example for our dashboard. but the problem I am facing is that my manually rendered components do not have access to the rest of my app. in particular global functions we add to app.config.globalProperties
or childchild components which try to access those. Do you know any way of instancing vue components like you are doing but with the full app scope available? I tried referencing named components which I registered using app.component(...) but it just says they are not found...
Edit: I found a way to hack it by writing createdWidgetVNode.appContext = this.appContext || null and first storing appContext in my parent component in setup() from getCurrentInstance()?.appContext. However the above code still doesn't work in the case where my component gets re-rendered. My component is in a keep-alive tab component and when I change to another tab and back, the tab becomes visible and it wants to re-render. All my gridstack widgets are rendered but are disconnected from the gridstack so they have no height and working layout anymore.
分享我的 vue (vue3) 解决方案,以防它有帮助,而且我也有兴趣了解其他人如何推出自己的解决方案。
这为 Gridstack 提供了完全控制权来处理创建和删除项目,我挂钩该
added
事件以编程方式将 Vue 3 组件添加到内容中。到目前为止,这个实现与 Gridstack 的其他功能配合良好:响应式网格、拖入小部件 (acceptWidgets
) 等。<script setup lang="ts"> import { h, onMounted, render, watchEffect } from 'vue' let grid: GridStack | null = null; onMounted(() => { grid = GridStack.init({ margin: 12, cellHeight: 100, float: true, disableOneColumnMode: true, acceptWidgets: true, minRow: 1, }) grid.on('added', function(event: Event, items: GridStackNode[]) { for (const item of items) { const itemEl = item.el as HTMLElement const itemElContent = itemEl.querySelector('.grid-stack-item-content') as HTMLElement const widgetId = item.id if (typeof widgetId === 'undefined') { continue } // dynamically render a vue component, and append it to the grid stack item content // https://vuejs.org/guide/extras/render-function.html const widgetNode = h(SpaceWidget, { widgetId }) render(widgetNode, itemElContent) } }); grid.on('removed', function(event, items) { for (const item of items) { const itemEl = item.el const itemElContent = itemEl.querySelector('.grid-stack-item-content') // Unmount the vue node from the item element // Calling render with null will allow vue to clean up the DOM, and trigger lifecycle hooks render(null, itemElContent) } }); watchEffect(() => { grid?.load(<gridstack settings>) }) }); </script> <template> <div class="grid-stack grow shrink-0 w-full h-full"></div> </template>
This method specifies that the drag handle selector 'handle:'. handleClass' is invalid. How can I solve this problem
@thalida I am trying to adapt your example for our dashboard. but the problem I am facing is that my manually rendered components do not have access to the rest of my app. in particular global functions we add to
app.config.globalProperties
or childchild components which try to access those. Do you know any way of instancing vue components like you are doing but with the full app scope available? I tried referencing named components which I registered using app.component(...) but it just says they are not found...Edit: I found a way to hack it by writing createdWidgetVNode.appContext = this.appContext || null and first storing appContext in my parent component in setup() from getCurrentInstance()?.appContext. However the above code still doesn't work in the case where my component gets re-rendered. My component is in a keep-alive tab component and when I change to another tab and back, the tab becomes visible and it wants to re-render. All my gridstack widgets are rendered but are disconnected from the gridstack so they have no height and working layout anymore.
Hi @rocifier, did you find any solution to the keep-alive
problem?
@carum98 this solution worked for me.
onActivated(() => {
grid = GridStack.init(gridOptions);
});
onDeactivated(() => {
grid?.destroy(false); // if false nodes and grid will not be removed from the DOM
});
@carum98这个解决方案对我有用。
onActivated(() => { grid = GridStack.init(gridOptions); }); onDeactivated(() => { grid?.destroy(false); // if false nodes and grid will not be removed from the DOM });
nice bro!
这个解决方案对我有用。
Thanks
Page url: https://gridstackjs.com/demo/vue3js_v-for.html
When I add two grid, then remove one of them, the rest could not draggable.