Fetch Data using Vue http based on Router params - javascript

I have searched around for an answer to this and also followed the example on the vue router documentation but am still having issues. I am trying to do an http call on initial load of a component and then also watch the router params and update the 'get' call from vue-resource.
My vue component js looks like this...
export default {
name: 'city',
components: {
Entry
},
data () {
return {
city: null
}
},
mounted() {
this.fetchData();
},
watch: {
'$route': 'fetchData'
},
methods: {
fetchData() {
const cityName = this.$route.params.name;
this.$http.get('http://localhost:3000/cities?short=' + cityName).then(function(response){
this.city = response.data;
}, function(error){
alert(error.statusText);
});
console.log(cityName)
}
}
}
I have logged out the 'cityName' in my fetchData method and it always returns the right name, but when I append that 'cityName' to the http get call it is not returning the proper data. On initial load, this.city remains null and then each time I update the route, the data returns with the previous city selected instead of the new updated city in the route. I have tried Vue's created property in place of mounted and the same thing happens. Any ideas?

Try changing your fetchData method to the following:
fetchData() {
const cityName = this.$route.params.name;
this.$http.get('http://localhost:3000/cities?short=' + cityName).then((response) => {
this.city = response.data;
}, (error) => {
alert(error.statusText);
});
console.log(cityName)
}
The => function notation keeps this in context of the component.

Related

Call API in another page in nuxtjs

I would like to put my calls to my API in a separate page and not in the template page of my app. So, I create a file "customersAPI.js" and I put this code :
export function findAllCustomers () {
axios.get('http://127.0.0.1:8000/api/customers')
.then((reponse)=>{
console.log(reponse.data['hydra:member'])
return reponse.data['hydra:member']
}).catch(err=>console.log(err))
}
So I try to retrieve my data in my template page and put these data in data but It does not work because of the asynchronous thing of api call and because I don't know how to pass the data...
I do this in my template page :
data() {
return {
customer: [],
}
},
mounted() {
this.getAllCustomers();
},
getAllCustomers() {
this.customer = findAllCustomers();
}
I know it is not the good way to do this but I don't know how to do... So I need clarification about that. And, every time I go into the documentation, there are no examples with an API call outside of the part where there is the page template. Is it a good practice to want to put the api call apart? And in general calls to functions so that the code is not too long?
Thanks for help
In your case I advise you to try add async in mounted or in func.
async mounted() {
this.customers = await this.findAllCustomers();
},
------
methods: {
async getAllCustomers(){
this.customer = await findAllCustomers();
}
}
But better practice to fetch information from store:
COMPONENT
<script>
import {mapActions} from 'vuex'
export default {
data() {
return {
customer: [],
}
},
mounted() {
this.customer = this.fetchAll();//better to get via getters
},
methods() {
...mapActions('customers', ['fetchAll']),
//OR
// fetchAllCustomers(){
// this.$store.dispath('customers/fetchAll')
// }
}
}
</script>
STORE
// async action that put all customers in store
const fetchAll = async ({ commit }) => {
commit(types.SET_ERROR, '')
commit(types.TOGGLE_LOADING, true)
try {
const { data} = await customerAPI.findAll(namespace)
commit(types.SET_ALLIDS, data['hydra:member'])
commit(types.TOGGLE_LOADING, false)
return data['hydra:member']
} catch (error) {
commit(types.TOGGLE_LOADING, false)
commit(types.SET_ERROR, error)
}
},
API
// func that receive promise
export function findAll () {
return axios.get('http://127.0.0.1:8000/api/customers')
}
Please read about vuex
https://vuex.vuejs.org/guide/actions.html

Data fetched from API correctly; app gives TypeError with 'undefined' variable while processing with 'computed'

I ran into troubles trying to process data fetched from remote API.
The app is running VueJS with Vuetify, data is formatted with Vuetify's data table component.
This is my code:
export default {
data () {
return {
headers: [
{ text: 'City', value: 'city' },
{ text: '#Citizens', value: 'citizens' },
{ text: '#Schools', value: 'schools' },
{ text: 'Schools per Citizen', value: 'schoolsPerCitizen' },
(...)
API URL is defined as a variable on the root level of the app.
Then, there is this method launched when created() kicks in:
methods: {
loadData() {
axios.get(citiesApiUrl)
.then((response) => {
console.log(response.data) // data displayed correctly
return response.data
})
.catch(error => {console.error(error)})
}
},
created () {
this.loadData()
}
As you noticed in the comment, response.data does display desired values.
Problems start from this point:
computed: {
stats() {
return this.loadData().map(item => {
item.schoolsPerCitizen = (item.schools / item.citizens).toFixed(2)
return item
})
}
}
I get an error: TypeError: Cannot read property 'map' of undefined.
Any ideas what is wrong with my code?
Issues
When loadData is called in created, the axios promise is consumed but nothing happens with the returned data except it's logged and returned to the promise resolver.
When loadData is called in stats (computed), .map is chained off of the return value from loadData, but loadData has no return value.
Even if loadData returned the axios promise, that promise would have to be consumed in stats first before accessing the data (needs .then)
The design is flawed because the computed will make an identical API call every time it recalculates, which is likely unnecessary.
Also, the promise returned by stats wouldn't be resolved by the template render function anyway.
Fix
Create a variable for the loaded data (I'll call it mydata):
data() {
return {
// ...
mydata: []
}
}
Change loadData to:
loadData() {
axios.get(citiesApiUrl).then((response) => {
this.mydata = response.data // <--- Set the data to `mydata`
}).catch(error => {
console.error(error)
})
}
Change stats to:
stats() {
// This is also not designed properly, it's going to mutate `mydata`...
// You should study Vue and learn what the purpose for computeds are before using them
return this.mydata.map(item => { // <-- Once `mydata` is populated, this will recalculate
item.schoolsPerCitizen = (item.schools / item.citizens).toFixed(2)
return item
})
}
loadData does not return any value.
loadData() {
return axios.get(citiesApiUrl)
.then((response) => {
console.log(response.data) // data displayed correctly
return response.data
})
.catch(error => {console.error(error)})
}

Vuex Mutation running, but component not updating until manual commit in vue dev tools

I have a vue component that I can't get to update from a computed property that is populated from a service call.
Feed.vue
<template>
<div class="animated fadeIn">
<h1 v-if="!loading">Stats for {{ feed.name}}</h1>
<h2 v-if="loading">loading {{ feedID }}</h2>
</div>
</template>
<script>
export default {
data: () => {
return {
feedID: false
}
},
computed: {
feed(){
return this.$store.state.feed.currentFeed
},
loading(){
return this.$store.state.feed.status.loading;
}
},
created: function(){
this.feedID = this.$route.params.id;
var fid = this.$route.params.id;
const { dispatch } = this.$store;
dispatch('feed/getFeed', {fid});
}
}
</script>
That dispatches 'feed/getFeed' from the feed module...
feed.module.js
import { feedStatsService } from '../_services';
import { router } from '../_helpers';
export const feed = {
namespaced: true,
actions: {
getFeed({ dispatch, commit }, { fid }) {
commit('FeedRequest', {fid});
feedStatsService.getFeed(fid)
.then(
feed => {
commit('FeedSuccess', feed);
},
error => {
commit('FeedFailure', error);
dispatch('alert/error', error, { root: true });
}
)
}
},
mutations: {
FeedRequest(state, feed) {
state.status = {loading: true};
state.currentFeed = feed;
},
FeedSuccess(state, feed) {
state.currentFeed = feed;
state.status = {loading: false};
},
FeedFailure(state) {
state.status = {};
state.feed = null;
}
}
}
The feedStatsService.getFeed calls the service, which just runs a fetch and returns the results. Then commit('FeedSuccess', feed) gets called, which runs the mutation, which sets state.currentFeed=feed, and sets state.status.loading to false.
I can tell that it's stored, because the object shows up in the Vue dev tools. state.feed.currentFeed is the result from the service. But, my component doesn't change to reflect that. And there is a payload under mutations in the dev tool as well. When manually commit feed/feedSuccess in the dev tools, my component updates.
What am I missing here?
In the same way that component data properties need to be initialised, so too does your store's state. Vue cannot react to changes if it does not know about the initial data.
You appear to be missing something like...
state: {
status: { loading: true },
currentFeed: {}
}
Another option is to use Vue.set. See https://vuex.vuejs.org/guide/mutations.html#mutations-follow-vue-s-reactivity-rules...
Since a Vuex store's state is made reactive by Vue, when we mutate the state, Vue components observing the state will update automatically. This also means Vuex mutations are subject to the same reactivity caveats when working with plain Vue
Hey for all the people coming to this and not being able to find a solution. The following was what worked for me:
Declaring base state:
state: {
mainNavData: [],
}
Then I had my action which is calling the now fixed mutation:
actions : {
async fetchMainNavData({ commit }) {
var response = await axios.get();
commit('setMainNavData', response));
},
};
Now my mutation is calling this updateState() function which is key to it all
mutations = {
setMainNavData(state, navData) {
updateState(state, 'mainNavData', navData);
},
};
This is what the updateState function is doing which solved my issues.
const updateState = (state, key, value) => {
const newState = state;
newState[key] = value;
};
After adding updateState() my data reactively showed up in the frontend and I didn't have to manually commit the data in Vue tools anymore.
please note my store is in a different file, so its a little bit different.
Hope this helps others!
Sometimes updating property that are not directly in the state is the problem
{
directprop: "noProblem",
indirectParent: {
"test": 5 // this one has a problem but works if we clone the whole object indirectParent
}
}
but it is a temporary solution, it should help you to force update the state and discover what is the real problem.

Correct syntax for importing axios method in Vue js

I am trying to separate my axios calls from my main vue instance by importing them instead of calling them directly in the created hook.
I have this in a separate file called data.js
import axios from 'axios'
export default{
myData() {
return axios.get(`http://localhost:8080/data.json`)
.then(response => {
// JSON responses are automatically parsed.
return response.data;
})
.catch(e => {
return this.myErrors.push(e)
});
},
And in my vue instance I have the following:
import myDataApi from '#/api/data.js'
export default {
name: 'app',
components: {
myDataApi, // not sure if this is correct
},
data: function () {
return {
myInfo: '',
}
},
created() {
this.myInfo = myDataApi.myData();
console.log('this.myInfo= ', this.myInfo)
},
I am trying to populate myInfo with the json called by myData. This returns [object Promise] in Vue devtools and the as PromiseĀ {<pending>} in the console.
All the data I need is inside that PromiseĀ {<pending>} in an array called [[PromiseValue]]:Object so I know it is working, I just need to know the correct way implementing this.
I don't have a development environment enabled to test this at the moment, but I do notice that you are trying to assign a variable the moment that the component is initialized. This object is a promise, but you're not handling the promise after it is resolved inside the component where you have imported it.
I would recommend trying to handle the promise inside of the actual component, something like:
import myDataApi from '#/api/data.js'
export default {
name: 'app',
components: {
myDataApi, // not sure if this is correct
},
data: function () {
return {
myInfo: '',
}
},
created() {
myDataApi.myData()
.then((data) => {
this.myInfo = data
console.log('this.myInfo= ', this.myInfo);
});
.catch((e) => handleError) // however you want to handle it
},
Just to add to #LexJacobs answer. I omitted the parenthesis around data in .then() as seen below. Vue was squawking about data not being available even though it was. This solved that problem, although to be honest I don't know why.
myDataApi.myData()
.then(data => {
this.dataHasLoaded = true;
this.myInfo = data;
})
.catch(e => {
this.myErrors.push(e)
});

load json of data into vuex store and access in component

I am trying to load a JSON file of content into my vuejs app and accessing it in my components. I am able to load the json into the vuex store by creating an API:
import Vue from 'vue';
const Http = new Vue();
export function getData() {
return Http.$http.get('./app/assets/content/en_uk.json')
.then(response => Promise.resolve(response.data))
.catch(error => Promise.reject(error));
}
and an action
export const getSiteContent = ({commit}) => {
api.getData().then(data => {
commit('siteContent', data);
});
};
I run getSiteContent on created function of the main vue instance
export default new Vue({
el: '#root',
store,
router,
created() {
getSiteContent(store);
},
render: h => h('router-view')
});
using the vue debug tool in chrome i can see the store
export const state = {
isSearching: false,
searchQuery: '',
siteData: {},
filteredComponents: [],
hasResults: false,
activeComponent: null
};
gets updated with the siteData.
This is part of the json:
{
"global": {
"project_name": {
"text": "Project title"
},
"search": {
"form_placeholder": {
"text": "Search..."
},
"no_results": {
"text": "Sorry no results for '{0}' was found"
},
"search_text": {
"text": "You are searching for '{0}' and there are {1} found"
}
}
}
}
When I try and access
computed: {
...mapGetters(['siteData']),
mumbo () {
return this.siteData.global.project_name;
}
}
in my component like {{mumbo}} I get cannot read property of project_name of undefined.
I feel like this is a time issue as it doesn't fall over when I set it to return siteData.global
I'm not sure if I am doing something wrong or I am missing a connection to get this to work.
As you guessed the problem here is that Vue is trying to access the contents of siteData for that computed property while the data is still loading. Although siteData is a valid object initially, trying to access siteData.global.project_name fails because siteData has no field global when the data hasn't loaded yet. To prevent the error, you will have to include a check like this:
mumbo () {
return this.siteData.global ? this.siteData.global.project_name : 'Loading...';
}
To illustrate the solution, here's a simple JSFiddle based on your code.

Categories

Resources