I have this same design as an official nested vuedraggable example here.
I am using vuex in my version and I am wondering if it is possible to update the items within the rows with a getter/setter computed property so they trigger updating the store state. I have essentially the same file but the computed property for the getter and setter for rows updates the state, however, I can't use a getter and setter on the subcomponent (items) as I don't believe I can pass a parameter to the computed property to identify the row it resides in.
How could I do this with vuex and having an array structure of rows that have arrays of items within each row?
UPDATE
#HuyDuy I took your advice. I was able to use a function with an object including multiple pieces of data in it. In case someone finds this useful in the future this is how I got it to work:
methods: {
...mapActions('builder', ['setSteps']),
},
computed: {
...mapGetters('builder', ['getSteps']),
steps: {
get() {
return this.getSteps(this.stageID);
},
set(val) {
this.setSteps({steps: val, stageID: this.stageID});
}
},
},
In vuex then I have a getter like this:
getSteps: (state) => (stageID) => {
return state.stages.find(stage => stage.id === stageID).steps;
},
And a setter (mutation) like this, (an action not listed here just commits it):
SETSTEPS(state, payload) {
state.stages.find(stage => stage.id === payload.stageID).steps = payload.steps;
},
This seems to sync the data, is reactive and because it's handled by a mutation I can track changes, (to provide undo and redo), which is what I was after.
Related
I have a button that’s set to update the a store object using Vue.set but the getter for that same piece of data in a different component isn’t reactive until I change the state using a different component method.
The state object in question is set up as a hash that's keyed by UUID's. The object is generated and then added to the state object with Vue.set
The button is set to dispatch an action, which I see it going through immedietely in the devtool, that does this:
mutations: {
COMPLETE_STEP(state, uuid) {
let chat = state.chatStates[uuid];
let step = chat.currentStep;
Vue.set(chat.data[step], "complete", true);
}
},
actions: {
completeStep({ commit }, uuid) {
commit("COMPLETE_STEP", uuid);
}
},
Now, when I want to grab that data, I have a getter that grabs that data. This doesn't run until I do something else that causes a re-render:
getters: {
getChatStepComplete: state => (uuid, step) => {
let chatState = state.chatStates[uuid];
return chatState.data[step].complete;
},
}
I want the getter to show the updated change right away instead of waiting to update on a different re-render. How do I make that happen?
Figured out my issue: I wasn’t creating the data array when I first create and add chat to the state. Once I started initializing it to an empty array, it’s started being reactive.
I have standard Vue.js component and I'd like to convert attributes in data property to watcher or in other words I want to construct a watch object based on data property attributes automatically
my idea looks something like this
watch: {
...(() => {
const watchers = {}
Object.keys(this.$data).forEach(key => {
watchers[key] = () => {
new ProductNutrientUpdate(this).run()
}
})
return watchers
})(),
},
the problem with this approach is that this.$data is not constructed yet
maybe there is some way how I can add watchers in created hook for example??
Vue already watches properties of the data object (note if any of these values are themselves objects, I think you need to update the whole object, i.e. change its value to a shallow copy with the desired nested key-values).
Refer to: https://v2.vuejs.org/v2/guide/reactivity.html
You can then use the update lifecycle hook to watch for all changes to data: https://v2.vuejs.org/v2/api/#updated
I was able to resolve a challenge using the following approach
created() {
Object.keys(this.$data).forEach(key => {
this.$watch(key, function() {
// ... some logic to trigger on attribute change
})
})
}
I have a component whose purpose is to display a list of items and let the user select one or more of the items.
This component is populated from a backend API and fed by a parent component with props.
However, since the data passed from the prop doesn't have the format I want, I need to transform it and provide a viewmodel with a computed property.
I'm able to render the list and handle selections by using v-on:click, but when I set selected=true the list is not updated to reflect the change in state of the child.
I assume this is because children property changes are not tracked by Vue.js and I probably need to use a watcher or something, but this doesn't seem right. It seems too cumbersome for a trivial operation so I must assume I'm missing something.
Here's the full repro: https://codesandbox.io/s/1q17yo446q
By clicking on Plan 1 or Plan 2 you will see it being selected in the console, but it won't reflect in the rendered list.
Any suggestions?
In your example, vm is a computed property.
If you want it to be reactive, you you have to declare it upfront, empty.
Read more here: reactivity in depth.
Here's your example working.
Alternatively, if your member is coming from parent component, through propsData (i.e.: :member="member"), you want to move the mapper from beforeMount in a watch on member. For example:
propsData: {
member: {
type: Object,
default: null
}
},
data: () => ({ vm: {}}),
watch: {
member: {
handler(m) {
if (!m) { this.vm = {}; } else {
this.vm = {
memberName: m.name,
subscriptions: m.subscriptions.map(s => ({ ...s }))
};
}
},
immediate: true
}
}
I have been using Vuex to control my whole app state for a while. But now I am facing a problem I have never meet before.
The Workflow is like this:
The root component will fetch data from my database, which is an array with multiple objects in it.
I created child-component and use v-for to show this array to user (that means, each child-component represents an object in the array later)
The Problem comes, when I try to fetch async data for each child-component, for the data-fetching, I also need parameter which come from the array I mentioned.
I use the Vuex actions in the created hook of my child components. The actions fetch_sim_Type will take the payload (which is parameter from parent-component, also from the initial array). And change the state simType in the state: {} of Vuex.
By using the computed properties I can get the fetched simType in my child-component and show it to the user. Everything works so far.
CODES:
THE initial Array (simSumArray in Parent-component) looks like this:
[
{ typeid: 1, name: XXX },
{ typeid: 2, name: ZZZ },
{ typeid: 3, name: GGG },
{ typeid: 4, name: JJJ },
]
PARENT-COMPONENT:
<template
v-for="(singleSim, idx) in simSumArray"
>
<sim-single :singleSim="singleSim"></sim-single>
</template>
CHILD-COMPONENTS:
props: ['singleSim'],
created () {
store.dispatch('fetch_sim_Type', this.singleSim.typeid);
},
computed: {
simType () {
console.log("store.getters.get_simType: ", store.getters.get_simType)
return store.getters.get_simType;
}
},
IN VUEX:
state: {
simType: 'unknown'
},
actions: {
fetch_sim_Type (context, typeid) {
//.. fetch the data based on typeid from DB
context.state.simType = fetchedData;
}
}
But it only works when in the array only when object exist. When there are more than one child-component being created. The state simType in the Vuex store.js will be replaced many times and in every child-component the simType() is always the same.
The Problem is kind of hard to describe. The central problem is, the state in Vuex is meant to be shared everywhere in the whole app, so if I have multiple child-components, they all fetch data for themself, than the shared state will be replaced all the time and I can't get individual state for every child-components.
I don't know if I describe the problem clair but I really tried hard to.
Maybe There is a better way to do this data fetching job without Vuex or maybe I just used the Vuex by the wrong way.
I am sure this should not be a hard question. But I can't find any relative answer online.
Reading your code, the behaviour you describe is normal. I see two solution to your problem (solution 2 is probably closer to what you are looking for) :
Solution 1 - store simType in your component
if you need to access the simType from somewhere else than inside your component and have it stored in your state, skip to solution 2
When your component is created, store the simtype in the component's data. This would look like this:
In your component:
data () {
return {
simType: undefined //declare simType as one of your component's data property
}
},
created () {
store.dispatch('fetch_sim_Type', this.singleSim.typeid).then(simType => {
this.simType = simType //store the fetched simType
})
}
In your vuex Actions:
actions: {
fetch_sim_Type (context, typeid) {
//.. fetch the data based on typeid from DB
return fetchedData //pretty much the same except return the result
}
}
Solution 2 - store simTypes in your state indexed by their IDs
Store your fetched simType by id, like this:
state: {
simTypes: {} //simTypes is now plural, and is an empty object at first. It will later contain several simTypes, indexed by their respective Ids
},
actions: {
fetch_sim_Type (context, typeid) {
//.. fetch the data based on typeid from DB
context.state.simType[typeid] = fetchedData; // a fetched simtyped will be store in simTypes and can be accessed with the typeid as a key
}
}
to retrieve a simType, you can write a vuex getter like this:
getters: {
getSimTypeById: (state) => (typeId) => {
return state.simTypes[typeId]
}
}
So in your example, the computed method would be :
computed: {
simType () {
console.log("store.getters.getSimTypeById(this.singleSim.typeid): ", store.getters.getSimTypeById(this.singleSim.typeid)
return store.getters.getSimTypeById(this.singleSim.typeid);
}
},
This solution, as a bonus, allows you to fetch a simType only once if several of your items have the same simType.
I have had success by keeping shared data in the Vuex store, and watching it from my components.
Although not a best practice, I sometimes don't even bother to use actions or commits to change the state, and just modify the state directly. In this scenario, Vuex just acts like a shared data object for all my components.
Vue Store
state: {
myvalue: []
}
Components
watch: {
'$store.state.myvalue'(value) {
}
}
I have a list of todos that I'd like to watch and auto update when a change is made in a firebase backend
Got a bit stuck on what i hope is the last step (I am using nuxtjs for a SPA)
I have a getter in my vuex store as follows
getMyState: state => {
return state.todos
}
I am returning this getter to my component as follows
computed: {
...mapGetters(['getMyState'])
},
How do i now get my list to recognise when something has changed and update my array of results?
Thanks
Use watch property
watch: {
getMyState: function (new, old) {
}
},