Vue mapState non reactive - javascript

I try to get the state from the store using the mapState function, But I can't use the generated code that returns the values into my template code ...
<template>
// Some code
<template v-if="!isLoggedIn">
// Some code
</template>
<template v-else>
// Some code
{{ currentUser.name }}
</template>
// Some code
</template>
<script>
import { mapState } from "vuex";
export default {
// Some code
computed: {
...mapState({auth : ['currentUser', 'isLoggedIn','customers']})
}
}
</script>
instead the following code work
<script>
import { mapState } from "vuex";
export default {
// Some code
computed: {
currentUser() {
return this.$store.state.auth.currentUser
},
isLoggedIn() {
return this.$store.state.auth.isLoggedIn
},
}
}
</script>
Warning message
[Vue warn]: Property or method "isLoggedIn" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
Thanks in advance

The right syntax to access non-root properties is the following (using arrow functions) :
computed: {
...mapState({
currentUser: state => state.auth.currentUser,
isLoggedIn: state => state.auth.isLoggedIn,
customers: state => state.auth.customers
})}
Check the documentation.

If you're trying to access values from a namespaced vuex module called auth, pass the name of the module as the first argument and the array of values to map as the second argument:
computed: {
...mapState('auth', ['currentUser', 'isLoggedIn','customers'])
}

You can mapState the module and then use, say this.auth.isLoggedin

Related

Getters: Property was accessed during render but is not defined on instance

I have a component in which I want to show a data defined in the state. I have created the getter:
export default createStore({
state: {
foo: true,
},
getters: {
getFoo: state => state.foo
}
}
And in the component I call it from computed:
computed: {
...mapGetters(['getFoo']),
}
I use that variable in an if in the template:
<template>
<template v-if="foo">
<span>Bar</span>
</template>
</template>
And in the console I get the following warning: [Vue warn]: Property "foo" was accessed during render but is not defined on instance.
I have tried to do it without the getters but I get the same warning:
computed: {
getFoo() {
return this.$store.getters.getFoo;
}
}
Since you're mapping the getters in your computed option, you should that getter name getFoo :
<template>
<template v-if="getFoo">
<span>Bar</span>
</template>
</template>

Toggle true/false using vuex modules?

i am using vuex modules and i have a module named customer.js. In that i have a state showName which is by default set to false. I set a mutation to toggle that state. and in my about.vue file i have a button that calls that mutation but everytime i try to run that mutation, it gives me an error message Property or method "showCustomerName" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
In my customer.js module
enter code here: state: {
showName:false,
},
mutations: {
showCustomerName : state => {
state.showName = !state.showName
console.log('works??!!!')
}
},
actions: {
}
})
In my about.vue file
<template>
<div>
<button #click="showCustomerName">Click Me</button>
</div>
</template>
<script>
import {mapMutations} from "vuex"
export default {
methods : {
...mapMutations ([
"customer/showCustomerName"
])
}
}
</script>
I would assume this would work, but not sure what i am doing wrong. Thank you.
Okay so figured it out, instead of ...mapMutations ([
"customer/showCustomerName"
])
change to
...mapMutations ('customer',{
showCustomerName: "showCustomerName"
})

Using nested properties from object in Vuex store inside a template

Assuming that I have a getter for my Vuex.Store that returns an object with many properties and I want to use this object in a component. Is there any way to do something similar to this:
<div v-bind="$store.getters['x/y']">
<p>{{scopedObject.prop1}}</p>
</div>
instead of:
<div>
<p>{{$store.state.x.data.y.prop1}}</p>
</div>
I'm asking if I can scope objects to blocks.
The simple answer is no.
You could define a computed property which returns the nested object.
computed: {
scopedObject() {
return this.$store.getters['x/y'];
}
}
In fact, this is where mapGetters is meant to be used.
import { mapGetters } from 'vuex';
export default {
computed: mapGetters({ scopedObject: 'x/y' }),
}
Then, the template would simply be
<div>
<p>{{scopedObject.prop1}}</p>
</div>
If you really want to make a new scope, define a new component which will only define the section of the template you want it to use.
A simple prop could be used, so it doesn't need to know about the store.
<template>
<div>
<p>{{scopedObject.prop1}}</p>
</div>
</template>
<script>
export default {
props: {
scopedObject: {
type: Object,
required: true
},
},
};
</script>
Then, in the parent:
<template>
<scoped-component :scoped-object="$store.getters['x/y']"><scoped-component>
</template>

Using root functions inside of prop object

I'm using Vuejs2.
I have a prop that contains some validation. I want to use a Lang object that is mixed into all my components. One of my props has a default value that needs to access this lang object. I can access the lang object in the create() function but not in the props.foo.default() option.
app.js
import Vue from 'vue';
import messages from '../lang/messages';
import lang from 'lang.js';
const Lang = new lang({
'messages': messages,
'locale': 'en'
});
Vue.mixin({
data: function () {
return {
lang: Lang
}
}
});
const app = new Vue({
el: '#app'
});
bar.component.vue
<template>
<div>{{ foo }}</div>
</template>
<script>
export default {
props: {
foo: {
type: String,
default: this.lang.get('some.lang') // Cannot read property 'get' of undefined
}
},
created () {
console.log(this.lang.get('some.lang'); // returns correct value
}
}
</script>
So to clarify I can access this.lang inside my created() function but I cannot get to it in my props object.
From what I could derive the context of this is different depending on the object / function you are in. I've tried to edit the props in the created hook but could not find a way to get access. I managed to get it working by creating a filter but it's inconsistent with some other code that I have in my component.
What would be a good way for a prop to have a default value that can access the root vm or be set outside of the props object?
You can't set a prop's default value based on a data property in a Vue instance. You can't access the Vue instance in the context of the props object, and you can't set the default value after the component has been instantiated.
Either import the lang object directly into the bar.component.vue file:
<script>
import lang from 'lang.js'
export default {
props: {
foo: {
type: String,
default: lang.get('some.lang')
}
}
}
</script>
Or, create a computed property fooVal which will return this.lang.get('some.lang') if the foo prop is not defined, and then use that in your template instead:
<template>
<div>{{ fooVal }}</div>
</template>
<script>
export default {
props: {
foo: { type: String }
},
computed: {
fooVal() {
return (this.foo === undefined) ? this.lang.get('some.lang') : this.foo;
}
}
}
</script>

How to design a store in Vuex to handle clicks in nested, custom components?

I'm trying to design a store to manage the events of my Vuex application. This far, I have the following.
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const state = { dataRows: [], activeDataRow: {} };
const mutations = {
UPDATE_DATA(state, data) { state.dataRows = data; state.activeDataRow = {}; },
};
export default new Vuex.Store({ state, mutations });
I'm going to have a number of list items that are supposed to change the value of the data in the store when clicked. The design of the root component App and the menu bar Navigation is as follows (there will be a bunch of actions in the end so I've collected them in the file actions.js).
<template>
<div id="app">
<navigation></navigation>
</div>
</template>
<script>
import navigation from "./navigation.vue"
export default { components: { navigation } }
</script>
<template>
<div id="nav-bar">
<ul>
<li onclick="console.log('Clickaroo... ');">Plain JS</li>
<li #click="updateData">Action Vuex</li>
</ul>
</div>
</template>
<script>
import { updateData } from "../vuex_app/actions";
export default {
vuex: {
getters: { activeDataRow: state => state.activeDataRow },
actions: { updateData }
}
}
</script>
Clicking on the first list item shows the output in the console. However, when clicking on the second one, there's nothing happening, so I'm pretty sure that the event isn't dispatched at all. I also see following error when the page's being rendered:
Property or method "updateData" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
I'm very new to Vuex so I'm only speculating. Do I need to put in reference to the updateData action in the store, alongside with state and mutations? How do I do that? What/where's the "data option" that the error message talks about? Isn't it my components state and it's properties?
Why the error
You are getting the error, because when you have <li #click="updateData"> in the template, it looks for a method updateData in the vue component which it does not find, so it throws the error. To resolve this, you need to add corresponding methods in the vue component like following:
<script>
import { updateData } from "../vuex_app/actions";
export default {
vuex: {
getters: { activeDataRow: state => state.activeDataRow },
actions: { updateData }
},
methods:{
updateData: () => this.$store.dispatch("updateData")
}
}
</script>
What this.$store.dispatch("updateData") is doing is calling your vuex actions as documented here.
What/where's the "data option"
You don't have any data properties defined, data properties for a vue component can be used, if you want to use that only in that component. If you have data which needs to be accessed across multiple components, you can use vuex state as I believe you are doing.
Following is the way to have data properties for a vue component:
<script>
import { updateData } from "../vuex_app/actions";
export default {
date: {
return {
data1 : 'data 1',
data2 : {
nesteddata: 'data 2'
}
}
}
vuex: {
getters: { activeDataRow: state => state.activeDataRow },
actions: { updateData }
},
methods:{
updateData: () => this.$store.dispatch("updateData")
}
}
</script>
You can use these data properties in the views, have computed properies based on it, or create watchers on it and many more.

Categories

Resources