Hi I'm using Vuejs to get some pokemon data. So I figured out how to retrieve all the pokemon name and their api urls to get more information about them. The issue is I don't know how to take those URLs and access each pokemon's specific data. I tried to increment a variable and concatenate it to the URL to get their data but it didn't work. I also tried to access the data from the api call I already but that also didn't work.
<template>
<div>
<h2>{{subtitle}}</h2>
<div v-for="pokemon in basicInfo" v-bind:key="pokemon.name">
<span>{{ pokemon.name}}</span>
</div>
<!-- Nothing is produced, and I dont get I an error -->
<div v-for="pokemon2 in advInfo" v-bind:key="pokemon2.index">
<span>{{pokemon2}}</span>
</div>
<script>
import axios from "axios";
export default {
data() {
return {
subtitle: "First 150 pokemon",
basicInfo: [],
advInfo:[],
i:0
};
},
methods: {
// trying to increment i
getNext: function(){
this.i=i++;
}
},
mounted() {
axios
// this gets a list of the first 20 pokemon. I can get the pokemon's name and their url
.get("https://pokeapi.co/api/v2/pokemon/")
.then(response => {
this.basicInfo = response.data.results;
});
// here I'm trying to access more specific data on each pokemon by concatenating a number to the url
axios
.get("https://pokeapi.co/api/v2/pokemon/5")
.then(response => {
this.advInfo= response.data.results;
});
}
};
</script>
<style scoped>
</style>
It looks like ".../api/v2/pokemon/" produces an object with a results array, and those results contain uri's like ".../api/v2/pokemon/(some id)"
The way to combine them is as follows:
axios.get("https://pokeapi.co/api/v2/pokemon/").then(response => {
this.basicInfo = response
let promises = this.basicInfo.map(result => {
return axios.get(result.url)
})
Promise.all(promises).then(response => {
this.advInfo = response
})
});
Now advInfo will be an array, like you expect so you can render it with v-for....
<div v-for="(pokemon2, i) in advInfo" :key="i">
<pre>{{pokemon2}}</pre>
</div>
Related
I'm new in VueJs, actually coming from react, the problem I'm having is that when i fetch the data with an axios.get the console.log shows a succesfull response. But when I to iterate through the Array with v-for it renders nothing.
It's quite important so it be super appreciated if you can tell me what im doing wrong or you have a suggestion.
heres the code:
<SignUpForm />
<h1>Countries List</h1 >
<div v-for="country in countries" :key="country.name" >
<p>{{country.name}}</p>
</div>
</template>
<script>
import SignUpForm from "#/components/SignUpForm.vue";
import axios from 'axios'
export default{
name: 'SignUpView',
components:{
SignUpForm
},
data(){
return{
countries: []
}
},
async mounted() {
const {data} = await axios.get('https://gist.githubusercontent.com/keeguon/2310008/raw/bdc2ce1c1e3f28f9cab5b4393c7549f38361be4e/countries.json')
console.info(data)
this.countries = data
}
}
</script>
You can see that what I'm actuallt doing is the axios.get in the mounted(){} f(x), later I = the data to my empty Array declared in data(){}. => So I can iterate trough the json response with a v-for in a div.
Heres the picture so you can see:
Observations :
countries list is a string and v-for is work if the input data is an array.
No. of objects in an array is 243 but this 9251 is a count of characters in a string.
Also, if we will try to convert that into a JSON object via JSON.parse() getting SyntaxError: Unexpected token n in JSON at position 6.
new Vue({
el: "#app",
data: {
countries: []
},
methods: {
getData() {
var vm = this;
axios.get("https://gist.githubusercontent.com/keeguon/2310008/raw/bdc2ce1c1e3f28f9cab5b4393c7549f38361be4e/countries.json")
.then(function(response) {
console.log(typeof response.data); <---- string
console.log(response.data.length); <---- actual objects in an array is 243 but this 9251 is a count of characters in a string.
console.log(JSON.parse(response.data)); <---- SyntaxError: Unexpected token n in JSON at position 6
})
.catch(function(error) {
alert(error);
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<div id="app">
<button #click="getData">Click to check Api response</button>
</div>
Consider this:
An API loads a manifest of image metadata. The images have an ID, and with another API call returns a base64 image from the DB. The model for the manifest is attachmentRecord and the ID is simply a field.
I would rather not preload these large strings into an array (that would work).
so I have this (which lazy loads on any manifest change):
<div v-for="(attachment, index) in attachmentRecord" :key="index">
<img :src="fetchImage(attachment.id)" />
</div>
fetchimage() is a wrapper for an axios function which returns back from a promise. (writing this from memory):
this.axios({
method: "get",
url: url,
}).then(res => res.data)
.catch(() => {
alert("Unable to load raw attachment from this task and ID");
});
}
Now, the network calls go thru fine, the ID passes in correctly, I can see the base 64data, but they don't seem to make it to wrapper function or the src attribute. It always comes up blank. I tried wrapping it in another promise,only to get a promise back to the src attribute. What would be a best practice for this situation in Vue?
Ok, so far I made these changes with Constantin's help:
I tried to strip it down without a helper function:
Vue template Code:
<div v-for="(attachment, index) in attachmentRecord" :key="index">
<img :src="getAttachmentFromTask(attachment.id)" />
base method:
async getAttachmentFromTask(attachmentID) {
if (!attachmentID) alert("Unknown Attachment!");
let sendBack = "";
let url = "/server/..."
await this.axios({
method: "get",
url: url
})
.then(res => {
sendBack = res.data;
})
.catch(() => {
alert("Unable to load raw attachment from this task and ID");
});
// >>>>>>>>>alerts base64 correctly; Vue loads [object Promise] in img
alert(sendBack);
return sendBack;
}
It turns out that Vue doesn't handle async / await as well as I thought. Therefore, you have to save the image data to each attachment in attachmentRecord. This getAttachmentFromTask method now handles this when accessed the first time and populates a data property for the corresponding attachment object. On successive calls, that property is returned if it is already populated. Note the usage of Vue.set() because the property is not available in the initial data, but we want it to be reactive. You can even set up a fallback image like a loader, see the shortly flickering SO logo without text before the larger logo appears:
new Vue({
el: '#app',
data: {
attachmentRecord: [{
id: 1
}]
},
methods: {
getAttachmentFromTask(attachmentIndex, attachmentID) {
let record = this.attachmentRecord[attachmentIndex];
if (!record.data) {
Vue.set(record, 'data', null);
axios.get('https://kunden.48design.de/stackoverflow/image-base64-api-mockup.json').then((result) => {
Vue.set(record, 'data', result.data);
});
}
return this.attachmentRecord[attachmentIndex].data;
}
}
});
img {
max-width: 100vw;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<div v-for="(attachment, index) in attachmentRecord" :key="index">
<img :src="getAttachmentFromTask(index, attachment.id) || 'https://cdn.sstatic.net/Sites/stackoverflow/img/apple-touch-icon.png'" />
</div>
</div>
old answer: (Unfortunately doesn't work that way with Vue currently)
Axios requests are asynchronous by default. So the function doesn't wait for then() to return the value. You could add the async keyword before your fetchImage function name and add the await keyword before this.axios. Then make the then callback assign the return value to a variable in the fetchImage function scope and have the function return it.
async fetchImage() {
let returnValue;
await this.axios({
method: "get",
url: url,
}).then(res => { returnValue = res.data; })
.catch(() => {
alert("Unable to load raw attachment from this task and ID");
});
return returnValue;
}
I'm trying to accomplish the following but I don't even know if it is even possible with Vue as I'm struggling to get the desired result:
I have an endpoint for an API which returns many objects within an array.
I am successfully rendering the data within my Vue application but I wanted to know if it is possible for Vue to "track" when the array has been updated with more objects and then render those in the view.
I am using setInterval to perform a GET request every 10 minutes and the new data is going into the object within my data() correctly but the changes are not reflected within the view.
At the moment I am changing a boolean from true to false at the beginning and end respectively so that the view is rendered again with v-if.
My goal is to create a simple Twitter feed app that performs a GET request every 10 minutes, collects the tweets, puts them into my Vue instance and show them in the view without having to reload the page/re-render the component. Like an automatic Twitter feed that just constantly loads new tweets every 10 minutes.
Is this even possible? I've tried using the Vue.set() method but that hasn't made any difference.
If it's not possible, what would be the best way to implement something similar?
Here is my code:
JavaScript:
new Vue({
el: '#app',
data: {
items: [],
},
created() {
this.load();
setInterval(() => this.load(), 5000);
},
methods: {
load() {
axios.get('https://reqres.in/api/users?page=2')
.then(response => {
this.items = response.data.data;
});
}
}
});
HTML
<div id="app">
<p v-for="item in items">
{{ item.first_name }}
</p>
</div>
CodePen: https://codepen.io/tomhartley97/pen/VwZpZNG
In the above code, if the array is updated by the GET request, the chances are not reflected within the view?
Yes it is possible. The way you need to set new reactive properties in your Vue instance is the following:
For Object properties: Vue.set(this.baseObject, key, value)
The baseObject cannot be a Vue instance or the base data() object, so you will have to declare a container property.
For Array entries use native array methods: e.g. Array.prototype.push().
Using Vue.set(array, arrayIndex, newArrayElement) does not work
Hence, your solution might look something line that:
<script>
export default {
data() {
return {
response: [],
};
},
mounted() {
setInterval = (() => this.getData), 600000);
}
methods: {
async getData() {
const res = await request();
const resLength = res.data.length;
for (let i = 0; i < resLength; i++) {
// check if entry is already in array
const entryExists = this.response.some((entry) => {
return entry.id === res.data[i].id
})
if (!entryExists) {
// this will make the array entries responsive, but not nested Objects
this.response.push(res.data[i]);
// to create nested responsive Objects you will have to set them explicitly
// e.g. Vue.set(this.response[this.response.indexOf(res.data[i])], nestedObjectKey, res.data[i].nestedObject)
}
}
}
}
};
</script>
Well, I view the codepen, I known why your view do not get update: the api response always return the same array!
Try to return different data.
The api returns an array, so the data defines
data() {
return {
array: [] // array that api returns
}
}
The template may look like this
<div v-for="item in array">
</div>
And the update methods
update() {
setInterval(async () => {
let resp = await api()
this.array = resp.data.concat(this.array)
}, TEN_MINUTES)
}
I'm just starting out with React and ES6 so apologies if this is a bit of a simple one.
I'm currently playing round with the FoursquareAPI. I'm using ES6 fetch to return a series of objects (they're actually different venues in different parts of the world) which are then mapped and returned and stored in the application's state. This works fine and returns what I want:
// method to call api
getVenues: (searchTerm) => {
const fetchVenuesURL = `${urlExplore}${searchTerm}&limit=10&client_id=${clientId}&client_secret=${clientSecret}&v=20180602`;
return fetch(fetchVenuesURL).then( response => {
return response.json();
}).then( jsonResponse => {
if (jsonResponse.response.groups[0].items) {
return jsonResponse.response.groups[0].items.map(item => (
// populate venues
{
id: item.venue.id,
name: item.venue.name,
address : item.venue.location.address,
city : item.venue.location.city,
country : item.venue.location.country,
icon : item.venue.categories[0].icon
}
));
} else {
return [];
}
});
// method in App.js to setState
search(term){
Foursquare.getVenues(term).then(foursquareResponse => {
this.setState({venues: foursquareResponse});
});
}
The problem arises when I need to fetch photographs associated with each of the 'venues' returned by the original fetch. These come from a different endpoint. I'm not sure what the best approach is.
One way would be to have two separate api calling methods and then somehow populate an empty photos field of the first with the photos from the second back in App.js but that seems clunky.
My instinct is to somehow nest the Api calls but I'm uncertain about how to go about this. I'm hoping to do something along the lines of somehow applying a method to each iteration of the first mapped object, something along the lines of but not sure how to link them together so that the second goes into the photo property of the first:
{
id: item.venue.id,
name: item.venue.name,
address : item.venue.location.address,
city : item.venue.location.city,
country : item.venue.location.country,
icon : item.venue.categories[0].icon
photos : []
}
const fetchPhotosURL = `${urlPhotos}${venueId}/photos?limit=10&client_id=${clientId}&client_secret=${clientSecret}&v=20180602`;
return fetch(fetchPhotosURL).then( response => {
return response.json();
}).then( jsonResponse => {
if (jsonResponse.response.photos.items) {
console.log(jsonResponse.response.photos.items[0].venue)
return jsonResponse.response.photos.items.map(item => (
{
id : item.id,
created: item.createdAt,
prefix: item.prefix,
suffix: item.suffix,
width: item.width,
height: item.height,
venue: item.venue
}
));
} else {
return [];
}
})
Can anyone point me in the right direction with this. I'm guessing that it's one of those things that isn't that hard once you've done it once but I'm finding it difficult.
Thanks in advance.
nI try to fetch some userdata from a mongodb (json format) using axios.get within an vue.js application. After this, i want to visualize this data using a iteration through all user-objects within the users array. But my problem is, that every single character is a single object in this array. What i want is, that every single user-json file is one object in this array. If i have three user-objects user.length should be three.
Here the code for the axios call:
axios
.get(RL + "/users")
.then(response => {
this.users = response.data
})
.catch(e => {
this.errors.push(e);
console.log("Errors in Users: " + e);
});
And with this snipped i want to iterate through all objects, displaying the username. But user.name is always only a single character and not the whole name.
<div v-if="users">
<li v-for="user in users">
{{user.name}}
</li>
</div>
Without any plugin.
var app = new Vue({
el: '#app',
// YOUR DATA HERE
data: {
filterResult: [] // THIS IS YOUR JSON FILE
},
// READY FUNCTION HERE
created: function() {
this.filterResult = this.dataResult();
},
// YOU FUNCTION HERE
methods: {
// CALLING API
dataResult: function() {
$.getJSON('/api/feature/stores/list', function(json) {
app.results = json;
app.filterResult = json;
});
},
}
});