Example vue3js.html has `this` as undefined
Inside the onMounted() and grid.on("dragstop" functions of the demo vue3js.html there are several comments saying that the value of this will be the vue.js instance. The value of this is actually undefined at these points.
Your environment
- gridstack.js 4.2.6
- Edge / Chromium Version 92.0.902.67 on Mac Mojave
Steps to reproduce
Add a couple of diagnostic lines in demo/vue3js.html printing the value of this e.g.
onMounted(() => {
console.log('onMounted this is', this) // <------
grid = GridStack.init({
float: true,
cellHeight: "70px",
minRow: 1,
});
// Use an arrow function so that `this` is bound to the Vue instance. Alternatively, use a custom Vue directive on the `.grid-stack` container element: https://vuejs.org/v2/guide/custom-directive.html
grid.on("dragstop", (event, element) => {
console.log('dragstop this is', this) // <------
const node = element.gridstackNode;
// `this` will only access your Vue instance if you used an arrow function, otherwise `this` binds to window scope. see https://hacks.mozilla.org/2015/06/es6-in-depth-arrow-functions/
info.value = `you just dragged node #${node.id} to ${node.x},${node.y} – good job!`;
});
});
Run the demo and notice the console says this is undefined in both cases.
The comments in the demo are thus incorrect.
Expected behavior
Ideally this is the vue.js instance. Interestingly, the vuejs 2 version of the demo works ok and this is correctly the vue.js instance.
Actual behavior
I'm actually not sure what vue.js 3 is supposed to do? If vue.js 3 has changed, then the comments should be changed accordingly.
The deeper problem is how do we call methods of the vue instance (e.g. in the vuejs methods section) from inside gridstack.js event handlers, if we can't get access to the vue instance?
I don't know/use Vue. maybe author of #1404 @manang or @RiFrost can chime in ?
Seems like I have missed removing the comments.
As of my understanding in the setup Method in vue3 there is no this anymore. Since everything is withoin this setup method u do have access to all functions and vars created inside. So instead of saying this.function you should be able to directly call function. To access functions and vars in the template section of the vue component they have to be returned by the setup function.
In vue3 you are still able to use the standard options api like in the vue2 example. But since vue3 introduced the new composition api I thought it might be a good idea to show the difference.
Does this answer the question?
@RiFrost I agree it was a good idea to show off the new composition API of vue3. I just found that the vue3 doco does warn against the use of this within setup():
You should avoid using
thisinside setup as it won't refer to the component instance. setup is called before data properties, computed properties or methods are resolved, so they won't be available within setup.
I do find it interesting that because grid is not exposed by setup, all the handlers that access grid, like addNewWidget(), have to be defined deep inside the setup() function rather than living in the traditional methods: area. I'm not familiar enough with the new composition API of vue3 to judge whether this is good practice or not.
My vue2 instincts want me to be able to access grid from the traditional methods: area.
- I tried exposing
gridby returning it fromsetupbut becausesetupreturns well beforeonMountedis ever called (which creates grid), thus grid is of coursenull. - I tried declaring
let grid2 = ref(null);and returninggrid2fromsetupand settinggrid2.value = gridinsideonMounted- but then doing anything withthis.grid2from the traditionalmethods:area doesn't work. For examplethis.grid2.addWidget({ w: 3, content: 'hi' });doesn't do anything, and incidentallythis.grid2.valueis undefined. Plus I was starting to get some browser lockups at this stage, so backed off in my hacking!
As @abulka said, vue would throw the DOM template binded ref as null if it is initialised before it is mounted. We need to consider that the composition api essentially works with hooks so one option would be to create a wrapper similar to the demo. gridstack itself is not reactive the same way as vue so use them both respectively.
it is of course tempting to wrap the gridstack grid instance in a vue ref like so:
const grid = ref(null);
grid.value = GridStack.init({
float: true,
row: 0,
column: 12,
minRow: 1
});
but there is no point in doing this because the function itself is not reactive.
however if you would want to utilise the template ref. As the docs say: https://github.com/gridstack/gridstack.js/tree/master/doc#initoptions-gridstackoptions---elorstring-gridstackelement--grid-stack-gridstack
then you would target the ref that needs to utilise the element rather than trying to let the vue ref initialise the gridstack instance, so gridstack takes care of the element while vue still has reference to it, of course this would be optional and it is not required to let it work with gridstack. Not to mention there is no mutation observer anyhow(as far as i am aware)
<div
ref="gridref"></div>
let grid;
const gridref = ref(null);
onMounted(() => {
grid = GridStack.init(
{
float: true,
column: 12,
minRow: 1,
},
gridref.value
);
});
The example sandbox is how i would utilise gridstack in vue3, it is very rough of course. There are of course more clever ways to use it, not to mention the gridstack api being quite broad :), but this is the basic idea. feel free to fork and contribute to make it more vuetiful :)
Notice: notice what happens in gridstack.vue when you remove the grid-stackclass from the div, it still inits on the ref.
https://codesandbox.io/s/github/Fanna1119/gridstackvue3
https://github.com/Fanna1119/gridstackvue3