Vue 3 - Options API vs Composition API - Load external file into component - javascript

I have the following file which is an external file with functions (language.js). I also have created a other component which needs to use language.js (it needs to use the languageText funcion inside language.js). I did used it in a Composition API component. But now I want to get it working in a Options API component. Please check the function inside methods called languageSelector. Inside this function I want to use the global function from language.js (languageText())
Any help?
Options API template (Form.vue)
<script>
import languageText from '#/composables/language';
export default defineComponent({
name: 'Form',
props: {
processingData: Object,
formData: Object
},
emits: ["gateway"],
components: {
Icon
},
data() {
return {
fieldData: this.formData,
}
},
methods: {
languageSelector(data) {
const h = languageText(data) **I want to USE the FUNCTION here.**
console.log(h)
return languageText(data)
},
}
language.js
import { ref, computed, watch } from 'vue';
import { useI18n } from "vue-i18n";
import { useStore } from "vuex";
export default function language() {
const store = useStore();
const i18n = useI18n();
const language = computed(() => {
return store.getters.currentUser.language;
});
function languageText(json) {
const obj = JSON.parse(json)
return obj[language.value]
}
return {
languageText
}
}

Related

How can I modify the value of state in pinia in vue3 component test and affect the component?

Using vue-test-utils to test the component using pinia, I need to modify the value of the state stored in pinia, but I have tried many methods to no avail. The original component and store files are as follows.
// HelloWorld.vue
<template>
<h1>{{ title }}</h1>
</template>
<script>
import { useTestStore } from "#/stores/test";
import { mapState } from "pinia";
export default {
name: "HelloWorld",
computed: {
...mapState(useTestStore, ["title"]),
},
};
</script>
// #/stores/test.js
import { defineStore } from "pinia";
export const useTestStore = defineStore("test", {
state: () => {
return { title: "hhhhh" };
},
});
The following methods have been tried.
Import the store used within the component to the test code and make changes directly, but the changes cannot affect the component.
// test.spec.js
import { mount } from "#vue/test-utils";
import { createTestingPinia } from "#pinia/testing";
import HelloWorld from "#/components/HelloWorld.vue";
import { useTestStore } from "#/stores/test";
test("pinia in component test", () => {
const wrapper = mount(HelloWorld, {
global: {
plugins: [createTestingPinia()],
},
});
const store = useTestStore();
store.title = "xxxxx";
console.log(wrapper.text()) //"hhhhh";
});
Using the initialState in an attempt to overwrite the contents of the original store, but again without any effect.
// test.spec.js
import { mount } from "#vue/test-utils";
import { createTestingPinia } from "#pinia/testing";
import HelloWorld from "#/components/HelloWorld.vue";
test("pinia in component test", () => {
const wrapper = mount(HelloWorld, {
global: {
plugins: [createTestingPinia({ initialState: { title: "xxxxx" } })],
},
});
console.log(wrapper.text()) //"hhhhh";
});
Modify the TestingPinia object passed to global.plugins in the test code, but again has no effect.
// test.spec.js
import { mount } from "#vue/test-utils";
import { createTestingPinia } from "#pinia/testing";
import HelloWorld from "#/components/HelloWorld.vue";
test("pinia in component test", () => {
const pinia = createTestingPinia();
pinia.state.value.title = "xxxxx";
const wrapper = mount(HelloWorld, {
global: {
plugins: [pinia],
},
});
console.log(wrapper.text()) //"hhhhh";
});
Use global.mocks to mock the states used in the component, but this only works for the states passed in with setup() in the component, while the ones passed in with mapState() have no effect.
// test.spec.js
import { mount } from "#vue/test-utils";
import { createTestingPinia } from "#pinia/testing";
import HelloWorld from "#/components/HelloWorld.vue";
test("pinia in component test", () => {
const wrapper = mount(HelloWorld, {
global: {
plugins: [createTestingPinia()],
mocks: { title: "xxxxx" },
},
});
console.log(wrapper.text()) //"hhhhh"
});
This has been resolved using jest.mock().
import { mount } from "#vue/test-utils";
import { createPinia } from "pinia";
import HelloWorld from "#/components/HelloWorld.vue";
jest.mock("#/stores/test", () => {
const { defineStore } = require("pinia");
const useTestStore = defineStore("test", { state: () => ({ title: "xxxxx" }) });
return { useTestStore };
});
test("pinia in component test", () => {
const wrapper = mount(HelloWorld, {
global: { plugins: [createPinia()] },
});
expect(wrapper.text()).toBe("xxxxx");
});
Thanks to Red Panda for this topic. I use "testing-library", and "vue-testing-library" instead of "vue-test-utils" and "jest", but the problem is the same - couldn't change pinia initial data of the store.
I finally found a solution for this issue without mocking the function.
When you $patch data, you just need to await for it. Somehow it helps. My code looks like this and it totally works:
Popup.test.js
import { render, screen } from '#testing-library/vue'
import { createTestingPinia } from '#pinia/testing'
import { popup } from '#/store1/popup/index'
import Popup from '../../components/Popup/index.vue'
describe('Popup component', () => {
test('displays popup with group component', async () => {
render(Popup, {
global: { plugins: [createTestingPinia()] }
})
const store = popup()
await store.$patch({ popupData: 'new name' })
screen.debug()
})
})
OR you can set initialState using this scheme:
import { render, screen } from '#testing-library/vue'
import { createTestingPinia } from '#pinia/testing'
import { popup } from '#/store1/popup/index'
import Popup from '../../components/Popup/index.vue'
test('displays popup with no inner component', async () => {
const { getByTestId } = render(Popup, {
global: {
plugins: [
createTestingPinia({
initialState: {
popup: {
popupData: 'new name'
}
}
})
]
}
})
const store = popup()
screen.debug()
})
Where popup in initialState - is the imported pinia store from #/store1/popup. You can specify any of them there the same way.
Popup.vue
<script>
import { defineAsyncComponent, markRaw } from 'vue'
import { mapState, mapActions } from 'pinia'
import { popup } from '#/store1/popup/index'
export default {
data () {
return {}
},
computed: {
...mapState(popup, ['popupData'])
},
....
I'm working on a project using Vue 3 with composition API styling.
Composition API is used for both components and defining my store.
Here is my store
player.js
import { defineStore } from 'pinia'
import { ref, reactive } from 'vue'
export const usePlayerStore = defineStore('player',()=>{
const isMainBtnGameClicked = ref(false)
return { isMainBtnGameClicked }
})
MyComponent.vue
//import { usePlayerStore } from '...'
const playerStore = usePlayerStore()
playerStore.isMainBtnGameClicked = true
isMainBtnGameClicked from my store is updated properly.
You can also update variables from components by passing them by reference to the pinia store. It's working in my project.
For sake of saving future me many hours of trouble, there is a non-obvious thing in play here - the event loop. Vue reactivity relies on the event loop running to trigger the cascade of state changes.
When you mount/shallowMount/render a component with vue-test-utils, there is no event loop running automatically. You have to trigger it manually for the reactivity to fire, e.g.
await component.vm.$nextTick;
If you don't want to mess around with ticks, you have to mock the store state/getters/etc. (which the docs strongly lean toward, without explaining the necessity). Here OP mocked the whole store.
See also: Vue-test-utils: using $nextTick multiple times in a single test

'How to acccess data property inside setup in vue 3?

I am trying to access data property inside setup() in vue 3 but is giving me this error
TypeError: Cannot read property '$localStorage' of undefined
data()
data() {
return {
$localStorage: {},
}
setup
setup() {
this.$localStorage
}
How i can access this property inside setup()?
One solution would be importing getCurrentInstance from vue and use it in onMounted lifecycle or in a consumable:
import { onMounted, getCurrentInstance } from "vue";
export default {
data() {
return {
key: "test",
};
},
setup() {
onMounted(() => {
console.log(getCurrentInstance().data.key);
});
},
};
However, this usage is highly discouraged as stated in here.
Instead you could define that data property in setup using ref or reactive like this:
import { ref } from "vue";
export default {
setup() {
const $localStorage = ref({});
},
};
or
import { reactive } from "vue";
export default {
setup() {
const $localStorage = reactive({});
},
};
These solutions will work if your data and setup function will be in the same component.
If you are trying to access to a child component, you can use ref for accomplishing this task. More detailed information about ref is here

Using `useStore` API with vuex 4.x

Follow the official example to export your own useStore, and then use it in the component.
import { createStore, Store, useStore as baseUseStore } from 'vuex';
export const key: InjectionKey<Store<RootState>> = Symbol();
export function useStore() {
return baseUseStore(key);
}
use in the component
setup() {
const store = useStore();
const onClick = () => {
console.log(store)
store.dispatch('user/getUserInfo');
}
return {
onClick,
}
},
After running, store is undefined.
It can be obtained normally when I use it in the methods attribute
methods: {
login() {
this.$store.dispatch('user/getToken')
}
}
why? how to fix it
In that simplifying useStore usage tutorial, you still need to register the store and key in main.ts as they did. You will get undefined if you don't do this:
// main.ts
import { store, key } from './store'
const app = createApp({ ... })
// pass the injection key
app.use(store, key)
The reason is that baseUseStore(key) has no meaning until that's done.

vuex unknown action type: showRegisterLogin/show

I wrote the following code but it shows an error. What is the reason for this?
Error
[vuex] unknown action type: showRegisterLogin/show
HomePage.vue // component
When using the sh method This error is caused
import { mapState, mapActions } from "vuex";
export default {
name: "HomePage",
components: {
RegisterLogin
},
data() {
return {}
},
computed: {
...mapState({
showRegisterLogin: state => state.showRegisterLogin.show
}),
},
methods: {
sh() {
this.$store.dispatch('showRegisterLogin/show');
}
}
}
/ store / modules / showRegisterLogin.js
// States
const state = {
show: false,
};
// Getters
const getter = {
show (state) {
return state.show;
}
};
// Mutations
const mutation = {
showPage (state) {
return state.show = true;
},
hidePage (state) {
return state.show = false;
}
};
// Actions
const action = {
show({ commit }) {
commit('showPage');
},
hide({ commit }) {
commit('hidePage');
}
};
export default {
namespaced: true,
state,
getter,
mutation,
action
}
/ store / store.js
'use strict';
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
import showRegisterLogin from "./modules/showRegisterLogin";
export default new Vuex.Store({
modules: {
showRegisterLogin,
}
});
I also imported the store.js file into app.js and registered it in new vue
The structure of the store, module, and component are fine, except for the name of the store objects in your module:
getter should be getters
mutation should be mutations
action should be actions
Probably just typos. Those can't be arbitrarily named since Vuex looks specifically for those keys.

vuex and axios debugging

I'm going crazy, I have a working api that sends data, I connected it to a VueJS app and it was working fine. I'm trying to implement Vuex and I'm stuck. Here's my store.js file
import Vue from 'vue';
import Vuex from 'vuex';
import axios from 'axios'
Vue.use(Vuex);
const state = {
message: "I am groot",
articles: []
}
const getters = {
getArticles: (state) => {
return state.articles;
}
}
const actions = {
getArticles: ({ commit }, data) => {
axios.get('/articles').then( (articles) => {
commit('GET_ARTICLES', articles);
console.log(articles); // Trying to debug
}, (err) => {
console.log(err);
})
}
}
const mutations = {
GET_ARTICLES: (state, {list}) => {
state.articles = list;
}
}
const store = new Vuex.Store({
state,
getters,
mutations,
actions,
mutations
});
console.log(store.state.articles); // this lines works but data is empty
export default store
The console.log within axios call doesn't run and store.state.articles is empty. I must be missing something. I'm just trying to console the articles data on page load...
Please help, I'm near insanity :)
Component :
<template>
<div class="container">
<h1>Test component yo !</h1>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
name: 'Test',
computed: {
message() {
return this.$store.state.message
}
},
mounted: () => {
this.$store.dispatch('getArticles')
}
}
</script>
App.js :
import Vue from 'vue';
import ArticlesViewer from './articles_viewer.vue';
import UserArticles from './user_articles.vue';
import App from './app.vue'
import store from './store'
new Vue({
el: '#app-container',
store,
render: h => h(App)
})
You define the mounted lifecycle hook of your component using an arrow function.
As per the documentation:
Don’t use arrow functions on an instance property or callback (e.g. vm.$watch('a', newVal => this.myMethod())). As arrow functions are bound to the parent context, this will not be the Vue instance as you’d expect and this.myMethod will be undefined.
You should define it like so:
mounted: function () {
this.$store.dispatch('getArticles');
}
Or, use the ECMAScript 5 shorthand:
mounted() {
this.$store.dispatch('getArticles');
}
Now, your dispatch method will be called correctly, populating your articles array.

Categories

Resources