Pass options to vuejs components registered globally - javascript

I am registering my vuejs components in this way:
index.js (A separate reusable npm+webpack project: myComponents)
import MyButtons from './src/components/my-buttons.vue'
import MyInput from './src/components/my-inputs.vue'
export default {
install(Vue, options) {
Vue.component("my-button", MyButtons);
Vue.component("my-input", MyInput);
}
};
Another project where I am using the above components with npm link
import Vue from 'vue'
import App from './vue/App.vue'
import MyComponents from 'myComponents'
Vue.use(MyComponents, {
theme: 'SomeTheme',
color: 'SomeColor'
});
new Vue({el: '#app', render: h => h(App)});
Now what I want to do is in install() function somehow pass the options to the components being registered and save them. So that I can control the color and theme in such a way that every instance of these components render according to the theme and color, where these components will have more then one theme/style and color.

A common way of solving this problem is to put the settings on the Vue prototype.
e.g.:
install(Vue, options) {
Vue.prototype.$myLibraryOrPluginName = { options }
Vue.component("my-button", MyButtons);
Vue.component("my-input", MyInput);
}
Then inside the components you can access the options using:
this.$myLibraryOrPluginName.options.theme
The way I've defined it here the options would not be reactive. You'd need to get Vue.observable involved to add reactivity:
Vue.prototype.$myLibraryOrPluginName = Vue.observable({ options })

Related

Is there a way to encapsulate Vuex store inside Vue plugin (its install function)?

I have a plugin and this plugin uses Vuex
// plugin.js
import Vuex from "vuex";
import store from "./store.js";
export default {
install(Vue, options) {
const storeInstance = new Vuex.Store(store);
Vue.prototype.$store = storeInstance;
}
};
And in that plugin I import a store object.
// store.js
export default {
actions: {
SOME_RANDOM_ACTION({ state, commit }) {
console.log("some random action");
}
}
};
Dispatching actions and using state is fine and works as expected.
But when I add this plugin to another Vue instance that uses vuex, store object re-initializes with new state.
// index.js
import Vue from "vue";
import Vuex from "vuex";
import App from "./App.vue";
import plugin from "./plugin.js";
Vue.use(Vuex);
Vue.use(plugin);
new Vue({
// WARN when i uncomment this next line of code Vuex gets re-initialized with new object
// store: new Vuex.Store({ state: { hello: "hix" } }),
components: {
App
}
}).$mount("#app");
When you uncomment store initialization, store that was defined in the plugin is now not available.
Currently, I have these solutions in mind:
Export my plugin store object to index.js main app, and use this store as a module.
Use some other state management.
Is there a way to use Vuex inside my plugin?
https://codesandbox.io/s/vibrant-sanne-67yej?file=/src/main.js:0-371
Vuex plugin uses store option to assign store instance to Vue.prototype.$store, similarly to your own plugin.
If the intention is to use multiple stores, their names shouldn't collide. The key is to name your store object inside plugin something other than $store
Vue.prototype.$myPluginStore = storeInstance;
But this still doesn't encapsulate $myPluginStore inside the plugin, as it is accessible within the app.
// App.vue
computed: {
appState() {
return this.$store.state;
},
pluginState() {
return this.$myPluginStore.state; // this is now accessible within the main app
}
}
It would be a reasonable solution to allow a store to be used as a module of existing store instead of creating a new store, but only when used within one app and not when used as a plugin for a package.
The main problem is that default store instance ($store) can make use of Vuex helpers - mapGetters, etc.
You can take advantage of the install method exposed by the plugin to get access to the store - which should be accessible from your other component.
One possible solution is to register your store in the index.js like:
import Vue from "vue";
import App from "./App.vue";
import store from "./store";
import plugin from "./plugin";
Vue.use(plugin);
new Vue({
store,
components: {
App
}
}).$mount("#app");
You can then expose $doStuff() and get access to $store in the plugin.js
export default {
install(Vue) {
Vue.prototype.$doStuff = function (payload) {
this.$store.dispatch("SOME_RANDOM_ACTION", payload);
};
}
};
The store instance is accessible from your plugin or all the other components.
You can see a working sample here

Vue - make helper for root component and all child component

Please is there a way to create a helper function on a root component in vue and also make the function accessible in all child components?
You can create helper functions and use it as a plugin. In case of you are using nuxt.js, you can create helpers.js in plugins and register it in nuxt.config.js file.
import Vue from 'vue'
import helpers from './helpers'
const plugin = {
install () {
Vue.prototype.$helpers = helpers
}
}
Vue.use(plugin)
In helpers.js, you can define all helper functions.
export default {
cloneObj(val) {
return JSON.parse(JSON.stringify(val));
}
};
Then you can use it in any child components like this:
this.$helpers.cloneObj()
You need to store it in a separate file because it's frustrating to pass it as a prop from one component to another and that's the main idea of why state management like Vuex is a better solution because it provides a centralized state manage which you can access from any component

How to import a js class in main.js file of a vue.js project and use it in all the components instead of importing in each component?

I have written some JS classes that I would like to import in the app.js/main.js file of my vue.js project so that I can instantiate them in the components. Right now I am having to import the same JS class in all the components where I need the class individually.
I've tried the import in the main.js file but the components don't recognize it.
in the main.js file, I am importing like as follows
import Permissions from './Permissions'
However, when I want to instantiate the Permissions class in my component like
data() {
permissions: new Permission({
some object properties...
})
}
the component doesn't know what Permissions is.
How do I let the component know what Permissions class is?
To do it in the vue way, you can create your own plugin or mixin. See detailed instructions here
So, you can create a permissions plugin in permissions-plugin.js
import Permissions from './Permissions'
const PermissionsPlugin = {
install(Vue, options) {
// This adds the $getPermissions method to all instances
Vue.prototype.$getPermissions = function(properties) {
return new Permission({
some object properties...
})
}
}
};
Then you have to tell vue to use your plugin:
import Vue from 'vue'
import PermissionsPlugin from './permissions-plugin.js'
import App from './App.vue'
// The plugin is loaded here.
Vue.use(PermissionsPlugin)
new Vue({
el: '#app',
render: h => h(App)
});
And lastly now from any component you should be able to use your function like:
this.$getPermissions(properties)

How to call all Custom Directive globally instead of calling each component?

I'm setting up a directive for my Vue project on separate individual files.
So far I have succeeded separating files and calling it globally but only possible to do it individually instead of exporting the entire directive and make it global.
**directive.js
const highlight ={
bind(el, binding, vnode) {
... some code
}
const highlight2 ={
... some code
}
export default {
highlight,
highlight
}
**main,js
import Vue from 'vue'
import App from './App.vue'
import * as directive from './directive.js'
Vue.directive(directive);
new Vue({
el: '#app',
render: h => h(App),
directive
})
so far I have been able to call this directive on my main.js but stuck on how to make it globally without calling every single component. like
import { highlight, highlight2} from './directive
Edit:
Found my way by looping through with forEach function.
Object.keys(directive).forEach(function(name){
Vue.directive(name, directive[name])
})

Using mixins with Vuejs

I'm currently learning how to develop an app with Vuejs. I have a main.js file with the code for setting up Vue.js. I created a new directory /mixins with a new file api.js. I want to use that as mixin so that every component can use a function to access my api. But I don't know how to do it.
This is my /mixins/api.js file:
export default{
callapi() {
alert('code to call an api');
},
};
This is my main.js file:
import Vue from 'vue';
import VueRouter from 'vue-router';
import VueResource from 'vue-resource';
import { configRouter } from './routeconfig';
import CallAPI from './mixins/api.js';
// Register to vue
Vue.use(VueResource);
Vue.use(VueRouter);
// Create Router
const router = new VueRouter({
history: true,
saveScrollPosition: true,
});
// Configure router
configRouter(router);
// Go!
const App = Vue.extend(
require('./components/app.vue')
);
router.start(App, '#app');
How can I include my mixin the right way now, so that every component has access to the callapi() function?
If you want to use a mixin on a specific component, rather than all components, you can do it like this:
mixin.js
export default {
methods: {
myMethod() { .. }
}
}
component.vue
import mixin from 'mixin';
export default {
mixins: [ mixin ]
}
Another thing you might consider is using a component extension design pattern i.e. creating a base component and then inheriting from that in sub components. It's a little more involved but keeps code DRY if you have many components sharing many options and perhaps inheriting the template too.
I've written about it on my blog if you're interested.
You can apply mixin globally using Vue.mixin
api.js
------
export default {
methods: {
callapi() {};
}
}
main.js
-------
import CallAPI from './mixins/api.js';
Vue.mixin(CallAPI)
As the documentation states you should use it carefully:
Use global mixins sparsely and carefully, because it affects every single Vue instance created, including third party components.

Categories

Resources