Vue3.js and Pinia: Why is my store state undefined in VueDevtools? - vue.js

I am using Vue 3 with Pinia ^2.0.14. I'm importing Pinia into the app in main.ts like so:
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App).use(pinia).mount('#app')
I'm creating the store language.ts like so:
import { defineStore } from 'pinia'
export const useLanguageStore = defineStore({
id: 'language',
state: () => ({
language: 'English',
languages: ['English', 'Spanish'],
}),
})
and using it in LanguageDropdown.vue like so:
<script setup lang="ts">
import { useLanguageStore } from '#/store/language.ts'
const languageStore = useLanguageStore()
</script>
<template>
<select
v-model="languageStore.language">
<option
v-for="language in languageStore.languages"
:key="language"
:value="language"
>
{{ language }}
</option>
</select>
</template>
The code works as expected, but in the Vue devtools inspector languageStore.language, languageStore.languages, and language.state are undefined. Why would that be?
screen shot of Vue devtools inspector

I found a away to solve it, though I think its not perfect
// should use computed
const languages = computed(() => languageStore.languages )

Related

Cypress component testing with Vue-I18N

I am trying to use Cypress for component testing in my Vue app. I am using the vue-i18n library to provide localisation for the app. When attempting to test the rendering of my loading spinner component, I am getting the following error from the vue-i18n library:
SyntaxError: Need to install with `app.use` function
at createCompileError (http://localhost:5173/__cypress/src/node_modules/.vite/deps/vue-i18n.js?v=64756eb2:183:17)
at createI18nError (http://localhost:5173/__cypress/src/node_modules/.vite/deps/vue-i18n.js?v=64756eb2:2625:10)
at useI18n (http://localhost:5173/__cypress/src/node_modules/.vite/deps/vue-i18n.js?v=64756eb2:4231:11)
Previously to this, I was getting an error from Pinia. I resolved this by adding the following to cypress/support/component.ts:
import { createPinia, setActivePinia } from 'pinia';
setActivePinia(
createPinia()
);
My LoadingSpinner component code is as follows:
<script setup lang="ts">
import { computed } from "#vue/reactivity";
import { useLocaleStore } from "#/stores/locale";
//props
const { i18n } = useLocaleStore();
</script>
<template>
<div class="d-flex justify-content-center m-5">
<div
class="spinner-border text-primary"
:style="{ width, height }"
role="status"
>
<span class="visually-hidden">{{ i18n.t("loading") }}</span>
</div>
</div>
</template>
And the test code:
import LoadingSpinner from "../../src/components/ui/LoadingSpinner.vue";
describe("LoadingSpinner", () => {
it("renders", () => {
cy.mount(LoadingSpinner);
});
});
/stores/locale:
import { computed } from "vue";
import { defineStore } from "pinia";
import { useI18n } from "vue-i18n";
export const useLocaleStore = defineStore("locale", () => {
const i18n = useI18n({
useScope: "global",
inheritLocale: true,
});
const currentLocale = computed(() => i18n.locale);
const locales = computed(() => i18n.availableLocales);
return { i18n, currentLocale, locales };
});
I found this github release that implies I need to add vue-i18n as a plugin to the mount() call, but I can't work out how to do it. Does anyone know a solution?

Using Pinia with Vue.js Web components

Question has been updated
I'm trying to use a Pinia's store with web components created with Vue.js but I have this error in the console:
[Vue warn]: injection "Symbol(pinia)" not found at <HelloWorld.ce msg="message" >
I have a dead simple exemple.
main.ts
import { defineCustomElement } from 'vue'
import HelloWorld from './components/HelloWorld.ce.vue'
const ExampleElement = defineCustomElement(HelloWorld)
customElements.define('hello-world', ExampleElement)
store.ts
import { defineStore, createPinia, setActivePinia } from "pinia";
setActivePinia(createPinia());
export const useCounterStore = defineStore('counter', {
state: () => ({
counter: 0,
}),
actions: {
increment() {
this.counter++;
},
},
});
HelloWorld.ce.vue
<script setup lang="ts">
import { ref } from 'vue'
import { useCounterStore } from '../store.ts'
defineProps<{ msg: string }>()
const store = useCounterStore()
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" #click="store.increment()">count is {{ store.counter }}</button>
</div>
</template>
You're recreating pinia in the store after already creating it in main.js. Remove these lines from your store:
import { createPinia } from 'pinia'
const pinia = createPinia()

Share global pinia store between components

I have two vue components with own loaders, mounted into two already rendered DOM nodes:
Component A:
import { createApp } from 'vue'
import ComponentA from '#/Vue/ComponentA.vue';
import {createPinia} from 'pinia';
createApp(ComponentA).use(createPinia()).mount(document.querySelector('.c-component-a'));
Component B:
import { createApp } from 'vue'
import ComponentB from '#/Vue/ComponentB.vue';
import {createPinia} from 'pinia';
createApp(ComponentA).use(createPinia()).mount(document.querySelector('.c-component-b'));
Now, I want to load a global pinia store into multiple components:
Pinia store:
import {defineStore} from 'pinia';
export type RootState = {
foobar: number;
}
export const useGlobalApplicationStore = defineStore({
id: 'global',
state: () => ({
foobar: 100
} as RootState),
actions: {
setFoobar(payload: number): void {
this.foobar = payload;
}
},
getters: {
getFoobar(state: RootState): number {
return state.foobar;
}
}
})
If component A sets a value in this store, component B should react to changes.
Component A:
const globalApplicationStore = useGlobalApplicationStore();
setTimeout(() => {
globalApplicationStore.setFoobar(400);
}, 2000);
Output of {{globalApplicationStore.foobar}} in component A changes from 100 to 400 after 2 seconds, as expected.
Component B:
const globalApplicationStore = useGlobalApplicationStore();
Output of {{globalApplicationStore.foobar}} in component B does not change from 100 to 400.
I guess, both components loads the store as local instances.
How can I share a store between seperate mounted components?
After a long search I found out that it's pretty easy (as often...).
In my case, I use the progressive aspect of Vue.js to put apps in different places of my HTML code. Specifically, I want to populate a shopping cart icon in the header of my layout with the number of items. So I am using a App.vue for my product-app and a Basket.vue for my basket-indicator.
The simple trick is to instantiate pinia just once. Let's say you have a main.js as an entry-point of your app:
import { createApp } from "vue";
import App from "./App.vue";
import Basket from "./Basket.vue";
import {createPinia} from 'pinia';
const pinia = createPinia();
// Init App
createApp(App)
.use(pinia)
.mount("#app");
// Init Basket
createApp(Basket)
.use(pinia)
.mount("#basket");
In your App.vue you just import your stores (in my case a product store and a cart store).
<script setup>
... import components ...
import {useProductStore} from "#/stores/ProductStore";
import {useCartStore} from "#/stores/CartStore";
const productStore = useProductStore();
const cartStore = useCartStore();
productStore.fill();
</script>
<template>
... your components ...
</template>
the same in your Basket.vue:
<script setup>
import CartWidget from "#/components/CartWidget.vue";
import {useCartStore} from "#/stores/CartStore";
import {useProductStore} from "#/stores/ProductStore";
const cartStore = useCartStore();
const productStore = useProductStore();
productStore.fill();
</script>
<template>
<div class="container">
<CartWidget/>
</div>
</template>
That's it.
"pinia": "^2.0.17",
"vue": "^3.2.39"

Vue.js with pinia npm server error variable is assigned a value but never used

I am using this video https://www.youtube.com/watch?v=Ok6vO98RV_Q&t=108s, but I keep getting the error: "error 'counter' is assigned a value but never used no-unused-vars". What am I missing?
<template>
{{ counter }}
</template>
<script>
import { storeToRefs } from "pinia";
import useStore from "../store/useStore";
const main = useStore;
const { counter } = storeToRefs(main);
</script>
store:
import { defineStore } from "pinia";
// import { auth } from "./auth.module";
export const useStore = defineStore("main", {
state: () => ({
counter: null,
}),
});
You have to specify <script setup> for Composition API or else you have to expose the properties manually to the template by returning them.
<template>
{{ counter }}
</template>
<script setup>
import { storeToRefs } from "pinia";
import useStore from "../store/useStore";
const main = useStore();
const { counter } = storeToRefs(main);
</script>

WebGL Earth or globe.gl in VUE.JS

Can I use "WebGL Earth" or "globe.gl" in vue.js? I search a lot but what I found was that there is "react-globe.gl" for react developers, but can't find the same for vue.
If I can use any of them in vue, how can I import and initialize it?
I am currently am using globe.gl with vue 3, got it running like this.
Can also checkout a template repo I have https://github.com/GBerghoff/Globe.gl-with-Vue-3
<template>
<div ref="globeDiv"></div>
</template>
<script>
import Globe from "globe.gl";
import { ref, onMounted } from "vue";
export default {
setup() {
const globeDiv = ref(null);
onMounted(() => {
const myGlobe = Globe();
myGlobe(globeDiv.value).globeImageUrl(
"//unpkg.com/three-globe/example/img/earth-night.jpg"
);
});
return {
globeDiv,
};
},
};
</script>