How can I directly set value in state of VueX - vue.js

I just want to change data in state of VueX without pass value through following step Action > Mutation > State then getData from state of VueX in other component, Is it possible to do or anyone has another best way to do send value with array to ...mapAction please explain me,
Actually, I just want to send data with array to other component which the data will be change every time when user selected checkbox on Treevue component that I used it.
Thank a lot.
## FilterList.vue ##
export default {
data() {
return {
listSelected: ['aa','bb','cc','...'], // this value will mutate when user has selected checkbox
}
}
}
=================================================================
## store.js ##
export default new Vuex.Store({
state = {
dataSelected: [ ]
},
mutation = {
FILTERSELECTED(state, payload) {
state.selected = payload
}
},
action = {
hasSelected(context,param) {
context.commit('FILTERSELECTED',param)
}
},
getters = {
getSelected: state => state.dataSelected,
}
strict: true
})

You can set strict: false and change data directly, but I wouldn't recommend it.
You'll lose the benefit Vuex provides, i'd rather share that object outside vuex.
Not every change needs to be synced with the store, it depends on the scenario.
For a EditUser component as example, I'll start with a deep copy of the user object from the store:
this.tmpUser = JSON.parse(JSON.stringify(this.$store.state.user))
This tmpUser is disconnected from the store and won't generate warnings (or updates) when you change its properties.
When the user presses the "save" button, i'll send the changed object back to the store:
this.$store.dispatch("user/save", this.tmpUser)
Which updated the instance in the store and allows the other parts of the application to see the changes.
I also only write actions when async (fetching/saving data) is needed.
For the sync operations I only write the mutations and the use the mapMutations helper or call $store.commit("mutation") directly.

Related

How can I keep the state saved when I coming to same route component from another in vuejs?

I have a claims dropdown and function called on change of dropdown value is like this:
changeDdVal(v) {
this.$store.dispatch('getUserRoles').then(userRoles => {
if (userRoles.admin) {
this.selectedYear = new Date().getFullYear();
switch (v) {
case 1:
this.dropValue = "salesClaimCount";
this.headerDropdownClaim = "sales";
this.tableHeadData = this.salesTableHeadData;
this.api.filterData = "getSalesClaimFilterDataForApprover";
this.$router.push({name: this.routeName, params: {approverClaimType: this.claimTableData.claimStatus, dropType: this.headerDropdownClaim, userRole: this.role}})
.catch(() => {});
this.yearChange(this.selectedYear);
break;
case 2:
this.dropValue = "staffingClaimCount";
this.headerDropdownClaim = "staffing";
this.tableHeadData = this.placementTableHeadData;
this.api.filterData = "getStaffingClaimFilterDataForApprover";
this.$router.push({name: this.routeName, params: {approverClaimType: this.claimTableData.claimStatus, dropType: this.headerDropdownClaim, userRole: this.role}})
.catch(() => {});
this.yearChange(this.selectedYear);
break;
}
}
})
}
And this yearChange function is taking care of calling api with the selected year and display data accordingly. I am calling this changeDdVal function on created state. When the data is displayed and if I click on any data, I am routing to detail view of that particular record.
Now the problem is when I change the year to any other from current year and click on that particular year data and come back to dashboard using "$router.go(-1)", my states resets i.e the year changes to current again but I want to remain in the selected year.
I know this is happening because of the "this.selectedYear = new Date().getFullYear();" and I can use vuex to store the state instead of this.
But in my case I want to reset the state on drop change and save the state if I come from the detail view of that record of selected year.
For example, by default the year is 2022 and I change it to 2021 the data is shown of 2021 and I click on any record of 2021 and use $router.go(-1) on that I should be in 2021. And if I have selected 2021 year and change the drop from sales to placement the year should reset to 2022.
this is my year change function
yearChange() {
this.axiosCancelToken.cancel("cancel requested")
this.changeYear = false;
this.firstLoad = true;
this.sortBy = "claimId";
this.sortType = "descending";
this.handlePagination(this.paginationOptions);
this.mavePaginationList(false);
this.fetchData();
var dataApi = new FormData();
dataApi.append("year", this.selectedYear);
api
.post(this.api.filterData, {data: dataApi})
.then((res) => {
//resposne
})
},
According to your question-
You need to keep some values saved when changing the route and some values to refresh as per API requests. (keep-alive won't do much in that use case.)
the values which need to persist are either a form variable (select dropdown) or some manual variable (pagination number).
As you said you can't use Vuex because of some problems, My guess, is that Vuex and vuex-map-fields together should ease the process of your problem.
vuex-map-fields is a good way to enable two-way data binding for fields saved in a Vuex store.
If I take your question as an example-
Suppose at your Home.vue you change the dropdown value, and on its changes, an API request gets fired and brings some data which you display on your component, and you can select any data which will lead to a new route, let's say About.vue and when you come back to Home.vue the year you selected should persist.
So, you can do this by like this-
Install the vuex-map-fields and configure it properly.
Create a property in your Vuex state of name, "selectedYear", like this-
import Vuex from "vuex";
// Import the `getField` getter and the `updateField`
// mutation function from the `vuex-map-fields` module.
import { getField, updateField } from "vuex-map-fields";
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
selectedYear: null
},
getters: {
// Add the `getField` getter to the
// `getters` of your Vuex store instance.
getField
},
mutations: {
// Add the `updateField` mutation to the
// `mutations` of your Vuex store instance.
updateField
}
});
In your Vue template, use this Vuex state property to get and mutate the selectedYear.
<select v-model="selectedYear" #change="callAPI()">
<option>2020</option>
<option>2021</option>
<option>2022</option>
</select>
import { mapFields } from "vuex-map-fields";
export default {
name: "Home",
computed: {
// The `mapFields` function takes an array of
// field names and generates corresponding
// computed properties with getter and setter
// functions for accessing the Vuex store.
...mapFields(["selectedYear"]),
},
}
And when you want to reset the dropdown state, simply do this-
this.selectedYear = null;
It will update in the Vuex state as well.
In that way, you should notice that when you switch the route, the selected box state will persist because this is bonded with the store.
In the same fashion, you can keep the state of pagination, and another variable you want.
The benefit of using vuex-map-field is that you don't need to write getter and setter functions manually to contact the state.
Note- If you want to persist your Vue state between page reloads also, then use vuex-persistedstate with Vuex.
As your code depends on multiple concepts, I cannot provide the snippet here, but I created a fiddle and try to reproduce the problem, you can look at it, and modify it according to your case and make sure things work.
In the fiddle, I used the same problem mentioned in your question (but the data and API request is dummy), and you should see when you change the routes (from Home.vue to About.vue), the year would persist but when reload the page it won't because I didn't use vuex-persistedstate.
Here it is- https://codesandbox.io/s/vue-router-forked-mdvdhd?file=/src/views/Home.vue:935-1181

How to empty a Vuex store module state object?

How to empty a Vuex store module state object completely? I know how to delete a single property of state object for e.g Vue.delete(state.slidesList, 'slide1') but I want to completely empty the state object (not delete the object itself) without losing reactivity on the object itself, when individual properties are deleted (using Vue.delete) removes reactive getters and setters, I believe, pls correct if this is wrong.
Does directly setting state.slideList = {} empties the object, while still being reactive? if yes then it implies state.slideList = {fname: 'Appu' } is a way to overwrite the object without emptying the object (if the goal is to overwrite the object completely with another object, rather than empty and re-write in it).
If not, what is the correct way to overwrite the state object with another new non-empty object.
Thanks
set adds a property to a reactive object, ensuring the new property is also reactive, so triggers view updates. This must be used to add
new properties to reactive objects, as Vue cannot detect normal
property additions. doc
module.js
import Vue from 'vue'
const initialState = {
foo: {},
bar: {},
// ...
}
const state = {...initialState}
const mutations = { // To reset a state
RESET_FOO (state) {
Vue.set(state, 'foo', initialState.foo)
},
UPDATE_FOO (state, payload) { // To update the state
Vue.set(state, 'foo', payload)
},
UPDATE_IN_FOO (state, { key, value }) { // To update a key in a state
Vue.set(state.foo, key, value)
},
RESET_MODULE (state) { // To reset the entire module
Object.assign(state, initialState)
}
}
Vuex has replaceState method that replaces store's root state.
Create a mutation that resets the state. Then, reset the state with a default value like this:
Object.assign(state, newState)

vuex: do not mutate vuex store state outside mutation

I have the following scenario:
Component Textfield:
<v-text-field
v-model="form.profile.mobile_business"
label="Mobile"
prepend-inner-icon="mdi-cellphone"
></v-text-field>
I get the current value via:
data() {
return {
form: {
profile: JSON.parse(JSON.stringify(this.$store.getters["user/Profile"])),
},
};
},
I have a submit button that calls this method:
updateUserProfile() {
this.$store.dispatch("user/updateProfile", this.form.profile);
}
Everything works perfect. On my store dispatch I make the API call and update the store via my mutation:
context.commit('UPDATE_PROFILE', profile);
No errors until this step.
But if I change the form input again - after I pressed the submit button, I get:
vuex: do not mutate vuex store state outside mutation
But I don't want to change the vuex store just when I change the value on my form input.
It should only be updated if someone hits the submit button.
v-model provides 2-way data binding. Changing anything in the view will automatically attempt to update the model directly, rather than through a mutation. Thankfully, Vue allows get and set on computed properties to help us past that.
What you should do on your textfield component is add a computed property with get and set methods. It will look something like this:
computed: {
userProfile: {
get() {
JSON.parse(JSON.stringify(this.$store.getters["user/Profile"]));
},
set() {
// only commit the changes to the form, do not submit the action that calls the API here.
this.$store.commit("user/updateProfile", this.form.profile)
}
}
Your v-model attribute should then be set to this newly created property, and any 'set' operations (read: a user changing the input value) will call the action as opposed to attempting to set the value in the Store directly.
Here is a live example: CodePen
I solved it this way:
form: {
profile: _.cloneDeep(this.$store.getters['user/Profile'])
},
and added a watch handler:
form: {
handler: _.debounce(function (form) {
console.log("watch fired");
}, 500), deep: true
}
so if the user changes the value, nothing happens (except my console.log action).
if he presses the submit button, the store dispatch action will be fired.

Computed Property does not get updated when state changes

We are trying to detect whether a person is logged in or not using the vuex store state: loggedIn. When I call the API service from the action it calls the mutation after successful login and changes the data in the state:
loginSuccess(state, accessToken) {
state.accessToken = accessToken;
state.authenticating = false;
state.loggedIn = true;
console.log(state.loggedIn);
}
The console.log() shows the value, so the mutation is working.
In my other component, I use a computed property to watch for changes in the store using ...mapState() and bound the property in the template view:
computed: {
...mapState('authStore',['loggedIn' ]);
}
But the view never gets updated based on the computed property. I checked using the Vue dev tools in the console. It shows the state changes.
I have initialized the state.
export const states = {
loggedIn: false
};
I have tried to call the state directly.
this.$store.state.authStore.loggedIn;
I have tried different approaches.
...mapState('authStore', { logging:'loggedIn' });
//or
...mapState('authStore',['loggedIn' ]);
also, tried watch: {} hook but not working.
Interestingly though, the state's getter always shows undefined, but the state property changes in the dev tools.
Cannot figure out what is wrong or how to move further.
here is the screenshot of devtools state after successful login:
This catches my eye:
export const states = {
loggedIn: false
};
My suspicion is that you're then trying to use it something like this:
const store = {
states,
mutations,
actions,
getters
}
This won't work because it needs to be called state and not states. The result will be that loggedIn is unreactive and has an initial value of undefined. Any computed properties, including the store's getter, will not be refreshed when the value changes.
Whether my theory is right or not, I suggest adding console.log(state.loggedIn); to the beginning of loginSucess to confirm the state prior to the mutation.

Watching and tracking state changes with Vuex inside child components

This is more a "which method should I use" question, rather than a how to.
I have the following in my Vuex.Store() instance:
store.js:
export default new Vuex.Store({
state: {
acceptedTermsAndConditions: false
},
})
From various components I'm emitting an event which sets this.$store.state.acceptedTermsAndConditions to true or false, dependent on different User inputs.
However, in my component I would set the checked value of a "Accepts T&Cs" checkbox to this value, something like this:
components/Component.Vue:
data () {
return {
form: {
checkboxTermsAndConditions: this.$store.state.acceptedTermsAndConditions
}
}
}
I'm just not sure what method handles this? Does a solution require a getter? If not, what is the best way to watch for state changes and set data values accordingly?
If you want to set the checkbox state based on the stored value, you should use the computed object and the mapGetters helper function in your component:
https://vuex.vuejs.org/guide/getters.html#the-mapgetters-helper
computed: {
...mapGetters(['acceptedTermsAndConditions'])
}
Like this, the value will be accessible in your component. If you want to do the contrary (refresh the store based on the checkbox value), you should create a mutation in your store and you should use this in your component:
methods: {
...mapMutations(['setTACcheckbox'])
}
This way, inside your component you can refresh the store value with this.setTACcheckbox(value).