5

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.

Rob
  • 10,851
  • 21
  • 69
  • 109
  • 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. Can you make a property of `State` called `status` that is updated? 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 – Jeff Mar 21 '16 at 16:32

2 Answers2

3

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

Jeff
  • 24,623
  • 4
  • 69
  • 78
  • I think you misunderstood. There is no `$state.status`. When calling `this.$state.post(ctx, 'someStateVar`...` gets passed in. During that request `ctx['someStateVar']` get's updated with the state. The idea is to not have to pass the `ctx` context in, it should just be there. Anyway, it's just an example, the point is about getting the proper context in there. For instance the `vue-resource` plugin, you can just do `this.func()` or `this.var` inside the `success` and `error` callbacks without having to reference is as `var _this = this` outside the scope. I'm trying to achieve the same thing. – Rob Mar 22 '16 at 08:10
  • You can already call `this.func()` or `this.var` in your success and error callbacks. They are written in the scope of the Vue component and then passed in, so they still have the original scope. From here, you just call them as `cb()` or `errorCb()`. The point is that a plugin should never need to know the context it is being called from. You should never update `ctx['someStateVar']`from a plugin, instead it should be a property on the plugin. Otherwise your plugin depends on that variable which is not a good idea. I edited with more... – Jeff Mar 22 '16 at 14:44
  • It's a custom variable already. I wrote my comment wrong, but if you look at the example, you will see nothing is hard coded in the plugin. It's just `ctx[someVar]`. Again the `$http` does bind it somehow so it is possible to do it without passing the `ctx` or calling `bind`. Just trying to figure out how. – Rob Mar 23 '16 at 06:05
  • Where does this use the calling context? https://github.com/vuejs/vue-resource/blob/master/src/http/index.js#L81 . In your edit example, the function is defined *inside the Vue component* so of course it has the right context. In your full code, it is defined inside `State`, so it has that context. `vue-resource` isn't binding anything here – Jeff Mar 23 '16 at 14:06
  • There is a bind function here... do you have access to the variable `this.$vm` in your plugin? https://github.com/vuejs/vue-resource/blob/master/src/http/index.js#L81 – Jeff Mar 23 '16 at 14:13
1

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

Rob
  • 10,851
  • 21
  • 69
  • 109
  • 2
    The snippet link to Github is not working, can you please provide the snippet here or a new link ? I have been trying to figure out a way to implement the same in a plugin. Your help will be much appreciated. – Donkarnash Jul 10 '16 at 08:38