VueJS: How to get nested object value from vuex store? - vue.js

I am trying to access nested property of store state data but it is undefined when I try to access. I have following data
const state = {
entity: {
initial: {valid: false},
general: {valid: false},
buildFiles: {valid: false},
license: {valid: false},
author: {valid: false},
}
};
const getters = {
ENTITY: (state: any): Submission => {
return state.entity; // Works fine
},
INITIAL: (state) => {
console.log(state.entity); // Prints observable with entity and properties
console.log(state.entity.initial); // Prints undefined
return state.entity.initial;
},
};
All I am trying to do is use getters from the component. Is there any way I can access property intitial?

I think you need return state as a function. And your getters needed some a little changes: Try some thing like this:
const state = () => ({
entity: {
initial: { valid: false },
general: { valid: false },
buildFiles: { valid: false },
license: { valid: false },
author: { valid: false },
},
})
const getters = {
ENTITY: (state) => (submission) => {
return state.entity // Works fine
},
INITIAL: (state) => {
console.log(state.entity) // Prints observable with entity and properties
console.log(state.entity.initial) // Prints undefined
return state.entity.initial
},
}

Related

Update object property state inside of watch vue

I'm trying to update the selected boolean to true from my lists items array of objects when the from_id property from the items array inside of the entry object matches with the item_id of the object in lists fromData array. The selected property updates but I got this error:
Error in callback for watcher "function () { return this._data.$$state }": "Error: [vuex] do not mutate vuex store state outside mutation handlers.
store.js
entry: {
items: [
{
from_id: 5,
to_id: 1,
quantity: 100,
},
{
from_id: 11,
to_id: 3,
quantity: 119,
},
{
from_id: 7,
to_id: 3,
quantity: 59,
},
]
}
lists: {
from: [
{
item: {...},
item_id: 5,
selected: false
},
{
item: {...},
item_id: 6,
selected: false,
},
{
item: {...},
item_id: 7,
selected: false,
}
]
}
const getters = {
entry: (state) => state.entry,
lists: (state) => state.lists,
};
This what I've tried
computed: {
...mapGetters('Conversion', ['entry', 'lists']),
from: function () {
return this.lists.from.filter((item) => !item.selected);
},
},
watch: {
from: {
deep: true,
handler: function (items) {
const _entries = this.entry.items;
_entries.map((entry) => {
items
.filter((item) => item.item_id === entry.item_from_id)
.map((item) => (item.selected = !item.selected));
});
},
},
},
The error means what it says. You're mutating the state outside the store. This should happen in mutation/action.
You need to have an action/mutation that modifies the state this way:
mutations: {
toggle(state, id) {
let item = state.items.find(item => item.id === id)
item.selected = !item.selected;
}
}
And dispatch/commit it outside the store:
this.$store.dispatch('toggle', someId)
Vuex is one-way data flow store. You will not able to mutate data directly with the getters, use action/mutation instead. https://vuex.vuejs.org/#what-is-a-state-management-pattern
In your store.js
const store = createStore({
...
mutations: {
patchEntry (state, payload) {
// Your logic here => return _entries
state.entry = Object.assign({}, state.entry, _entries)
}
}
})
In your watcher:
watch: {
from: {
deep: true,
handler: function (items) {
store.commit('patchEntry', items)
});
},
},
},
Ref: https://vuex.vuejs.org/guide/mutations.html#committing-mutations-in-components

Data not being passed from Child Data to Parent Props

I have a Request Form Component, and within this request form Component I have a Dropdown Menu Component, which I will link both below. All values in my table are pushed into an object upon hitting the Submit Button. However my dropdown selection is only being picked up by my console.log and not being pushed into the Object.
I'm not so familiar with Vue, so I'm not sure what direction to go in for fixing this. I'll attach the relevant (?) pieces of code below.
Parent Component:
<SelectComponent :selected="this.selected" #change="updateSelectedValue" />
export default {
fullScreen: true,
name: 'CcRequestForm',
mixins: [BaseForm],
name: "App",
components: {
SelectComponent,
},
data() {
return {
selected: "A",
};
},
props: {
modelName: {
default: 'CcRequest',
},
parentId: {
type: Number,
default: null,
},
},
mounted() {
this.formFields.requester.value = this.currentRequesterSlug;
},
destroyed() {
if (!this.modelId) return;
let request = this.currentCcRequest;
request.params = request.params.filter(p => p.id)
},
computed: {
...mapGetters(['ccTypesForRequests', 'currentRequesterSlug', 'currentCcRequest']),
ccTypesCollection() {
return this.ccTypesForRequests.map((x)=>[x.slug, this.t(`cc_types.${x.slug}`)]);
}
},
methods: {
addParam() {
this.addFormFields(['params'], {
slug: '',
name: '',
isRequired: true,
description: '',
typeSlug: '',
selected: ''
});
},
deleteParam(idx){
this.removeFormFields(['params', idx]);
},
restoreParam(idx){
this.restoreFormFields(['params', idx])
},
$newObject() {
return {
slug: '',
name: '',
isAbstract: false,
requester: '',
description: '',
status: 'inactive',
params: [],
selected: ''
};
},
$extraPrams() {
return {
parentId: this.parentId,
};
},
updateSelectedValue: function (newValue) {
this.selected = newValue;
},
},
watch: {
selected: function (val) {
console.log("value changed", val);
},
},
};
Child Component:
<script>
export default {
name: "SelectComponent",
props: {
selected: String,
},
computed: {
mutableItem: {
get: function () {
return this.selected;
},
set: function (newValue) {
this.$emit("change", newValue);
},
},
},
};
You have to define the emit property in the parent component, or else it won't know what to expect. That would look like:
<SelectComponent :selected="this.selected" #update-selected-value="updateSelectedValue" />
Check out this tutorial for more information: https://www.telerik.com/blogs/how-to-emit-data-in-vue-beyond-the-vuejs-documentation
To update selected property inside the object, in this constellation, you need to update object property manually upon receiving an event, inside of updateSelectedValue method. Other way could be creating a computed property, since it's reactive, wrapping "selected" property.
computed: {
selectedValue () {
return this.selected
}
}
And inside of object, use selectedValue instead of selected:
return {
...
selected: selectedValue
}

Apexchats.js axios gives me undefined with Vue

I am trying to get data from server using vue and apexcharts, but even after I called data with axios, it gives me undefined..
What have I missed?
template
<apexchart
ref="chart1"
width="100%"
:options="chartOptions" :series="series">
</apexchart>
data from url
{
"pageviews": 1313,
"new_users": 1014
}
script
export default {
data: function () {
return {
series: [],
chartOptions: {
chart: {
type: 'donut',
},
colors: ['#01cd49', '#007568'],
labels: ['new', 're'],
}
},
created: function () {
this.getByVisitor()
},
methods: {
getByVisitor() {
const url = 'url';
axios
.get(url)
.then(response => {
this.$refs.chart1.updateSeries([{
name: 'Sales',
data: response.data
}])
})
.catch(error => (this.byVisitor = error.data));
console.log(`---------------this.$refs.chart1`, this.$refs.chart1);
},
}
See Updating Vue Chart Data
There's no need to directly call the updateSeries() method on the chart component since it is able to react to changes in series. All you have to do is update your series data property
export default {
data: () => ({
series: [], // 👈 start with an empty array here
byVisitor: null, // 👈 you seem to have missed this one for your error data
chartOptions: {
chart: {
type: 'donut',
},
colors: ['#01cd49', '#007568'],
labels: ['new', 're'],
}
}),
created: function() {
this.getByVisitor()
},
methods: {
async getByVisitor() {
const url = 'url';
try {
const { data } = await axios.get(url)
// now update "series"
this.series = [{
name: "Sales",
data
}]
} catch (error) {
this.byVisitor = error.data
}
},
}
}

Vuex stores state is undefined in console, but Vue tool is showing that are working properly

I want to display my getters in web but in console.log i have undefined errors. but in vue dev tool my getters are working.
my component code :
export default {
data: () => ({
items: [
{ text: 'Real-Time', icon: 'mdi-clock' },
{ text: 'Audience', icon: 'mdi-account' },
{ text: 'Conversions', icon: 'mdi-flag' },
],
}),
computed: {
Question(){
return this.$store.getters.getQuest
}
},
methods: {
Next(){
this.$store.commit('incrementIndex')
}
},
}
and this my vuex :
export default new Vuex.Store({
state: {
question: [],
index: 0
},
getters: {
getQuest: state => {
let a = state.question[state.index]
let answer = [...a.incorrect_answers, a.correct_answer]
return {
question: a,
answer: answer
}
}
},
In the getter you need to check whether all information is loaded yet. So this is roughly what I'd do:
export default new Vuex.Store({
state: {
question: [],
index: 0
},
getters: {
getQuest: state => {
let a = state.question[state.index]
if (a){
let answer = [...a.incorrect_answers, a.correct_answer]
return {
question: a,
answer: answer
}
} else {
return null
}
},
You are getting undefined errors because initially question is an empty array.
So state.question[state.index] is basically undefined
And then you are trying to access property incorrect_answers of undefined which will not work and will give undefined errors

Computed property depends on vuex store. How to update the cached value?

The value of this.$store.state.Auth.loginToken is modified by one of its child components. The initial value of this.$store.state.Auth.loginToken is undefined. But still, the update in its value has no effect in the cached value of navItems thus it always returns the second value.
computed: {
navItems () {
return this.$store.state.Auth.loginToken != undefined ?
this.items.concat([
{ icon: 'add', title: 'Add new journal entry', to: '/' },
{ icon: 'power_settings_new', title: 'Logout', to: '/logout'}
]) :
this.items.concat([
{ icon: 'play_arrow', title: 'Login', to: '/login' }
])
}
}
Is there a better way to keep a watch on this.$store.state.Auth.loginToken so that it can be used same as navItems?
I created a basic example of how you can use vuex getters and Auth token (codepen):
const mapGetters = Vuex.mapGetters;
var store = new Vuex.Store({
state: {
Auth: {
loginToken: ''
},
menuItems: [
{ icon: 'home', title: 'Home', to: '/' },
{ icon: 'about', title: 'About', to: '/about' },
{ icon: 'contact', title: 'Contact', to: '/contact' }
]
},
mutations: {
SET_LOGIN_TOKEN(state, data) {
state.Auth.loginToken = 1
}
},
getters: {
menuItems(state, getters) {
if(state.Auth.loginToken !== '') {
return state.menuItems.concat([{
icon: 'profile', title: 'Profile', to: '/profile'
}])
}
return state.menuItems
},
loggedIn(state) {
return state.Auth.loginToken !== ''
}
},
actions: {
doLogin({commit}) {
commit('SET_LOGIN_TOKEN', 1)
}
}
});
new Vue({
el: '#app',
store,
data: function() {
return {
newTodoText: "",
doneFilter: false
}
},
methods: {
login() {
this.$store.dispatch('doLogin')
}
},
computed: {
...mapGetters(['menuItems', 'loggedIn'])
}
})
This is just an example so you can ignore the actual login action. Also, the store should be a directory, the getters, mutations and actions should have their own files which are then imported in the index.js in the store like in this example