How to check if mixin was passed to the component? - vue.js

I am testing a VueJS 2 application using vue-test-utils library and I want to check if the mixin was passed to specific component, something like mounting the component using mount and accessing the mixin throw something like wrapper.vm.mixins.
I've already tried to access using: wrapper.vm.mixin, wrapper.vm.mixins, wrapper.mixin, wrapper.mixins, wrapper.vm.$mixin and wrapper.vm.$mixins.
My vue component is like this:
export default (Vue as VueConstructor<Vue & InstanceType<typeof MyMixin>>).extend({
name: 'MyComponent',
mixins: [MyMixin]
})

Mixin.ts
import Vue from 'vue';
const Sum = Vue.extend({
name: 'Sum',
methods: {
sum(numA: number, numB: number) {
return numA + numB;
}
}
});
export { Sum };
Answer 1:
You can import the mixin in your test and check if it was passed in wrapper.vm.$options.mixins array like this:
import { Sum } from './Sum'
const mixins = wrapper.vm.$options.mixins as any;
expect(mixins).toContain(Sum)
Answer 2:
You can test by the mixins names, but you need to be sure the mixin have a name property or it will return undefined:
const mixins = (wrapper.vm.$options.mixins as any).map((mixin: any) => mixin.options.name)
expect(mixins).toContain('Sum');

Related

Using script setup and reactive state vue 3 with toRefs

I'm trying to use script setup in my vue project.
Before using script setup, my script would be like this:
<script>
import Layout from '../containers/Layout.vue';
import { reactive, toRefs } from 'vue'
export default {
name: 'Home',
setup() {
const state = reactive({});
return {
...toRefs(state),
};
},
components: { Layout, Layout }
}
</script>
Now I have it like this:
<script setup>
import Layout from '../containers/Layout.vue';
import { reactive, toRefs } from 'vue'
const state = reactive({});
const props = defineProps({
header: String
})
</script>
The thing that I am unsure about is how do I use the toRefs in this case? In the first case we are returning the variables so I understand the way we used ...toRefs(state)
But now, how do I use it? Or is it not needed anymore?
Thanks
script setup implicitly translate variable definitions
const a = ...
to
return {
a: ...
}
There is no substitute for return {...dynamicValue} in script setup, which is intended to suite common use cases only. This would require to combine it with script.
return {...toRefs(state)} serves no good purpose because the resulting refs aren't used in script block. Even if they are, they are usually defined as separate reactive values instead of state object:
const a = ref(...)
const b = reactive(...)
return { a, b }; // Not needed in script setup
If there is ever a need to handle these values as a single object, they could be combined together:
const a = ref(...)
const b = reactive(...)
const state = reactive({ a, b });
return { a, b }; // Not needed in script setup
This works it the same way for both script and script setup.
If you want to access the values of the state reactive directly in script setup you can use Object destructuring like this :
import { reactive, toRefs } from "vue"
const state = reactive({ name: "admin", age: 20 })
const { name, age } = toRefs(state)
Then you can access your values directly in the template
<template>
{{ name }}
</template>
However this is much less convenient to have to retype all of your properties

How I can access $data variable of a Mixin.js file from Store.js using Vue.Js?

I have a Mixin file like this:
export default {
data: function() {
return {
analysisTime: "nothing",
phantomPrefix: "One more",
}
},
methods: {
isGeneric: function() {
return this.phantomPrefix
},
}
}
Whenever I call Mixin.js its methods are accessible but the $data variable is returning undefined.
For example, Whenever I called isGeneric function it's returning undefined instead of 'one more' because it's returns this.phantomPrefix.
Ps: If I access the same method of Mixin.js file from another component let's say Example.Vue it's working fine for me.
I have imported the Mixin file in store.js like:
import Mixin from "./mixins/Mixin";
and calling method like this: Mixin.methods.isGeneric()
Store.js file contains:
import Vue from "vue";
import Vuex from "vuex";
import tmpMixin from "./mixins/tmpMixin";
Vue.use(Vuex);
let vue_plugins = [];
export default new Vuex.Store({
plugins: vue_plugins,
state:{},
action:{
get_snippet_data_using_tomograph: function(data) {
let output = tmpMixin.methods.isGeneric()
}
}
});
Vue mixins are made for Vue components, it's not compatible with a Vuex module.
A mixins expect a Vue component instance, which is not provided by Vuex when you import it like this.
If you need to have a reusable code between a Vuex store and Vue components, you still can create a simple js file that exports a functions. But it won't be reactive or have a current state because it's not a Vue component.

Is there any way to pass mixin to component loaded through Vue Router

I have a mixin which contains beforeCreate lifecycle event.
I would like to import that mixin only into certain components, which are directly loaded through router. I don't want to go into each one of them and manually import the mixin, and I would also want to avoid loading it globally.
I believe that the proper way to do it is in route options, possibly overriding the component method, or by adding mixin option for the route (alongside props, meta...).
I requested this new feature, but I guess I was misunderstood, or I didn't understand the proposed solution.
I tried to create main Vue instance and extend it in my components, but the method only executed from the main component.
Is there any way to make this work?
Example of project code is here
Perhaps I've misunderstood what you're asking but I'd have thought you could achieve this by extending the component:
import Vue from 'vue'
import Router from 'vue-router'
import MyMixin from './mixins/MyMixin'
import MyList from './components/MyList'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/list',
name: 'list',
component: {
extends: MyList,
mixins: [MyMixin]
}
}
// ...
]
})
So rather than using MyList directly it's being extended and the mixin added in.
Or if you've got a lot of them and want to avoid duplication you could do something like this:
export default new Router({
routes: [
{
path: '/list',
name: 'list',
doMagic: true,
component: MyList
}
// ...
].map(route => {
if (route.doMagic) {
route.component = {
extends: route.component,
mixins: [MyMixin]
}
}
return route
})
})
Here I've used a flag called doMagic to determine which components to modify but if you just wanted to change all of them then you wouldn't need such a flag.
That doesn't take nested routes into account but it could be adapted if required.
Likewise if you're using async components then you'll have to fiddle around with the promises but the core principle should be exactly the same.
Update:
Based on the example code provided, the following seems to work with lazily loaded components:
const routes = [
// ... routes defined as usual
];
const newRoutes = routes.map(route => {
const originalComponent = route.component;
let component = null;
if (typeof originalComponent === 'object') {
// Components that aren't lazily loaded
component = wrap(originalComponent);
} else {
// Components that are lazily loaded
component = async () => {
const module = await originalComponent();
return wrap(module.default || module);
}
}
return {
...route,
component
};
function wrap (cmp) {
return {
extends: cmp,
mixins: [MyMixin]
}
}
});
export default new Router({
routes: newRoutes
});

How to pass a prop with a default value in case the prop is empty without using the prop's "default" property

I'm using a third party multi-language package where translation values can only be obtained/used from component's template, not from component's logic (at least I can't find how to the latter).
I need to pass such translation value in some component as a default prop value:
:placeholder="{ propValue ? propValue : $t('value') }
If placeholder is explicitly specified then use that. If not, use $t('value') instead. What's the right way to write this?
I tried this in reaction to #Michael's answer:
import i18n from "#/utilities/i18n";
export default {
data() {
return {
test: i18n.t('register.agreeTermsCaptionLink')
};
}
};
Getting this error:
_utilities_i18n__WEBPACK_IMPORTED_MODULE_7__.default.t is not a function
Solution 1
Just remove brackets from prop expression: :placeholder="propValue ? propValue : $t('value')"
Sotution 2
More complicated but helps to keep templates cleaner...
where translation values can only be obtained/used from component's template, not from component's logic
With vue-i18n you can of course obtain translation directly in code by using $t function injected into your component instance like this: this.$t('key.to.translation')
Only problem is, this is not possible to use to initialize props default values because this is just not available there.
But $t in fact just returns instance function of VueI18n global object. So if you setup your VueI18n like this:
import Vue from "vue";
import VueI18n from "vue-i18n";
Vue.use(VueI18n);
const messages = {
en: {
messages: {
placeholder: "Placeholder"
}
},
cz: {
messages: {
placeholder: "Zástupný symbol :)"
}
}
};
export default new VueI18n({
locale: "en",
messages
});
You can do this to provide translation as default value of your prop:
import i18n from "../i18n";
export default {
name: "HelloWorld",
props: {
placeholder: {
type: String,
// default: this.$t("value") - Wont work as `this` is not Vue instance
default: i18n.t("messages.placeholder")
}
}
};
Demo
You can set default value to prop like this:
props: {
propValue: {
type: String,
default: this.$t('value')
}
}
With that in your template you need just to assign that value like: :placeholder="propValue"
Is that what you trying to achive?
Good luck!

Vue.js / Mixins - Is there a way to get the global mixin-object outside of the vue component?

I am new with Vue.js
I am using Vue.js 2.4.4.
I have created the global mixin in my app.js file:
...
import router from './router'
...(some imports and plugins definitions)
Vue.use(VeeValidate);
Vue.component(VuePassword);
...
Vue.mixin({
data: function(){
return {
get auth(){
return Auth;
}
}
}
});
const app = new Vue({
el: '#root',
template: `<app></app>`,
components: { App },
router
});
This mixin imports some Auth object with validation methods e.t.c which needed to be in every component.
All of my components can check this mixin and it's working fine.
But I need to check the auth state after every route request, and I want to use my currently existing mixin, so I am trying to make something like this in my router.js file:
import Vue from 'vue'
import VueRouter from 'vue-router'
...
Vue.use(VueRouter);
const router = new VueRouter({
routes:[
...
]
});
router.beforeEach((to, from, next) => {
if(to.meta.requiresAuth) {
if(...call to mixin method) {
next();
} else {
next('/');
}
} else {
next();
}
});
export default router
Question:
Is there a way to get the global mixin object and change it's inner values or can you please give some small advise or example what is the right solution to this kind of tasks?
Or should I use the plugins instead of mixins?
I would rather create a seperate file for auth and not make it a mixin. Then using Vue.use() which will set auth on the vue object.
A sample of what the files might look like:
auth.js
export default function(Vue) {
Vue.auth = {
// do your auth logic
}
}
Then in your main js file
main.js
import Auth from './auth.js'
Vue.use(Auth);
Then you should be able to use Vue.auth
Another option would be keep using the mixin and pass the value to a store (like vuex) or create your own if your project is small...