I have read a lot of other topics regarding it but still have not got a solution.
I am trying to access a store state in my computed property and passing it as a prop to a child component. Changing that state is not triggering my computed property. It is still evaluating the old value.
Here is my computed property. I am trying to access a property in the state through a getter.
getRFQProjectStatus() {
if (this.project)
return this.$store.getters["RBViewStore/getRFQProjectStatus"](this.project.projectId);
return undefined;
},
I have even tried directly hardcoding the projectId instead of sending it through object, but still the same result.
My mutation:
setProjectStatus(state, { projectId, property = "", status }) {
console.log(property);
let project = state.projectsListView[projectId];
project.RFQProjectData.rfqProjectStatus = status;
var updatedRFQProj = Object.assign({}, state.projectsListView[projectId]);
Vue.set(state.projectsListView, projectId, updatedRFQProj);
},
Action
updateProjectStatus(store, { projectId, property, status }) {
store.commit("setProjectStatus", {
projectId,
property,
status,
});
console.log(store.getters.getRFQProjectStatus(projectId));
},
Getter
getRFQProjectStatus: state => projectId => {
if (state.projectsListView[projectId] && state.projectsListView[projectId].RFQProjectData && state.projectsListView[projectId].RFQProjectData.rfqProjectStatus)
return state.projectsListView[projectId].RFQProjectData.rfqProjectStatus;
return undefined;
}
state
export const state = {
projectsListView: {},
};
Printing my getter value after committing is printing the updated value. I am not sure what I am doing wrong. Any help is appreciated.
Related
I have the following code, and the darkmode getter updates correctly. The initial state of _darkmode = false.
state = {
_darkmode: false
}
darkmode: (state) => {
if (localStorage.getItem(STORAGE_KEY_DARKMODE) === null) {
return state._darkmode;
} else {
const newDarkmode = JSON.parse(
localStorage.getItem(STORAGE_KEY_DARKMODE)
);
if (state._darkmode) {
console.log("parsed", newDarkmode);
} else {
console.log("parsed", newDarkmode);
}
return newDarkmode;
}
},
But the following code results in the getter not being updated:
darkmode: (state) => {
if (localStorage.getItem(STORAGE_KEY_DARKMODE) === null) {
return state._darkmode;
} else {
const newDarkmode = JSON.parse(
localStorage.getItem(STORAGE_KEY_DARKMODE)
);
return newDarkmode;
}
},
I'm clueless on what the "magic" behind this is.
Vuex caches the result of the getter and only will update its value when the dependencies changes.
Here a quote from the docs:
Vuex allows us to define "getters" in the store. You can think of them as computed properties for stores. Like computed properties, a getter's result is cached based on its dependencies, and will only re-evaluate when some of its dependencies have changed.
Link: https://vuex.vuejs.org/guide/getters.html#getters
I think you should update the _darkmode value with some action inside your code logic with the value from the localStorage, then the getter will be updated.
Context:
I have a reports application that contains a report editor. This Report Editor is used to edit the contents of the report, such as the title, the criteria for filtering the results, the time range of results, etc..
The Problem:
There is something wrong with the way I have used Vuex/Vuejs in my components I believe. My store contains getters for each aspect of this report editor. Like this:
const getters = {
activeReportTitle: state => {
return state.activeReport.title;
},
activeReportID: state => {
return state.activeReport.id;
},
timeframe: state => {
return state.activeReport.timeframe;
},
includePreviousData: state => {
return state.activeReport.includePreviousData;
},
reportCriteria: state => {
return state.activeReport.reportCriteria;
},
emailableList: state => {
return state.activeReport.emailableList;
},
dataPoints: state => {
return state.activeReport.configuration?.dataPoints;
},
...
Each getter is used in a separate component. This component uses the getter only to initialize the local data, and uses actions to modify the state. The way I have done this is by adding a local data property and a watcher on the getter that changes the local data property. The component is using the local data property and that data property is sent to the action and the getter is updated.
ReportSearchCriteria.vue
...
data() {
return {
localReportCriteria: [],
currentCriteria: "",
};
},
watch: {
reportCriteria: {
immediate: true,
handler(val) {
this.localReportCriteria = [...val];
}
}
},
computed:{
...reportStore.mapGetters(['reportCriteria'])
},
methods: {
...reportStore.mapActions(["updateReportCriteria"]),
addSearchCriteria() {
if (this.currentCriteria) {
this.localReportCriteria.push(this.currentCriteria);
this.updateReportCqriteria(this.localReportCriteria);
}
this.currentCriteria = "";
this.$refs['reportCriteriaField'].reset();
},
...
The hierarchy of the components is set up like this
Reports.Vue
GraphEditor.vue
ReportSearchCriteria.vue
Could you clarify what the problem is? Does the 'reportCriteria' not get updated when it's supposed to? How does the function 'updatedReportCriteria' look like? You use mutations to update a state in the store. Also, you have a typo when you're calling the action.
Following the tutorial at this web address http://stackabuse.com/single-page-apps-with-vue-js-and-flask-state-management-with-vuex/, I encountered a problem that the function in the computed property was not automatically invoked after the state in the store was changed. The relevant code is listed as following:
Survey.vue
computed: {
surveyComplete() {
if (this.survey.questions) {
const numQuestions = this.survey.questions.length
const numCompleted = this.survey.questions.filter(q =>q.choice).length
return numQuestions === numCompleted
}
return false
},
survey() {
return this.$store.state.currentSurvey
},
selectedChoice: {
get() {
const question = this.survey.questions[this.currentQuestion]
return question.choice
},
set(value) {
const question = this.survey.questions[this.currentQuestion]
this.$store.commit('setChoice', { questionId: question.id, choice: value })
}
}
}
When a radio button in the survey questions is chosen, selectedChoice will change the state in the store. However surveyComplete method was not called simultaneously. What's the problem? Thanks in advance!
surveyComplete() method does not 'spy' your store, it will be updated, when you change this.survey.questions only. So if you modify the store, nothing will happen inside surveyComplete. You may use the store inside the method.
I have a situation where I need to update data when it detects changes to a state. The user needs to be able to make further changes this info within a textarea. Using computed properties pulls in the data exactly how I want, but any changes made by the user after this are overridden because the computed property keeps changing this data back to it's initial values. What would be the best way to pull in data initially upon a state change but then allow for editing after that point?
Thanks!
Edit: Updated to what i've tried for #Libby.
<textarea v-model="exampleData"></textarea>
computed: {
...mapGetters({
item: 'item'
})
methods: {
exampleFunction() {
this.exampleData = this.item;
}
mounted() {
this.exampleFunction();
}
Update exampleData in a watcher for item:
watch: {
item(value) {
this.exampleData = value;
}
}
This way you can bind your exampleData to the textfield, but changes to the item will still affect it.
And if you want exampleData to be initially set to the value of item, do that in the component's mounted hook:
mounted() {
this.exampleData = this.item;
}
Here's a fiddle.
If you set your property indata, you can initialize it in mounted which only runs once when the page is loaded:
data:
text: null
mounted: ->
text = "This text is initialized"
And then set v-model on your textarea
<textarea v-model="text"></textarea>
So the value of the textarea will start out as "This text is initialized", but the user will be able to change it, and those changes will be saved in text
Vue already has a built-in solution to handle this if you use the getter/setter syntax for computed properties
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
As a result, when your state changes you can update the computer property by assigning it a value:
// state has changed in text area handler
this.fullName = 'new value'
I had an API call to the backend and based on the returned data, I set the reactive data dynamically:
let data = {
quantity: [],
tickets: []
}
api.default.fetch()
.then(function (tickets) {
data.tickets = tickets
tickets.forEach(ticket => {
data.quantity[ticket.id] = 0
})
})
Based on this flow, how can I set watcher for all reactive elements in quantity array dynamically as well?
You can create a computed property, where you can stringify the quantity array, and then set a watcher on this computed property. Code will look something like following:
computed: {
quantityString: function () {
return JSON.stringify(this.quantity)
}
}
watch: {
// whenever question changes, this function will run
quantityString: function (newQuantity) {
var newQuantity = JSON.parse(newQuantity)
//Your relevant code
}
}
Using the [] operator to change a value in an array won't let vue detect the change, use splice instead.