Vue: how to apply multiple global mixins - vue.js

I am currently working on a Vue project including multiple applications.
It has project-wide used methods and application-wide used methods.
Therefore, I created 2 mixins which need to be available in every Vue component. However, I don't know how to implement it using Vue.mixin().
Please help.
I tried this. Not work..
The error says "Cannot read property 'VALIDATION' of undefined".
Somehow URL is not imported. URL() returns a object where urls are defined according to either DEV or PRODUCTION mode.
import global_mixin from './global_mixin.js'
import application_mixin from './application_mixin.js'
Vue.mixin(global_mixin)
Vue.mixin(application_mixin)
new Vue({
el: '#app',
render: h => h(App)
})
global_mixin.js
export default {
data() {
return {
// data items
}
}
}
application_mixin.js
import { URL } from './_util'
export default {
data() {
return {
URL_VALIDATION: URL().VALIDATION
}
}
}
_util.js
import store from './_store'
export const URL = () => {
const urls = {
PROD: {
VALIDATION: '/api/web/company/profile/validation',
PROFILE: '/api/web/company/profile',
COUNTRY: '/api/app/countries',
ADDRESS: '/api/web/address'
},
DEV: {
PROFILE: '/data/profile_company.json',
VALIDATION: 'https://httpbin.org/post',
COUNTRY: '/data/countries.json',
ADDRESS: '/data/zip.json'
}
}
return urls[store.getters.mode]
}

Related

How to get the this instance in vue 3?

In vue 2+ I can easily get the instance of this as a result I can write something like this,
// main.js
app.use(ElMessage)
// home.vue
this.$message({
showClose: true,
message: 'Success Message',
type: 'success',
})
What should I do for vue 3 as,
Inside setup(), this won't be a reference to the current active
instance Since setup() is called before other component options are
resolved, this inside setup() will behave quite differently from this
in other options. This might cause confusions when using setup() along
other Options API. - vue 3 doc.
Using ElMessage directly
ElementPlus supports using ElMessage the same way as $message(), as seen in this example:
import { ElMessage } from 'element-plus'
export default {
setup() {
const open1 = () => {
ElMessage('this is a message.')
}
const open2 = () => {
ElMessage({
message: 'Congrats, this is a success message.',
type: 'success',
})
}
return {
open1,
open2,
}
}
}
Using $message()
Vue 3 provides getCurrentInstance() (an internal API) inside the setup() hook. That instance allows access to global properties (installed from plugins) via appContext.config.globalProperties:
import { getCurrentInstance } from "vue";
export default {
setup() {
const globals = getCurrentInstance().appContext.config.globalProperties;
return {
sayHi() {
globals.$message({ message: "hello world" });
},
};
},
};
demo
Note: Being an internal API, getCurrentInstance() could potentially be removed/renamed in a future release. Use with caution.
Providing a different method where the idea is to set a globally scoped variable to the _component property of the viewmodel/app or component:
pageVM = Vue.createApp({
data: function () {
return {
renderComponent: true,
envInfo: [],
dependencies: [],
userGroups: []
}
},
mounted: function () {
//Vue version 3 made it harder to access the viewmodel's properties.
pageVM_props = pageVM._component;
this.init();
},

VueJS - function in import js file not getting triggered

We are building a web application using Vue JS and PHP, we are new to Vue JS. The server-side execution is fine, the API is able to fetch data as JSON. While trying out a static array display before making the API call, we find that the function in imported "app.js" is not getting called and the table displayed is empty. Please let us know what we might be doing wrong. Appreciate your help.
import Vue from 'vue';
export const MY_CONST = 'Vue.js';
export let memberList = new Vue({
el: '#members',
data: {
members: []
},
mounted: function () {
this.getAllMembers();
},
methods: {
getAllMembers: function () {
/*
axios.get("https://xxxxxx.com/services/api.php")
.then(function (response) {
memberList.members = response.data.members;
});
*/
memberList.members = [{ "empname": "Dinesh Dassss" },
{ "empname": "Kapil Koranne" }];
}
}
});
This is the Vue component. The members object is empty.
<script>
import * as mykey from './app.js'
export default {
name: 'Home',
props: {
msg: String
},
data() {
return {
message: `Hello ${mykey.MY_CONST}!`,
members: mykey.memberList.members
}
}
};
</script>
You can also use this reference for current instance reference:
getAllMembers: function () {
var me = this;
/*
axios.get("https://xxxxxx.com/services/api.php")
.then(function (response) {
// direct this not works here but we have
//saved this in another variable and scope of a var is there
me.members = response.data.members;
});
*/
// this reference works fine here.
this.members = [{ "empname": "Dinesh Dassss" },
{ "empname": "Kapil Koranne" }];
}

Vuex: createNamespacedHelpers with dynamic namespace

In almost all guides, tutorial, posts, etc that I have seen on vuex module registration, if the module is registered by the component the createNamespacedHelpers are imported and defined prior to the export default component statement, e.g.:
import {createNamespacedHelpers} from 'vuex'
const {mapState} = createNamespacedHelpers('mymod')
import module from '#/store/modules/mymod'
export default {
beforeCreated() {
this.$store.registerModule('mymod', module)
}
}
this works as expected, but what if we want the module to have a unique or user defined namespace?
import {createNamespacedHelpers} from 'vuex'
import module from '#/store/modules/mymod'
export default {
props: { namespace: 'mymod' },
beforeCreated() {
const ns = this.$options.propData.namespace
this.$store.registerModule(ns, module)
const {mapState} = createNamespacedHelpers(ns)
this.$options.computed = {
...mapState(['testVar'])
}
}
}
I thought this would work, but it doesnt.
Why is something like this needed?
because
export default {
...
computed: {
...mapState(this.namespace, ['testVar']),
...
},
...
}
doesnt work
This style of work around by utilising beforeCreate to access the variables you want should work, I did this from the props passed into your component instance:
import { createNamespacedHelpers } from "vuex";
import module from '#/store/modules/mymod';
export default {
name: "someComponent",
props: ['namespace'],
beforeCreate() {
let namespace = this.$options.propsData.namespace;
const { mapActions, mapState } = createNamespacedHelpers(namespace);
// register your module first
this.$store.registerModule(namespace, module);
// now that createNamespacedHelpers can use props we can now use neater mapping
this.$options.computed = {
...mapState({
name: state => state.name,
description: state => state.description
}),
// because we use spread operator above we can still add component specifics
aFunctionComputed(){ return this.name + "functions";},
anArrowComputed: () => `${this.name}arrows`,
};
// set up your method bindings via the $options variable
this.$options.methods = {
...mapActions(["initialiseModuleData"])
};
},
created() {
// call your actions passing your payloads in the first param if you need
this.initialiseModuleData({ id: 123, name: "Tom" });
}
}
I personally use a helper function in the module I'm importing to get a namespace, so if I hadmy module storing projects and passed a projectId of 123 to my component/page using router and/or props it would look like this:
import projectModule from '#/store/project.module';
export default{
props['projectId'], // eg. 123
...
beforeCreate() {
// dynamic namespace built using whatever module you want:
let namespace = projectModule.buildNamespace(this.$options.propsData.projectId); // 'project:123'
// ... everything else as above
}
}
Hope you find this useful.
All posted answers are just workarounds leading to a code that feels verbose and way away from standard code people are used to when dealing with stores.
So I just wanted to let everyone know that brophdawg11 (one of the commenters on the issue #863) created (and open sourced) set of mapInstanceXXX helpers aiming to solve this issue.
There is also series of 3 blog posts explaining reasons behind. Good read...
I found this from veux github issue, it seems to meet your needs
https://github.com/vuejs/vuex/issues/863#issuecomment-329510765
{
props: ['namespace'],
computed: mapState({
state (state) {
return state[this.namespace]
},
someGetter (state, getters) {
return getters[this.namespace + '/someGetter']
}
}),
methods: {
...mapActions({
someAction (dispatch, payload) {
return dispatch(this.namespace + '/someAction', payload)
}
}),
...mapMutations({
someMutation (commit, payload) {
return commit(this.namespace + '/someMutation', payload)
})
})
}
}
... or maybe we don't need mapXXX helpers,
mentioned by this comment https://github.com/vuejs/vuex/issues/863#issuecomment-439039257
computed: {
state () {
return this.$store.state[this.namespace]
},
someGetter () {
return this.$store.getters[this.namespace + '/someGetter']
}
},

unexpected token error importing a vuex store to laravel page

New-ish to Vue nd extremely new to Vuex. Im trying to import a store to my main page from which all my components branch out, but I keep getting an "unexpected token {" error in the browser console. I read through the documentation, but I cant find anything that would address this issue. I have tried changing every bit of syntax I can, and it doesnt seem to make a difference. The brackets around store in the import appear to be the problem, but when I remove them, i just get a new "unexpected identifier", or an "unexpected string" error. Am I importing it incorrectly? This format works fine on all my components, just not on this new vue instance.
vuex-test.blade.php
#extends('core.core_layouts.core_blank')
#section('browsertitle')
#endsection
#section('top-css')
#endsection
#section('breadcrumb')
#endsection
#section('main')
<component :is="currentView" v-bind="currentProperties"></component>
#endsection
#section('bottom-js')
<script>
import { store } from './../stores/store1.js';
var app = new Vue({
el:"#app",
store,
data: {
currentView: 'org-list',
choseOrg: {{ $org }},
}, // end data
computed: {
currentProperties: function() {
if (this.currentView === 'org-list') { return { } }
if (this.currentView === 'add-org') { return { parentOrg: '' } }
}
},
mounted : function() {
}, // end mounted
methods: {
}, // end methods
components: {
},
});
</script>
#endsection
store1.js
export const store = new Vuex.Store({
state: {
safelyStoredNumber: 'ret',
count: 2,
},
mutations: {
setOrgIdentity(state, orgID) {
state.OrgID = orgID
}
}
});
Per comments:
Yes, I get the error at the browser.
import won't work there. It is meant to run on node.js, during the build phase of a vue-cli-based project.
If you are deploying the code directly to browser, you will need to use code that is supported by the browser. In this case, the solution to import other JavaScript files is standard <script> tags.
So, in your code, change:
import { store } from './../stores/store1.js';
to something like (change the path to what is more appropriate):
<script src="./../stores/store1.js"></script>
And in store1.js (because export is too meant for node.js), replace:
export const store = new Vuex.Store({
With:
window.store = new Vuex.Store({

Inject Custom Store into Application

Vuex allows you to inject the store into your root instance, making it accessible via this.$store in all child components.
Without Vuex, is it possible to inject a custom store implementation into child components?
e.g.
// main.js
let app = new Vue({router, store, ...App}).$mount("#flightdeck-app")
export { app, store, router }
// SomeComponent.vue
export default {
name: "Overview",
components: { "credentials": Credentials },
computed: {
count() {
// injected store; is currently undefined.
return this.$store.state.items.length
}
},
Attempting to access this.$store results in undefined in child components, as Vuex seemingly has additional hooks to make this happen.
You may create a custom plugin for vue that register an initialize your custom store (or anything, I'll create a logger object just for demonstration).
For example you could have
//myLogger.js
export default {
install(Vue, options) {
function log(type, title, text) {
console.log(`[${type}] ${title} - ${text}`);
}
Vue.prototype.$log = {
error(title, text) { log('danger', title, text) },
success(title, text) { log('success', title, text) },
log
}
}
}
Before your main Vue instance tell to register your plugin
//main.js
import Logger from './path/to/myLogger';
Vue.use(Logger);
var vm = new Vue({
el: '#app',
template: '<App/>',
components: { App }
})
Now you can call this.$log on any child component
//myComponent.vue
export default {
data() {
return {};
},
methods: {
Save() {
this.$log.success('Transaction saved!');
}
}
}
Hope it helps, for more detail please see Vue plugins documentation
Just use in your components files:
import store from './vuex/store.js'
Place your store in separate file to get it clear.
Import store to every component where you need store.