VueX: wait until store data is loaded - javascript

I am currently stuck at trying to show a form to edit a existing site.
My Problem is one site has about 60 Input fields and writing setters and getters for every input seems not like a good approach.
So the best thing I could think of is to save my store data to a local variable, edit the local variable and send it back.
Edit.vue
<b-form-input id="currentSiteName" v-model="this.editSite.title"></b-form-input>
...
computed: {
editSite() {
return this.$store.state.currentSite
}
},
mounted: function() {
this.$store.dispatch('SHOW_SITE', {
siteId: this.$route.params.id
});
},
Store action
SHOW_SITE: function ({ commit }, siteParams) {
http.get('/sites/' + siteParams.siteId).then((response) => {
commit('SHOW_SITE', {
site: response.data.foundSite
});
},
(err) => {
// eslint-disable-next-line
console.log(err);
})
},
Store mutations
SHOW_SITE: (state, { site }) => {
state.currentSite = site;
},
If I look in my vue-dev-tools I can see that editSite has the correct values and the values are all shown in the form but i get following two errors:
Error in event handler for "input": "TypeError: Cannot read property 'editSite' of null"
Cannot read property 'editSite' of null at callback
What I am doing wrong here or is there a better / c way to solve my problem?
Any help would be greatly appreciated!

You should use getters for access to store states.
import { mapGetters, mapActions } from 'vuex'
async mounted() {
await this.showSite(this.$route.params.id);
},
computed: {
...mapGetters([
'currentSite',
]),
},
methods: {
...mapActions([
'showSite'
]),
},
Now, this way you should able to access store states without null exception. And you should use async await for http.get. This way your code looks cleaner.

The following documentation provides a helpful explanation of what I think you are trying to achieve: https://vuex.vuejs.org/guide/forms.html
In short, bind the :value attribute of the form field to the computed property. Listen to a change event and ensure that the actual mutation of the Vuex store property happens within a Vuex mutation handler. Also, note the use of the MapState function which acts as a nice little helper to map store properties to component computed properties.
Make sure that you are thinking of the Store state prior to when the AJAX request has completed and updates the store. Setting a default state for the store will help alleviate any null references. Also worth noting that this. in the "this.editSite.title" attribute binding is unnecessary.

Related

BeforeMount not working on a computed method

Hello i have a problem whenever i reload my page i want to call a beforeMount() method to call my filterRecords (computed method) so that i can receive the data ,but it is just not working fine it is telling me that filterRecords is not a function.
My computed:
computed: {
...mapGetters({
sec: "sec"
}),
createdDate() {
return moment().format("DD-MM-YYYY ");
},
createdHours() {
return moment().format("HH:mm ");
},
filteredRecords() {
return this.records.filter(record => {
return record.template_id === this.sec;
});
}
},
so i just do this:
beforeMount() {
this.filteredRecords();
},
it is not calling it so i get nothing to my filteredRecords.
how am i able to call it when i reload the page ( i am recieving the records data from an API rails)
Be sure to check the Vue docs about computed properties.
You should not call them as functions, but as properties (since they are computed properties).
You can try to console log filteredRecords in the beforeMount hook like this:
beforeMount() {
console.log(this.filteredRecords)
},
This seems like a fundamental misunderstanding on how Computed properties work. Computed properties are accessed the same way you would access props or data on a component. They are consumed as values, not as methods. A big hint to this fact is that you're calling map**Getters**
You could consume filteredRecords in your template something like:
<div v-for="record of filteredRecords" :key="record.someKey">...</div>
or by assigning it to a data property
this.records = this.filteredRecords

Mutating data in computed property also changed data in vuex store: why? Any help is appreciated

For my current project, I am working on a shop page. I am sending the data of each order made to my Laravel backend in order for it to be processed. Part of this data is an array with all the ordered products in it. I use JSON.stringify() on this array to avoid errors in the Laravel backend.
My issue is that when I stringify the array in a computed property, this also changed the data in my Vuex store for some reason. This in turn obviously causes quite a bit of error. Is this normal behavior, or am I doing something wrong? How can I avoid this from happening? Thanks in advance!
You can see my component's code in the snippet below.
import { mapGetters } from "vuex";
export default {
computed: {
...mapGetters({
order: "getOrder",
countries: "getCountries"
}),
orderData() {
let myOrder = this.order;
if (myOrder.products) {
myOrder.products = JSON.stringify(myOrder.products);
}
return myOrder;
},
country() {
return this.countries.find(
country => country.iso_3166_2 === this.order.country
).name;
}
},
methods: {
placeOrder() {
console.log(this.orderData);
}
}
};
</script>
You are assigning to myOrder the reference of this.order, subsequently all your modifications inside it will affect this.order (so you are mutating).
In this case, since you just want to modify products, you can shallow copy this.order just like this:
let myOrder = { ...this.order };
Then, all the properties at the first level will have different pointers, so you can change them without fear of mutations.

Event never gets emitted in VueJS within an axios call

I have a component which needs to initialize data at the beginning, it does some transformations to the data (based on some values which are saved in localstorage to mark those data as selected).
Using Vue 2.5.
// component.vue
import Vue from 'vue'
export default Vue.extend({
data() {
fetchedData: [],
},
mounted() {
this.getStuff()
window.Bus.$on('localStorageRefreshed', this.getStuff)
},
computed: {
selectedData() {
return this.fetchedData.filter(data => data.selected)
}
},
methods: {
getStuff() {
const doTransformations = function (res, existing) {
// blabla
}
axios.get('/endpoint/for/stuff/').then(result => {
doTransformations(result, this.fetchedData) // not exactly what happens, but I think this is unneeded to solve my problem. mostly there to illustrate how this all fits together.
window.Bus.$emit('stuffFetched', this.selectedData)
})
},
}
})
So window.Bus is a Vue instance which is just posing as a global event handler. Within my getStuff-method, the event never gets emitted, and I have no idea why. I've console logged everywhere in order to figure out if the bus is just not initialized (it is, because I have tons of components which works perfectly with it). The event is just never emitted. I've tried wrapping the emitting in $nextTick but that doesn't work either (I also tried doing this directly in the mounted-lifecycle method, because the computed property updates in Vue devtools like it should and contains all the right stuff).
I'm really at a loss of what to do here, everything seems to work like it should, but the event is not even registered in Vue devtools.
The reason I need to fire this event is in order to do some price calculations for another component which exists outside of the scope of this child and it's parent. Just emitting in the child scope (this.$emit('dataChanged')) doesn't emit an event either using this approach.
Anyone have an idea of what my ******* brain is doing to me here?
Did you try to use async await?
That i'll probably make the timeout job, something like:
async mounted() {
await this.getStuff()
window.Bus.$on('localStorageRefreshed', this.getStuff)
}
and then do that on your getStuff too:
async getStuff() {
const doTransformations = function (res, existing) {
// blabla
}
await axios.get('/endpoint/for/stuff/').then(result => {
doTransformations(result, this.fetchedData) // not exactly what happens,
but I think this is unneeded to solve my problem. mostly there to
illustrate how this all fits together.
window.Bus.$emit('stuffFetched', this.selectedData)
})
}

Vue.js/Vuex: Calling getter vs Accessing state value directly on create lifecycle hook

I'm using the created lifecycle hook in vue.js to load data from my store to the data for a vue component. I noticed that this.selectedType = store.state.selectedType successfully loads the data from the store. However, if I use the getter to load from the store (i.e. this.selectedType = store.getters.getType()), I get the following error:
Error in created hook: "TypeError: Cannot read property 'selectedType' of undefined"
I don't understand why it is saying that selectedType is undefined because selectedType has the value "Item" in the store and is correctly loaded on create if I use this.selectedType = store.state.selectedType.
The getter is defined as such:
getters: {
getSelectedType: state => {
return state.selectedType
}
}
And the state is defined as:
state: {
selectedType: "Item"
}
Could someone please explain why this occurs? I'm hunch is that there is something about the lifecycles that I don't fully understand that is leading to this confusion.
You are not supposed to call getters. Just like computed properties, you instead write it like you are reading a variable. In the background the function you defined in the Vuex store is called with the state, getters (and possibly rootState and rootGetters) and returns some value.
Beside that, it is usually an anti-pattern to use a lifecycle hook to initialise any variable. Local 'component' variables can be initialised in the data property of the component, while things like the vuex state usually end up in a computed property.
The last thing I want to point out is that, if you have correctly added the store to your Vue application, you can access the store in any component with this.$store. To use getters in your application, you can use the mapGetters helper to map getters to component properties. I would recommend using something like this:
import { mapGetters } from 'vuex';
export default {
// Omitted some things here
computed: {
...mapGetters({
selectedType: 'getSelectedType'
})
},
methods: {
doSomething () {
console.log(this.selectedType);
}
}
}
Which is functionally equivalent to:
computed: {
selectedType () {
return this.$store.getters.getSelectedType;
}
}

How to rework an Angular1 App to a VueJs app

So I'll get right to it :
I'm trying to remake an existing app that used Angular 1 , instead using VueJS 2.
Being unfamiliar to Angular1 I find it challenging to decide on a few things :
1.What are factories(probably services), where do I place/write manage them?
2.Okay I know angular is big on controllers, but I cannot seem to understand if I were to use Vue , what's the alternative to the controller, and where to hold the code.
So, basically what I've gotten so far is to use VueX for state management and I have moved some services there, however - I can't figure out for example if a certain service #requires 'NameOfOtherService' , does it mean it imports it like in NodeJS const NameOfOtherService = require ('insert/path/here'); ?
Basically the app gets data from an API and php scripts, for example :
In the angular 1 version in my appsettings, which is using an AppConfig module I have a pathFromScript(script) => {} // etc.
My question is , how do I manage EVERYTHING that's going on within one app like that translated to Vue?
Thank you in advance I know it's entirely a shot in the dark here.
Yours truly ,
As for Vue.Js, your angular controllers are methods. If you need to get some data from an API, you can use a method to call a Vuex action and either return the value back to the method or add it to your state, so it's available everywhere. This will look like the example below.
I guess your factories / services are managing API calls. I would place that inside Vuex with axios to make API calls.
Methods is the same as controllers in Angular. You create methods on the specific page. If you need a method to be resused multiple places, you can use mixins. Mixins extends the specific page by importing it. You can read about it here: https://v2.vuejs.org/v2/guide/mixins.html
page.vue
{{data}}
export default {
import { mapActions, mapGetters } from 'vuex'
...
computed: {
...mapGetters({
data: 'getData'
})
},
methods: {
...mapActions({
getServerData: 'getDataFromServer'
})
},
created () {
this.getServerData()
}
}
store.js (vuex)
import axios from 'axios'
state: {
data: null
},
getters: {
getData: state => {
return state.data
}
},
mutations: {
setDataFromServer: (state, payload) => {
state.data = payload
}
},
actions: {
getDataFromServer: ({ commit }) => {
return axios.get('https://website.com/api/data').then((response) => {
commit('setDataFromServer', response)
})
}
}
In this example the created is calling the vuex action to get the data from the server. When the server returns the value, it calls a mutation which sets the response in a state (data). The mapGetters which calls the getters in vuex, returns the data from state. It updates whenever the state changes.
Hope this makes sense

Categories