In vuex, I have this:
getByLessonId: state => _lessonId => {
return state.entities.filter(e => e.lesson.id === _lessonId);
},
In component:
// using as a method...
...mapGetters("assignment", { getAssignmentsByLessonId: "getByLessonId" }),
Later in the code, since the mapping returns the function, I need to call it like this?
// load this lessons assignments...
this.assignments = this.getAssignmentsByLessonId()(this.id);
// this is what i started with
// this.$store.getters["assignment/getByLessonId"](this.id)
It works, just not sure if there is a better way to do this? Or should I put mapGetters in the computed properties of the component?
Better put mapGetters (as long as mapState) in a computed section of a component. This is a recommended way to use several getters or props of a state because you have one place only to control getters and props in a component.
Related
This is a screenshot from another question about mapGetters
Link to the question I took the screen from
In Vue.js I saw an answer to a post.
It said :
In your Component
computed: {
...mapGetters({
nameFromStore: 'name'
}),
name: {
get(){
return this.nameFromStore
},
set(newName){
return newName
}
}
}
And I wonder why newName is a "new" name ? And also what it looks like to call the getter in the template html section. Do we use the nameFromStore like a data in the template ?
I tried to see how getters are used but what I found didn't look like this structure. The things I found looked like someone using a value in data that returns this.$store.getters.nameFromStore but I don't want to use the data and do it all in the computed instead like the picture I attached if someone can please help me ?
If you simply want to use mapGetters to access data store in vuex state you can do it as you have it above, without the get/set computed. I'm not sure what the intention of that get/set computed property was but it doesn't look like it would work as expected.
Try this:
// vuex index.js
// ...
getters: {
getAnimals: state => {
return state.items.filter(i => i.type === 'animal');
}
}
// your component.vue
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
...mapGetters(['getAnimals']) // access using this.getAnimals
// or
...mapGetters({ animals: 'getAnimals' }) // access using this.animals
}
}
The bennefit to using it this way is being a computed property, when the items change in vuex state, they will be recomputed, and your component will react to this change. Hopefully this is a clear explanation.
The vue docs:
https://vuex.vuejs.org/guide/getters.html#method-style-access
I am trying to build a watchlist (data streaming program) by vue3 with vuex. When a watchlist component subscribes for a symbol it should receive updates for that symbol from the store. When removing the subscription from the component that particular component should not receive that state change after that. We cannot hardcode symbol names in store to mapstate from component for each individually since there can be hundreds. if we take all the symbols as an attribute of a single object and map the state to it it will be a performance overhead since not all watchLists are referring to all the symbols.
So my question is there any way to inject a dynamically changing array to mapstate?
In component->
computed: {
...mapState([this should change dynamically]),
},
in Store->
state : {
these states also should be dynamic
},
or is there any workaround in vue to achive this?
I have found an alternative way by using getters. When we are using that we don't have to map state at all.
I am returning a function from getters with an argument since Vuex getters don't accept arguments.
symbol: (state) => (symbol) => {
return state.payload[symbol]
}
and from the component, I am watching for that getter.
this.$store.watch(
(state, getters) => getters.allSymbols(currency),
(newValue, oldValue) => {
this.symbolObjects[symbol] = newValue;
// do something
},
);
the watch is triggering every time the currency update. Also using the Vuex watcher gives the benefit of accessing both old and new values.
I've seen many times other devs using computed property names to define actions & mutations names, eg.
export const SOME_ACTION = 'someAction'
And then:
[SOME_ACTION]({ commit }) { ... }
Which in turn lets you dispatch actions using this neat way:
this.$store.dispatch(SOME_ACTION)
Kinda feels like NgRX, which I like. But what about Vuex namespaced modules? Is there any way to dispatch actions neatly, providing only the SOME_ACTION var?
To anyone wondering how did I figure it out:
I've used createNamespacedHelpers from vuex to prepare the helpers first:
const { mapGetters, mapActions } = createNamespacedHelpers('some/nested/module')
Afterwards you can map 'em like that:
...mapActions([SOME_ACTION, SOME_OTHER_ACTION])
And call, for example, like this:
this[SOME_ACTION]()
How does this work for you?
...mapActions({
nsOne: 'namespaced/nsOne',
nsTwo: 'namespace/nsTwo',
nonNsOne: 'nonNsOne',
nonNsTwo: 'nonNsTwo'
})
and then call with
this.nsOne()
In my component I'm trying to access the getters from my auth module.
This is the getter function in store/auth.js
getters: {
isAuthenticated: (state) => !!state.token,
},
And here is how I trying to access and display this data
// html in template of component
<span
v-if='this.isAuthenticated'
class='font-weight-bold grey--text text--darken-3'>
Hello
</span>
-------------------
// computed properties of component
computed: {
...mapGetters([
'auth/isAuthenticated',
]),
},
and in the devtools this is the error i am getting
' Property or method "isAuthenticated" is not defined on the instance but referenced during render.'
I'm really not sure why and not sure how to access this data, in the Vue debugger i can see that the getter works and is returning true.
I have tried other ways to access the data such as
isAuthenticated: () => this.$store.getters.isAuthenticated // and this.$store.getters.auth.isAuthenticated
but this gives a different error saying typeError: Cannot read property '$store' of undefined when i try to access the computed function in the template.
This is really annoying because to my knowledge i am correctly trying to access the store and its not working.
An explanation would be greatly appreciated. Thanks.
You have two different errors in your two different approaches. Let's see your first approach first:
---
<span
v-if='this.isAuthenticated'
---
computed: {
...mapGetters([
'auth/isAuthenticated',
]),
}
---
Here, your problem is that you're trying to map a namespaced getter, but trying to access the attribute without the namespace. You can work around this by using an object parameter for the mapGetters function:
computed: {
...mapGetters({
isAuthenticated: 'auth/isAuthenticated',
}),
}
In your second approach, you almost get it right, but you run into a different set of issues:
isAuthenticated: () => this.$store.getters.isAuthenticated
First of all, if the module is namespaced, the correct way to access the getter would be this.$store.getters['auth/isAuthenticated'].
Apart from that, you should not use arrow functions in Vue components, since the this context is lost (it points to the function rather than the Vue instance). You need to use a regular function.
Combining both of these fixes, the result would be:
isAuthenticated(){
return this.$store.getters['auth/isAuthenticated']
}
Still a little bit young in VueJS but I'm loving every bit of it. But now, fixated somewhere.
I want to initialize some values in data() using values passed via props. This is so that I can be able to mutate them later on, since it is not recommended to mutate props inside a component. In fact the official docs recommend this property initialization using prop values as shown below:
{
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
I have something like the one below:
<template>
<div class="well">
<!-- Use Prop value directly on the template: works (but of no help in initializing data) -->
Department: {{department.name}}
<!-- Use prop value but gotten via computed property: Works inside the template but not in the initialization -->
Department: {{fetchDepartment.name}}
<!-- Use the array I initialized with the prop value: Does not work -->
Department: {{this_department.name}}
</div>
</template>
<script>
export default {
name: 'test',
props: ['department'],
data() {
return {
this_department: this.department
// below does not work either
//this_department: this.fetchDepartment
}
},
created() {
// shows empty array
console.log(this.department)
},
mounted() {
// shows empty array
console.log(this.department)
},
computed: {
fetchDepartment() {
return this.department
}
}
}
</script>
As seen in the commented sections above, the initialization is not successful. Neither does the value of this.department appear either from the created() or the mounted() hooks. And note, I can see it is defined using the Chrome Vue Devtools. So my question is, how exactly should I initialize data() attributes using props values, or which is the best way of going around this issue?
I know my answer comes in late but it helps me and hopefully someone else coming here. When props' data are async:
// in the parent component
<template>
<child :foo="bar" v-if="bar" />
</template>
That way, you render the component when props are already available making it safer to follow the guide's recommended ways to initialize data value with props as so:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
Happy coding!
You CAN modify a prop. Use the '.sync' modifier. I use it frequently because it is convenient and intuitive. This requires emitting an event to update the value on the parent. I am not really sure the warning of how it results in maintenance issues.
Another method I use if I want to modify a value and not update the parent is using Lodash clone. For example (assuming its available on mounted)
mounted(){
this_department = _.clone(this.department)
}
If you consistently want to mutate the prop and have it change with the parent, then use a computed property. However, in most cases you will want to depend on the state of that data within the component and change it using other functions and thus a computed property will not be what you need.
A computed property is the simplest way to provide a mutable version of a prop, but you might not want to lose data when the prop is updated. You could use an explicit watch.
Watchers
While computed properties are more appropriate in most cases, there
are times when a custom watcher is necessary. That’s why Vue provides
a more generic way to react to data changes through the watch option.
This is most useful when you want to perform asynchronous or expensive
operations in response to changing data.
This is most useful when you want to perform asynchronous or expensive
operations in response to changing data.