How to use Vue plugin in Store? - javascript

Is there a proper / documented way of using a plugin inside vuex module or plain js module?
I am using event bus to acheive it, not sure if it is the correct / best way. Please help.
Plugin1.plugin.js:
const Plugin1 = {
install(Vue, options) {
Vue.mixin({
methods: {
plugin1method(key, placeholderValues = []) {
return key;
},
},
});
},
};
export default Plugin1;
In App.vue:
Vue.use(Plugin1, { messages: this.plugin1data });
In store / plain-js module:
const vue = new Vue();
const plugin1method = vue.plugin1method;

you can access your Vue instance using this._vm;
and the Vue global using import Vue from 'vue'; and then Vue;
I'm guessing you defined an instance method, so it would be the former (this._vm.plugin1method())
update
I can't tell you which way you should use it because it I can't see how your function is defined in your plugin.
However, here is an example that should illustrate the difference between instance and global
const myPlugin = {
install: function(Vue, options) {
// 1. add global method or property
Vue.myGlobalMethod = function() {
// something logic ...
console.log("run myGlobalMethod");
};
Vue.mixin({
methods: {
plugin1method(key, placeholderValues = []) {
console.log("run mixin method");
return key;
}
}
});
// 4. add an instance method
Vue.prototype.$myMethod = function(methodOptions) {
console.log("run MyMethod");
// something logic ...
};
}
};
Vue.use(Vuex);
Vue.use(myPlugin);
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
this._vm.$myMethod();
Vue.myGlobalMethod();
this._vm.$options.methods.plugin1method(); // <-- plugin mixin custom method
state.count++;
}
}
});
when you commit the increment ie: this.$store.commit('increment') both methods will execute

Related

VueJS - Created global service function with vue provide , get undefined when call the service in vue component

i created a vue base project.
What i am trying to do:
Create a global service function (For call API), in every component able to use this.$service.service.get() to call any API that i want, without need to inject.
Use the app.provide and declare service as a global constant
Here is my main.js
// main.js
const config = {
service: createRepository({httpClient})
}
// Vue.prototype.$repository = config.repository;
const app = createApp({
created() {
},
render: () => (
h(App)
)
});
app.provide('$service', config.service)
app.mixin(mixin)
app.use(router)
app.mount('#app');
// mixin.js
import serviceProvider from "./serviceProvider";
const mixins = [serviceProvider];
const mixin = {
install: function (Vue, options) {
mixins.forEach(m => Vue.mixin(m))
}
}
export default mixin;
//serviceProvider.js
export default {
beforeCreate() {
const options = this.$options;
const service = options.$service || (options.parent ? options.parent.$service : null);
if (!service) return;
this.$service = service;
Object.keys(service).forEach((key, index) => {
this[`$${key}`] = service[key]
})
}
}
What is my expected result:
Expect to see the function being call from the HomePage.vue
// HomePage.vue
async created(){
await this.$service.authService.get()
}
What is my current result:
authService is undefined
Please advice is that my current setup got any problem. Thanks.
provide is useless without inject
If you don't want to use inject, just use app.config.globalProperties (replacement of Vue.prototype in Vue 2)
app.config.globalProperties.$service = createRepository({httpClient})
this.$service will now be available in every component in the app...

how can axios interceptor handler get access to vue component instance(this pointer)within whose created method is invoking axios.get?

Axios is a great library capable of doing ajax both in browser and in node environment. vuejs is a great framework used for component based web development. Normally, it is excellent for a vue comonent to use axios to launch ajax operation.
According to https://github.com/imcvampire/vue-axios , we can use following code to integrate vue and axios.
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
Vue.prototype.$http.interceptors.request.use(function(request){
// I want to access the vuecomponent instance here, how can i do that??
console.log((this.$options._componentTag ? (this.$options._componentTag+
' of '+ (this.$parent.$options._componentTag?
this.$parent.$options._componentTag:'root')): 'root component')+ ' for
url: '
+request.url+ ' body: '+JSON.stringify(request.body))
}
return request
},function(error){ return Promise.reject(error)});
// normally, following code will be run within vue component created hook
method, this represent the vuecomponent, $http is axio
this.$http.get(api).then((response) => {
console.log(response.data)
})
and, also, i'd like to know in which vue component the ajax operation is being executed. So, i use the interceptors to address that problem.
Unfortunately, this pointer does not represent the vuecomponent, how can i implement that?
I don't use vue-axios library, but you can implement it by using vue mixin like sample below:
let httpMixin = {
methods: {
get: function (url) {
// You can access `this` here
// ...
return this.$http.get(url)
}
}
}
new Vue({
mixins: [httpMixin],
created: function () {
// Use mixin methods instead $http
this.get('/api/xyz').then(...)
}
})
Just make it more reusable :')
The package/plugin vue-axios is not my favorite. If you google vue axios it’s the first result. And I think that’s the main reason of it's popularity.
But this is an alternative. We just override the Vue.prototype.$http and Vue.prototype.axios using the original Axios Library
You don't need to use vue-axios:
Vue.prototype.$http = axios;
Vue.prototype.axios = axios;
As we can see it takes the same amount of lines to write the whole functionality ourselves as it takes to configure the plugin.
const api = 'https://dog.ceo/api/breeds/list/all';
let childComp;
Vue.prototype.$http = axios;
Vue.prototype.axios = axios;
childComp = {
template: '<div><p>{{msg}}</p></div>',
created() {
console.log('child created');
this.$options._componentTag = 'tag-1';
this.$parent.$options._componentTag = 'parent-tag-1';
},
mounted() {
console.log('child mounted');
},
data() {
return {
msg: 'Hello Vue',
}
},
}
Vue.mixin({
created() {
console.log('parent created');
}
})
const app = new Vue({
el: '#app',
render: h => h(childComp),
methods: {
load: function() {
app.$http.interceptors.request.use(function(request) {
//console.log(app.$options.components);
// I want to access the vuecomponent instance here, how can i do that??
let name = app.$options._componentTag;
console.log(name);
let parentName = app.$root.$options._componentTag;
console.log('C');
console.log(
(name ? (name + ' of ' + (parentName ? parentName : 'root')) :
'root component') + ' for url: ' + request.url + ' body: ' + JSON.stringify(request.body));
return request;
},
function(error) {
return Promise.reject(error)
});
// normally, following code will be run within vue component created hook
// method, this represent the vuecomponent, $http is axios
app.$http.get(api).then((response) => {
//console.log(response.data);
});
},
},
});
app.load();
var Ctor = Vue.extend({
name: 'cool-stuff'
});
var vm = new Ctor();
// console.log(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.1/vuex.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.js"></script>
<div id="app"></div>

Vue/Vuex - Module two depends on module one, and module one gets data from server

Check this out:
import accountModule from '#/store/modules/account/account';
import otherModule from '#/store/modules/other/other';
export default new Vuex.Store({
modules: {
account: accountModule,
other: otherModule,
}
});
The data initialization in other depends on the account module because the account module has user specific settings. Suppose other.state.list depends on account.state.settings.listOrder. However, I want the data for the account module to come from the server. Which is async. So when other is trying to get set up, it can't just try to reference account.state.settings.listOrder because the response from the server may not have come back yet.
I tried exporting a promise in accountModule that resolves with the module itself. But that approach doesn't seem to work.
import accountModulePromise from '#/store/modules/account/account';
accountModulePromise.then(function (accountMoudle) {
import otherModule from '#/store/modules/other/other';
...
});
This gives me an error saying that import statements need to be top level.
The following doesn't work either:
let accountModule = await import '#/store/modules/account/account';
import otherModule from '#/store/modules/other/other';
...
It gives me an error saying that await is a reserved word. I'm confused though, because https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import says that I should be able to do it.
Your last code block didn't work because of await have to be inside async function.
Remember, the await keyword is only valid inside async functions. If
you use it outside of an async function's body, you will get a
SyntaxError.
From MDN.
You can use Dynamic Module Registration:
accountModulePromise.then(async () => {
let otherModule = await import('#/store/modules/other/other');
store.registerModule('other', otherModule.default);
});
But when you want to get state or dispatch actions you have to check whether module is registered which is pretty bad.
In my opinion it would be better if you redesign your module structure to decoupling each other. Try to move your initialize code to main.js or App.vue then dispatch actions to update module states from that.
Updates
From your last update, Another idea to decoupling your store, I think you should store your list without order and sort it only when you use. You can do this with:
Computed property:
...
computed: {
list () {
let list = this.$store.state.other.list
let order = this.$store.state.account.settings.listOrder
if (!list || !order) return []
return someSort(list, order)
}
},
beforeCreate () {
this.$store.dispatch('other/fetchList')
this.$store.dispatch('account/fetchListOrder')
}
...
Or Vuex getters:
...
getters: {
list: (state) => (order) => {
return someSort(state.list, order)
}
}
...
...
computed: {
list () {
let order = this.$store.state.account.settings.listOrder
return this.$store.getters['others/list'](order)
}
}
...
Okay, so you have two modules. One with state that is fetched from the server, the other with state that is dependent on the first, correct?
I would suggest the following approach:
Set up your modules with empty 'state' to begin with. Then create an action within accountModule to set up the state from the server. Use a getter on other to order the list. Finally, dispatch your action upon app creation.
const account = {
namespaced: true,
state: {
listOrder: ''
},
mutations: {
setListOrder (state, newListOrder) {
state.listOrder = newListOrder
}
},
actions: {
async fetchServerState (ctx) {
let result = await fetch("/path/to/server")
ctx.commit('setListOrder', result.listOrder)
// or whatever your response is, this is an example
}
}
}
const other = {
namespaced: true,
state: {
unorderedList: []
},
getters: {
list (state, getters, rootState) {
return someSort(state.unorderedList, rootState.account.listOrder);
}
}
}
within App.vue (or wherever)
created () {
this.$store.dispatch('account/fetchServerState')
}

Passing a parameter to a function inside a named export object literal

When using a named export to return an object literal composed of functions, is it possible to pass a parameter to one of those functions?
For example, let's say the function below returns conditional results depending on if user's an admin:
// gridConfig.js
function getColumnDefs(isAdmin = false) {
// conditionally return columns
return {
orders: [ ... ],
...
}
}
export const config = {
columnDefs: getColumnDefs(),
rowDefs: getRowDefs(),
...
};
// main.js
import { config } from './gridConfig';
function doStuff() {
const { columnDefs, rowDefs } = config;
grid.columnDefs = columnDefs['orders'];
...
}
If I add the parameter to the function call inside the export, it says the param isn't defined. Adding a parameter to the export alias gives syntax errors. Even if it allowed this, I'm not clear where I'd pass my param inside main.js.
Is there some way of passing a parameter when structuring an export in this manner?
Maybe keeping it simple can be useful :)
export const config = (isAdmin) => ({
columnDefs: getColumnDefs(isAdmin),
rowDefs: getRowDefs(),
...
});
// Import
import { config } from '[...]'; // Placeholder path of import
const myConfigFalse = config(false);
const myConfigTrue = config(true);
export const config = admin => ({
columnDefs: getColumnDefs(admin),
rowDefs: getRowDefs(),
});
// main.js
import { config } from './gridConfig';
function doStuff() {
const { columnDefs, rowDefs } = config(admin);//get the admin variable set before this line
grid.columnDefs = columnDefs['orders'];
...
}

Vue creating a plugin

I feel a bit like I'm missing something very simple, but I've been trying different stuff out and searching all over the place and can't figure out how to use a method from a custom plugin in my Vue application.
In 'vuePlugin.js' I have something like:
const myPlugin = {};
myPlugin.install = function(Vue, options){
Vue.myMethod = function(){
console.log("It worked!");
}
}
In my main.js I have:
import myPlugin from './js/vuePlugin.js'
Vue.use(myPlugin);
Then in my App.vue I have:
export default {
name: 'app',
props: {},
data () {
return{ someData: 'data' }
},
beforeCreate: function(){
myMethod();
}
}
With this I get an error that "myMethod is not defined".
I've tried saying:
var foo = myPlugin();
console.log(foo);
In my console I get an object called "install" with arguments:
"Exception: TypeError: 'caller' and 'arguments' are restricted function properties and cannot be accessed in this context. at Function.remoteFunction"
All of the documentation seems to just show how to create the plugin and "use" it, but not actually how to access anything in it. What am I missing here?
You have to export your object to be used in vuejs as follows
file vuePlugin.js
const myPlugin = {}
myPlugin.install = function (Vue, options) {
Vue.myMethod = function () {
console.log('It worked!')
}
Vue.prototype.mySecondMethod = function () {
console.log('My second Method ')
}
}
export default myPlugin
while calling the method you cannot call the method directly, you have to use as following code shown
file App.vue
export default {
name: 'app',
props: {},
data () {
return{ someData: 'data' }
},
beforeCreate: function(){
Vue.myMethod(); // call from Vue object , do not directly call myMethod()
this.mySecondMethod() // if you used prototype based method creation in your plugin
}
}
hopes it will help you

Categories