Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue.js binding context in plugin?

I'm trying to create a simple plugin in Vue.js to wrap the vue-resource plugin to track the state of a request.

function State() {}

State.prototype.post = function (ctx, name, url, data, successCB, errorCB) {
    var options = {};

    if (errorCB) {
        options.error = function (resp) {
            ctx[name] = 'error';

            return errorCB(resp);
        };
    }

    ctx[name] = 'sending';

    ctx.$http.post(url, data, function (res, code, req) {
        ctx[name] = 'sent';

        return successCB(res, code, req);
    }, options);
};

function install(Vue) {
    Object.defineProperties(Vue.prototype, {
        $state: {
            get: function () {
                return new State;
                // return Vue.state.bind({$vm: this});
            }
        }
    });
}

module.exports = install;

You will see I pass the ctx context from the calling Vue to get access to it's data values. I've seen with the vue-resource plugin that there is a way to automatically bind this through the plugin bat can't quite get the syntax right.

Basically I would like to avoid having to pass the ctx context in each time, it should just have the proper context already.

EDIT

To clarify I'm looking for a solution to pass the proper context in. The above is just sort of an example and I'm not looking for a solution to track states.

For example in the vue-resource plugin if we make any http request.

this.$http.get('/some/url', {}, function () {
    this.func();

    console.log(this.var);
});

The context is already there in the callback. I don't need to do some kind of var _this = this to get into the views scope. I want to achieve the same for my plugin so that the proper this is just there. I'm trying to figure it out from the vue-resource plugin but having a hard time following all the code.

like image 595
Rob Avatar asked Mar 19 '16 08:03

Rob


2 Answers

Expanding my comment to an answer -

So you have a name property on your Vue component, and you want this plugin to update that value as the HTTP request progresses? I think that gives you a bad chain of responsibility. Your Vue instance would be required to have a name property, and your plugin wouldn't be standalone.

It would be better to make the plugin handle all state-tracking on its own. You could make a property of State called status that is updated as the request progresses. Then you could know the current state using this.$state.status. Then the plugin is responsible for it's purpose, and the component remains independent

State.prototype.status = "not sent"; 

State.prototype.post = function (url, data, successCB, errorCB) {
    var options = {};

    if (errorCB) {
        options.error = function (resp) {
            this.status = 'error';

            return errorCB(resp);
        };
    }

    this.status = 'sending';

    this.Vue.http.post(url, data, function (res, code, req) {
        this.status = 'sent';

        return successCB(res, code, req);
    }, options);
};

function install(Vue) {
    Object.defineProperties(Vue.prototype, {
        $state: {
            get: function () {
                var state = new State;
                state.Vue = Vue;
                return state;
            }
        }
    });
}

Then in html:

<div v-if="$state.status == 'sending'">Sending...</div>
<div v-if="$state.status == 'sent'">Sent!</div>
<div v-if="$state.status == 'error'">Error!</div>

If you do want to do things your way, I think you'd just need to bind this to post() each time from within your Vue component:

this.$state.post(args){

}.bind(this)

So within the post function this would be your Vue. I think the first way is best

Edit --

The functions successCb and errorCb are already run in the scope of the Vue component, because you defined them there. The vue-resource callbacks in your situation have the scope of State because you defined them here, that isn't going to change unless you pass the context in as you've done. But the point here is that your plugin should not need to know the context of the component, just as vue-resource never knows the context of the component. It just gets the data, sends the request, and runs the callback. Never knows anything about the calling component.

So in the functions you send to this.$state.post as callbacks, you can edit your Vue data using this.var - as you should be able to. In the callbacks you send to Vue.http.post from your State, you can edit properties on the State object - again, expected behavior. You need to make the name or status variable a part of State and then reference it as this.$state.name from within your Vue component to check the status.

edit 2:

you could even have a variable $state.response and pass in myCustomVar, and then track $state.response.myCustomVar. That way you could pass in different custom variables on each request and track them independently

like image 125
Jeff Avatar answered Oct 24 '22 10:10

Jeff


I ended up sorting this out, it was easier than I thought.

It's just a simple shortcut wrapper around the $http method in the vue-router so that calls can be made like so:

this.$state.get('/buildings', data, function (resp) {
    this.buildings = resp.data;
}, {
    track: 'getBuildingState'
});

Or

this.$state('/buildings', {
    data: data,
    track: 'getBuildingState',
    success: function (resp) {
        this.buildings = resp.data;
    }
});

Can check out the snippet on Gihub here

like image 1
Rob Avatar answered Oct 24 '22 09:10

Rob