I am trying to access into an array from computed. console vuex showed computed array is there, but I cannot access into the array from created(). Any help is appreciated!
I have tried to save into a data array, does not find the computed array.
<template>
<div>
{{ product.product_name }}
</div>
</template>
<script>
export default {
name: 'product_4',
created() {
this.product = this.products.find((product) => product.id == 4);
},
data() {
return {
product: {}
}
},
computed: {
products() {
return this.$store.getters.products;
}
}
}
</script>
store.js
state: {
products:[]
},
getters: {
products(state) {
return state.products;
}
},
mutations: {
updateGetProducts(state, payload) {
state.products = payload;
}
},
actions: {
getProducts(context) {
axios.get('/api/getproducts')
.then((response)=> {
context.commit('updateGetProducts', response.data);
});
}
}
computed properties are mounted after the created life cycle hook, so try to use another hook like mounted :
mounted(){
this.product = this.products.find((product) => product.id == 4);
}
I think should not make any problem, if you use like below, but you have to ensure that find method returning a single object instead of null or array of objects.
created() {
this.product = this.$store.getters.products.find((product) => product.id == 4);
}
but I ll prefer to use that into mounted hook like below when you want to use computed property.
mounted() {
this.product = this.products.find((product) => product.id == 4);
}
And after looking your updated code you can use below solution, because I have seen that you have used actions to update products using api, so below solution will solve your problem
<template>
<div>
{{ product.product_name }}
</div>
</template>
<script>
export default {
name: 'product_4',
data() {
return {
}
},
computed: {
product() {
return this.$store.getters.products.find((p) => p.id == 4);
}
}
}
</script>
I changed the computed to this:
computed: {
product() {
return this.$store.getters.products.find((product) => product.id == 4);
}
}
I can access directly this way, do not need to put into data as it is not advised to do so in vue documentation.
Related
I am getting an array from Vuex and I want an object at 2 positions.
HTML
<p class="">
{{ MainImg[2].para}}
</p>
Vue
export default {
name: "App",
components: { },
data() {
return {
imageQuery: this.$route.params.image,
};
},
computed: {
...mapGetters("design", {
MainImg: ["singleDesigns"]
})
},
created() {
this.fetchDesigns();
},
mounted() {
console.log(this.MainImg);
},
methods: {
fetchDesigns() {
this.$store.dispatch("design/getSingleDesign", this.imageQuery);
}
}
};
But it shows an undefined error.
And When I add MainImg array in Vue data like this.
data() {
return {
imageQuery: this.$route.params.image,
MainImg:[{para:"1"},{para:"2"},{para:"3"},{para:"4"}]
};
It Works.
P.S.-
Store Code-
export const state = () => ({
designs: [],
})
export const getters = {
singleDesigns(state) {
return state.designs;
}
}
I am not adding Action and Mutation because it works fine with other code.
It looks like the array is empty at the first rendering, so you should add a condition to render it :
<p class="" v-if="MainImg && MainImg.length >= 2">
{{ MainImg[2].para}}
</p>
cannot edit list
I think the problem is in the update mutation.
Everything is fine with the label, probably a problem with input, it does not update the data.
I need to make the tasks can be edited on a double wedge.
cannot edit list
I think the problem is in the update mutation.
Everything is fine with the label, probably a problem with input, it does not update the data.
I need to make the tasks can be edited on a double wedge.
Vue.use(Vuex)
export default new Vuex.Store({
state: {
todos: localData().get()
},
mutations: {
editTodo: (state, id) => {
let todo = state.todos.find(todo =>
(todo.id === id))
todo.edit = true
localData().set(state.todos)
}, //mutations editTodo
update: (state, id, newEvent) => {
let todo = state.todos.find(todo =>
(todo.id === id))
todo.title = newEvent
todo.edit = false
localData().set(state.todos)
},
},
})
<template>
<li>
<label
v-if="!edit"
#dblclick="editTodo"
>
{{ title }}
</label>
<input
v-else
class="edit"
type="text"
:value="newEvent" //it seems he is interrupting the title
#keyup.enter="update"
>
</li>
</template>
<script>
export default {
name: 'todo',
props: ['id', 'title', 'edit', 'completed'],
data() {
return {
newEvent: '' //Am I doing the right thing to add newEvent?
}
},
computed: {
todos() {
return this.$store.state.todos
}
},
methods: {
editTodo() {
this.$store.commit('editTodo', this.id)
},
update() {
this.$store.commit('update', this.id, this.newEvent) //update method
},
}
}
First, let's define what is wrong in your code. You're updating Vuex state object using update function but you're giving :value="newEvent" which is in your component, so Vuex doesn't see this. First, create state data and getters for newEvent
state:{
//..
newEvent: ""
}
getters:{
newEvent: state => state.newEvent
}
Then use this state element in your component
// ..
import { mapGetters } from "vuex"
// ..
computed:{
...mapGetters(["newEvent"])
}
You should use logic like that
After reading many examples and the documentation from Vue, Vuex and Vue-Router I did this project: https://jsfiddle.net/junihh/30wda1em/5/
Even all goes fine when I try to load a row from Post section, the value come empty from the Vuex store. Here the component:
const PostContent = Vue.component('postcontent', {
data() {
return {
post: this.$store.state.currentPost
// post: {
// title: 'The title',
// content: 'The content for test.'
// }
}
},
template: getTemplate('#tpl-postcontent')
});
Here the component that update the state.currentPost value and call the "postcontent" component.
const Posts = Vue.component('posts', {
data() {
return {
url_path: '/posts/content',
rows: this.$store.state.dataAll
}
},
methods: {
openPost: function(e)
{
let rowID = e.currentTarget.getAttribute('data-rowid');
let dataAll = this.$store.state.dataAll;
let currentPost = dataAll.filter(row => (row.id == rowID))[0];
this.$store.state.currentPost = currentPost;
}
},
template: getTemplate('#tpl-posts')
});
Any help here? I'm stuck on that issue.
You need to use a computed property to gather the information from your store with reactivity:
const PostContent = Vue.component('postcontent', {
computed: {
post() {
return this.$store.state.currentPost
}
},
template: getTemplate('#tpl-postcontent')
});
Also try to avoid mutating state outside mutation handler. You can add a mutation to set your currentPost like this:
<template id="tpl-posts">
...
<li v-for="row in rows" :key="row.id">
<router-link :to="url_path" #click.native="openPost(row.id)">
{{ row.title }}
</router-link>
</li>
...
</template>
const Posts = Vue.component('posts', {
//...
methods: {
openPost: function(id)
{
this.$store.commit('SET_CURRENT_POST', id)
}
},
template: getTemplate('#tpl-posts')
});
const store = new Vuex.Store({
state: {
dataAll: {},
currentPost: {}
},
mutations: {
SET_CURRENT_POST: function(state, id) {
let post = state.dataAll.find(data => data.id === id)
state.currentPost = post
}
}
});
fiddle
I have a collection of Events. These will render in lists according to their status - upcoming/live/previous. Thus the rendering is dependent on the current time. How can I make the computed properties to update/recompute as time goes by?
<template>
<div>
<h2>Upcoming events</h2>
<p v-bind:key="event.name" v-for="event in upcomingEvents">{{ event.name }}</p>
<h2>Live events</h2>
<p v-bind:key="event.name" v-for="event in liveEvents">{{ event.name }}</p>
<h2>Previous events</h2>
<p v-bind:key="event.name" v-for="event in previousEvents">{{ event.name }}</p>
</div>
</template>
<script>
import Event from '../Event.js'
export default {
data() {
return {
events: []
}
},
computed: {
upcomingEvents() {
return this.events.filter(event => event.isUpcoming())
},
liveEvents() {
return this.events.filter(event => event.isLive())
},
previousEvents() {
return this.events.filter(event => event.isPrevious())
},
},
mounted() {
// this.events are populated here
}
}
</script>
You can declare a time-dependent data variable and use setInterval() to update it:
data() {
return {
events: [],
now: Date.now()
}
},
created() {
var self = this
setInterval(function () {
self.now = Date.now()
}, 1000)
},
computed: {
upcomingEvents() {
return this.events.filter(event => event.isUpcoming(this.now))
},
liveEvents() {
return this.events.filter(event => event.isLive(this.now))
},
previousEvents() {
return this.events.filter(event => event.isPrevious(this.now))
}
}
Note that you need to use now in computed properties to make them update.
One possibility for your case is $forceUpdate(). However, it should be note that it will work specifically for your case because you're NOT using child components.
If you were to use child components, you would then need to use slots within the parent component and insert the children within their respective slots.
So, for example, you could do:
created() {
setInterval(() => {
this.$forceUpdate()
}, 5000)
}
Which will cause the entire component to re-render. This may or may not be the desirable interaction you're looking for.
You could create a time variable that you update every second then use this variable in your computed properties.
new Vue({
el: "#app",
data: {
time: ''
},
computed: {
computedValue: function(){
return this.time;
}
},
mounted: function(){
var app = this;
setInterval(function(){
app.time = parseInt(new Date().getTime() / 1000);
}, 1000);
}
})
https://jsfiddle.net/ecwnvudz/
I have a sub component that gets some data passed in via props and some via computed properties. It works fine until I do a hard page reload; then it fails at the 'this.attributes.manufacturer' which returns undefined. Anyone suggest a way to fix this please?
<template>
<span>
{{ manufacturer | htmlDecode }}
</span>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters({
attributes: 'extraCart/attributeListByCode'
}),
manufacturer () {
let manufacturer = this.product.manufacturer
if (this.attributes.manufacturer) {
let option = this.attributes.manufacturer.options.find(av => {
return av.value === manufacturer
})
if (option) {
return option.label
}
}
}
},
props: {
product: {
type: Object,
required: true
}
}
}
</script>
<style scoped rel="stylesheet/stylus" lang="stylus">
</style>
You can add some condition checking:
if (this.product && this.attributes.manufacturer) {}
export default {
computed: {
...mapGetters({
attributes: 'extraCart/attributeListByCode'
}),
manufacturer () {
if (this.product && this.attributes.manufacturer) {
let manufacturer = this.product.manufacturer
let option = this.attributes.manufacturer.options.find(av => {
return av.value === manufacturer
})
if (option) {
return option.label
}
}
}
},
props: {
product: {
type: Object,
required: true
}
}
}
Turned out to be the way I set the mutations was set to an object instead of using Vue.Set which then made these attributes reactive. Thanks for your guys' help.