Using i18n-nuxt $t in Vuex store - vue.js

My problem is kinda specific. I'm using Vuex and Nuxt for this project. For the dependency we use i18n-nuxt but somehow I cannot use $t('some.translation') in the $store while in the components it works just fine. I tried every possible combinations I can imagine but still the result leads me to the same error.
ReferenceError $t is not defined
or
Cannot read the property of i18n
So in this case I could use some help either to solve this problem or it would be perfect if someone shows me the way how to use i18n as a filter. (Which I think is the only way around.)
For example this code block is from the $store.state
sortMethods: [
{ id: 'airline', name: this.i18n.$t('Airline'), asc: 'A', desc: 'Z', defaultDirection: 'asc' },
You can imagine I cannot translate them where they are.

It's possible to use t in mutations and actions with this.$i18n.t. Ex.:
export const mutations = {
foo() {
console.log(this.$i18n.t("bar"));
}
}
However I haven't found a way to use it in the state or getters because they don't have access to this.

i18n module is not injected in the store context by default , and the “this” keyword cannot access to the module too.
Is not a better approach to save a key from the translation file as a value in the store or even use the id of the object as the same translation key, and then in the component use the $t function to translate?
Like the following
store.js
const state = {
sortMethods: [
{
id: “airline”,
key_name: “airline”
}
]
}
And then in the select tag from your component.vue:
<option v-for=“method in $store.state.sortMethods” :key=“method.id”> {{$t(method.key_name)}}</option>
This will give you a translated list from the store. No need to translate directly.
You can use an id, a key, even the name, always that the translation key is the same in your lang file.

Related

Can't have a Vue shortcut (by reference) to a reactive variable?

I have a Pinia store with an object "objData", which holds one or more objects, with some additional metadata, which ends up becoming a fairly long variable. It has to be used in quite a number of places, therefore I made a "shortcut" variable instead to the "data" property. However, this shortcut fails to be reactive, whereas the variable i'm pointing to is reactive.
The Pinia object looks like:
objData: {
"fruit": {
data: {...},
...
},
"candy": {
data: {...},
...
},
}
The setup-function:
setup() {
const myStore = useMyStore()
// const fruit = myStore.objData['fruit'].data // <- direct, doesn't work
// const fruit = reactive(myStore.objData['fruit'].data) // <- reactive, doesn't work
const fruit = computed(() => myStore.objData['fruit'].data) // works
return {
myStore,
fruit,
}
}
The data change: (I'm sure I don't need to both do reactive() and refs(), or any at all, but I've tried all kinds of things to get reactivity in my shortcut). This happens in a composable that has access to the store.
if (!("fruit" in store.objData)) {
set(myStore.objData, "fruit", reactive({
data: ref(null),
}))
}
set(myStore.objData["fruit"], 'data', objNewData)
The page:
<div>
{{myStore.objData['fruits'].data.fruit_name}} OK
{{fruit.fruit_name}} OK, if computed(), otherwise not reactive
</div>
Unless I'm using a computed, I only get the inital value, which doesn't get updated when the store updates.
Is it actually bad/expensive/wrong to use a computed() to have a reactive data object in the page in this way? It "feels" wrong, but other than that I have no arguments against it.
(Why) is it not possible to simply make a variable by reference to a reactive variable, I always thought you're just pointing to a memory address.
I'm struggeling to provide an example, as this thing is so deeply integrated in my app. I'm at this point hoping for a glaring mistake on my part, or a simple answer that explains it.
Note 1: I'm using Vue2 with the composition API add-on.
Note 2: This is a very simplified example.

How to share variable with Nuxt.js from layout to pages?

I actually work on a project that consists of display data from Jsons on a Nuxt.js website using Vuetify. I have created a selector in my layout to choose which Json the user wants to display. I need to access this variable from all the different pages of my project.
Here is what my default.vue looks like :
<template>
<v-overflow-btn
:items="json_list"
label="Select an Json to display"
v-model="selected_json"
editable
mandatory
item-value="text"
></v-overflow-btn>
</template>
<script>
export default {
data() {
return {
selected_json: undefined,
json_list: [
{text: "first.json"},
{text: "second.json"},
],
}
}
}
</script>
The variable I would like to access from all my different pages is selected_json.
I see many things on the internet such as Vuex or a solution that consist to pass the variable with the URL. But I'm kind of newby in web programming (started Vue/Nuxt one week ago) and I don't really understand how to apply this in my project. So if there is a more easy way to do it or a good explaination, I'm interested!
Thanks in advance for your help :)
Using Vuex we can easily achieve what you want.
First of all create a file index.js in folder store (if you don't have a store folder then create it in the same directory where your pages, plugins, layouts etc folders are). Then paste this piece of code in index.js
//store/index.js
export const state = () => ({
selected_json: null
})
By doing this we set Vuex up. More precisely just state part of Vuex where if you don't know we store data accessible across your project.
Now we have to assign data from your default.vue to Vuex. We can achieve this by creating a mutation function through which we change state in Vuex. Add this to your index.js
//store/index.js
export const mutations = {
setSelectedJson(state, selectedJson) {
state.selected_json = selectedJson
}
}
Here function setSelectedJson takes two params, state which is automatically passed in by Nuxt.js and it includes all our Vuex state data. The second parameter selected_json we pass in ourselves.
Now in your default.vue we need to add a watcher for selected_json so we can update our Vuex when selected_json gets updated.
//layouts/default.vue
export default {
watch: {
selected_json(newValue) {
this.$store.commit("setSelectedJson", newValue)
}
}
}
We are almost done.
The last thing we need to do is to make a getter which is used to retrieve values from Vuex. A getter like this will do its job.
//store/index.js
export const getters = {
getSelectedJson(state) {
return state.selected_json
}
}
That's it.
Now you can access selected_json on any page you want by simply getting it from Vuex with this line of code.
this.$store.getters["getSelectedJson"]

Literal objects and properties reactivity in Vue 2.5

Why nested fields of literal objects bound to a component’s property do not get reactive and observed?
Example:
<my-comp :my-prop="{ my: { literal: { object: { with: { nested: { field: ‘not reactive’ }}}}}}"></my-prop>
When you do this inside my-comp:
created() {
console.log(this); // you can see in the Chrome console that nested fields of this.myProp do not get reactive (i.e. no getters)
}
I know that I can play around it, but I am just asking why not?
Is there a neat way to make it reactive?
Thank you!
This is because the way "HTML" templates are rendered.
When Vue "compiles" your html, it basicly creates the following "computed" function
render(createElement) { /* `createElement` is also known as `h` in the Vue world */
return createElement('my-comp', {
props: {
myProp: { my: { literal: { object: { with: { nested: { field: ‘not reactive’ }}}}}},
},
);
},
Since the full flow of the value of myProp never passes through the data function, nor through the return result of a normal computed function, it never gets marked for reactivity.
While it might sound weird that Vue marks the return result of all "computed" functions, expect the special render function reactive, it actually makes sense if looking at the design idea of Vue, namely props in, and events out. This not marking of reactivity gives us a performance boost here, compared to marking it reactive.
If you still require it to be reactive (and thus want to go against the core principles of Vue (and thus making your application harder to understand) ), you can do it, by storing the value inside the data section of your component, or by returning it from a computed property.

How to access Vuex modules mutations

I read thorugh his documentation from vue but I didn't find anything about how to actually access specific module in the store when you have multiple modules.
Here is my store:
export default new Vuex.Store({
modules: {
listingModule: listingModule,
openListingsOnDashModule: listingsOnDashModule,
closedListingsOnDashModule: listingsOnDashModule
}
})
Each module has its own state, mutations and getters.
state can be successfully accessed via
this.$store.state.listingModule // <-- access listingModule
The same is not true for accessing mutations cause when I do this
this.$store.listingModule.commit('REPLACE_LISTINGS', res)
or
this.$store.mutations.listingModule.commit('REPLACE_LISTINGS', res)
I get either this.$store.listingModule or this.$store.mutations undefined error.
Do you know how should the module getters and mutations be accessed?
EDIT
As Jacob brought out, the mutations can be accessed by its unique identifier. So be it and I renamed the mutation and now have access.
here is my mutation:
mutations: {
REPLACE_OPEN_DASH_LISTINGS(state, payload){
state.listings = payload
},
}
Here is my state
state: {
listings:[{
id: 0,
location: {},
...
}]
}
As I do a commit with a payload of an array the state only saves ONE element.
Giving in payload array of 4 it returns me back array of 1.
What am I missing?
Thanks!
It's a good idea, IMHO, to call vuex actions instead of invoking mutations. An action can be easily accessed without worrying about which module you are using, and is helpful especially when you have any asynchronous action taking place.
https://vuex.vuejs.org/en/actions.html
That said, as Jacob pointed out already, mutation names are unique, which is why many vuex templates/examples have a separate file called mutation-types.js that helps organize all mutations.
re. the edit, It's not very clear what the issue is, and I would encourage you to split it into a separate question, and include more of the code, or update the question title.
While I can't tell why it's not working, I would suggest you try using this, as it can resolve two common issues
import Vue from 'vue'
//...
mutations: {
REPLACE_OPEN_DASH_LISTINGS(state, payload){
Vue.$set(state, 'listings', [...payload]);
},
}
reactivity not triggered. Using Vue.$set() forces reactivity to kick in for some of the variables that wouldn't trigger otherwise. This is important for nested data (like object of an object), because vue does not create a setter/getter for every data point inside an object or array, just the top level.
rest destructuring. Arrays: [...myArray] Objects: {...myObj}. This prevents data from being changed by another process, by assigning the contents of the array/object as a new array/object. Note though that this is only one level deep, so deeply nested data will still see that issue.

How can I watch synchronously a state change in vuex?

I am using an opensource vuejs + vuex project and this is the source https://github.com/misterGF/CoPilot/tree/master/src/components
I am currently having problems knowing how to trigger an event from one components to another.
I can use this.$state.store.commit("foo", "bar") to store information in vuex, but when two seperate have two difference export default {} I don't know how I can make the app aware whenever "foo" is for exampled changed to "baz" ... unless I refresh/reload the app, there is no way for me to know the changes
Use this.$store.watch on your component. Created() is a good place to put it. You pass it the state to watch and then specify the callback for when it changes. The Vuex docs do not give good examples. Find more information on this Vuejs Forum page. Store watch functions the same as the vm.$watch, so you can read more about that here in the Vue docs.
this.$store.watch(
(state)=>{
return this.$store.state.VALUE_TO_WATCH // could also put a Getter here
},
(newValue, oldValue)=>{
//something changed do something
console.log(oldValue)
console.log(newValue)
},
//Optional Deep if you need it
{
deep:true
}
)
Your question is not entirely clear so I am going to make some assumptions here.
If you simply want your app to know when a store value has changed you can use a watcher to watch a computed property that is directly linked to a store getter:
So you would set a watcher on something like this:
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
},
watch:{
doneTodosCount(value) {
console.log(`My store value for 'doneTodosCount' changed to ${value}`);
}
}
If you want your commit to behave differently depending on what the current value of your foo property is set to, then you can simply check for this in your commit function.
Let me know if you have some more questions.