gridstack.js icon indicating copy to clipboard operation
gridstack.js copied to clipboard

Example vue3js.html has `this` as undefined

Open abulka opened this issue 4 years ago • 4 comments

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?

abulka avatar Aug 12 '21 08:08 abulka

I don't know/use Vue. maybe author of #1404 @manang or @RiFrost can chime in ?

adumesny avatar Aug 12 '21 16:08 adumesny

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 avatar Aug 12 '21 17:08 RiFrost

@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 this inside 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 grid by returning it from setup but because setup returns well before onMounted is ever called (which creates grid), thus grid is of course null.
  • I tried declaring let grid2 = ref(null); and returning grid2 from setup and setting grid2.value = grid inside onMounted - but then doing anything with this.grid2 from the traditional methods: area doesn't work. For example this.grid2.addWidget({ w: 3, content: 'hi' }); doesn't do anything, and incidentally this.grid2.value is undefined. Plus I was starting to get some browser lockups at this stage, so backed off in my hacking!

abulka avatar Aug 13 '21 00:08 abulka

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

Fanna1119 avatar Aug 23 '21 21:08 Fanna1119