I want to pass the data from this one function, fetched from an API, into another. But the data does not want to carry over. Can you help? It keeps coming back as undefined. When I console log it in the other function, it just returns "object object" over and over.
Much appreciated.
app.getDisruptionData = () => {
$.ajax ({
url: "https://data.edmonton.ca/resource/5yvt-mcye.json",
method:"GET",
dataType: "json",
data: {
"$limit" : 500000,
"$$app_token" : "J33yX1FYA0vwnOA36tGBFLd6l"
},
complete: function(data) {
setTimeout(app.getDisruptionData, 300000);
$serviceDisruptions.empty();
const htmlToAppend =
`<li class="stationGrid">
<div>
ROUTE
</div>
<div>
DESCRIPTION
</div>
<div>
DISRUPTION START
</div>
<div>
EXPECTED END TIME
</div>
</li>`
$serviceDisruptions.append(htmlToAppend);
}
}).then((response) => {
console.log(response)
app.sortDisruptions(response)
// response.forEach ( (disruptionObject) => {
// const routeID = disruptionObject.route_ID;
// const routeName = disruptionObject.route_long_name;
// const disruptionStart = disruptionObject.start_dttm;
// })
});
}
app.sortDisruptions = (data) => {
console.log(`look at this data ` + data)
}
jQuery's promise-like handling can be a bit weird. Also, complete doesn't receive data, only the jqXhr object and a text status.
I would consolidate your complete and then callbacks into a single done()
$.getJSON("https://data.edmonton.ca/resource/5yvt-mcye.json", {
$limit: 500000,
$$app_token: "J33yX1FYA0vwnOA36tGBFLd6l",
}).done((data) => {
setTimeout(app.getDisruptionData, 300000);
// setting html is the same as empty + append
$serviceDisruptions.html(
`<li class="stationGrid">
<div>
ROUTE
</div>
<div>
DESCRIPTION
</div>
<div>
DISRUPTION START
</div>
<div>
EXPECTED END TIME
</div>
</li>`
);
app.sortDisruptions(data);
});
Also, try not to concatenate objects to strings in logging. A stringified object almost always displays as [object Object]
app.sortDisruptions = (data) => {
console.log("sortDisruptions", data);
// or just
console.log(data);
};
Related
I get a list of pokemons from a third party API. I want to add specific ones to a favorites list when I click the add button. but whenever I try to render the list of favourite pokemon i just get objects saying undefined.
Any help would be appreciated.
//Here i try to add the fetched object to my rest api//
function createPokemonCard(data) {
const pokeContainer = document.getElementById("poke-container");
const pokemonEl = document.createElement('div');
pokemonEl.classList.add('pokemon');
const pokeInnerHtml = `
<div class="img-container">
<img src="${data.sprites.front_default}">
</div>
<div class="pokeCard">
<h1 id="id">${data.id}</h1>
<h3 id="name">${data.name}</h3>
<h4 id="type">${data.types.map(type => type.type.name).join(", ")}</h4>
<button onclick="addFavourite()">Add</button>
</div>
`;
pokemonEl.innerHTML = pokeInnerHtml;
pokeContainer.appendChild(pokemonEl);
}
// i am trying to get the value//
async function addFavourite() {
let name = document.getElementById('name').value ;
let type = document.getElementById('type').value ;
let data = {
"name": name,
"type": type
}
await fetch('https://web2-course-project-api-somrad.herokuapp.com/api/pokemons', {
method: "POST",
mode: 'cors',
headers: {
'Content-type': 'application/json; charset=utf-8'
},
body: JSON.stringify(data)
});
}
// my back end post request//
pokemonRouter.route('/pokemons')
//POST YOUR FAVOURITE POKEMON
.post((req, res)=>{
const collection = db.collection("pokedex");
collection.insertOne(req.body).then(
() => {
res.status(200).json({
message: 'Added!'
});
}
).catch(
(error) => {
res.status(400).json({
error: error
});
});
})
this is my collection, the first three entries is added manually
The list probably prints "undefined" since some of the objects provided by the database don't have a name.
[
{"_id":"610d3316bf52a4e260126877","name":"Pikachu","type":"Electric"},
{"_id":"610e689defe3ad8654648ec3","name":"Charmander","type":"Fire"},
{"_id":"610e68acefe3ad8654648ec4","name":"Bulbasaur","type":"Grass"},
{"_id":"6118df7554b38705559eaacd"},
{"_id":"6118f0e493b20fefb0fd1689"},
{"_id":"6118f1e7128dd43ee68f8140"},
{"_id":"6118f2e8128dd43ee68f8141","name":"test","type":"grass"},
{"_id":"6118f57a128dd43ee68f8142"},
{"_id":"6118f5ca128dd43ee68f8143"},
{"_id":"6118f6a6128dd43ee68f8144"},
{"_id":"6118f6da128dd43ee68f8145"},
{"_id":"6118f6fd128dd43ee68f8146"},
{"_id":"6118f86f128dd43ee68f8147"},
{"_id":"6118f8e4128dd43ee68f8148"},
{"_id":"6118f924128dd43ee68f8149"},
{"_id":"6118fb34128dd43ee68f814a"}
]
This already includes my other suggestions.
I recommend that you filter out the invalid ones before rendering the rest.
// your list with the pokemon before you create the html elements
// this simply removes all pokemon from the list that don't have a name or type
const yourList = [];
const yourFilteredList = yourList.filter(item => {
if(item.name === undefined) return false;
if(item.type === undefined) return false;
return true;
})
Please also note that in mongoDB the Primary Key of the database is _id not id
<h1 id="id">${data._id}</h1>
This could also be an issue, since the type field of the API you provided is a string not a list as indicated. This may result in an Uncaught TypeError: something.join is not a function
<h4 id="type">${data.types.map(type => type.type.name).join(", ")}</h4>
I'm building my React Component and got this error when I tried to use a axios request and add that response data to an object. But when I tried to console.log the object, it showed me the value when I expanded the object, but still display null.
let id, title, channel;
let details = {
timestamp: null,
views: null,
likes: null,
description: null
};
if (active == undefined) {
active = {};
} else {
id = active.id;
title = active.title;
channel = active.channel;
let dataObject = API.get(`videos/${id}`, {
params: {
api_key: "ab4d78b8-e8ff-4392-bff8-21d1058cd420"
}
});
dataObject.then(result => {
let data = result.data;
details.timestamp = data.timestamp;
details.views = data.views;
details.likes = data.likes;
details.description = data.description;
});
}
return (
<div className="videoContent">
<h1 className="videoContent__title">{title}</h1>
<div className="videoContent__details">
<div className="videoContent__details--user">
<p className="channel">By {channel}</p>
<p className="timestamp">{dateFormat(details.timestamp)}</p>
</div>
<div className="videoContent__details--social">
<p className="views">
<img src={viewIcon} alt=""></img>
{/* {details.views} */}
</p>
<p className="likes">
<img src={likeIcon} alt=""></img>
{/* {props.details.likes.toLocaleString()} */}
</p>
</div>
</div>
<div className="videoContent__description">
{/* <p>{details.description}</p> */}
</div>
</div>
);
}
And this is what I got from the console:
{
timestamp: null,
views: null,
likes: null,
description: null
}
timestamp: 1537003624000
views: "2,043,765"
likes: "400,058"
description: "Luxury is something everyone deserves from time to time. Such
an indulgence can make a vacation a truly rejuvenating experience. This video will
focus a lot on helping the first time or inexperienced traveler head out prepared and
confident in themselves."
__proto__: Object
Edit: My axios call:
import axios from "axios";
export default axios.create({
baseURL: "https://project-2-api.herokuapp.com/",
responseType: "json"
});
JavaScript is always synchronous and single-threaded. If you're executing a JavaScript block of code on a page then no other JavaScript on that page will currently be executed.
JavaScript is only asynchronous in the sense that it can make, for example, Ajax calls. The Ajax call will stop executing and other code will be able to execute until the call returns (successfully or otherwise), at which point the callback will run synchronously. No other code will be running at this point. It won't interrupt any other code that's currently running.
to solve your problem you should use promises or Async/await,try to modify your code like below:
let dataObject = () => {
return new Promise((resolve, reject) => {
API.get(`videos/${id}`, {
params:{
api_key: "ab4d78b8-e8ff-4392-bff8-21d1058cd420"
}
}, function(error, response, data){
if (error) reject(error);
let content = JSON.parse(data);
let result = content.value;
resolve(result);
})
}
);
};
dataObject(url)
.then(result => {
let data = result.data;
details.timestamp = data.timestamp;
details.views = data.views;
details.likes = data.likes;
details.description = data.description;
return (<div>...</div>)
})
.catch(err => {
console.(error)
});
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 am updating my original vue project and am getting an error w/data object sports_feeds_boxscores_*. The site has three tabs to pull down scores for the three major leagues. I am adding the player stats for each game now. I first did baseball and all worked fine. Now I am doing football and the problem arises. I have three objects setup for the stats for each league. The nfl also contains an object with the three days of the week they play. What is happening is the stats for Sunday get pulled down ok but then Thursday's stats which should only be one game instead has all sunday's games plus the one thursday game. And then Monday has both Sunday & Thursdays results in it besides Mondays. I have made all the components separate as well as three separate data objects for the component props. And if I first click the nfl tab and then go to the mlb tab all results from nfl data object are in sports_feeds_boxscores_mlb. I setup a site here to better understand whats going on in using Vue.js devtools. Here is the pertinent code:
index.html:
<component
v-if="currentTabComponent === 'tab-mlb'"
v-bind:is="currentTabComponent"
v-bind:props_league_data="sports_feeds_data"
v-bind:props_league_standings="standings"
v-bind:props_baseball_playoffs="baseball_playoffs"
v-bind:props_end_of_season="end_of_season[this.currentTab.toLowerCase()]"
v-bind:props_box_game_scores_mlb="sports_feeds_boxscores_mlb"
class="tab"
>
</component>
<component
v-if="currentTabComponent === 'tab-nfl'"
v-bind:is="currentTabComponent"
v-bind:props_league_data="sports_feeds_data"
v-bind:props_league_data_nfl="nfl_feeds"
v-bind:props_league_standings="standings"
v-bind:props_nfl_playoffs="nfl_playoffs"
v-bind:props_end_of_season="end_of_season[this.currentTab.toLowerCase()]"
v-bind:props_box_game_scores_nfl="sports_feeds_boxscores_nfl"
class="tab"
>
</component>
vue.js:
data() {
return {
sports_feeds_boxscores_mlb: null,
sports_feeds_boxscores_nfl: {
sun: null,
mon: null,
thurs: null
},
sports_feeds_boxscores_nba: null,
etc
/* Component Code */
// First let's get the Game and BoxScores Data
const nflScores = async () => {
this.nfl_feeds.sunday_data = await getScores(
nflDate.sundayDate,
config
);
this.nfl_feeds.thurs_data = await getScores(
nflDate.thursdayDate,
config
);
this.nfl_feeds.mon_data = await getScores(nflDate.mondayDate, config);
// Next we need the gameid's to retrieve the game boxscores for each day
this.nfl_feeds.sunday_data.forEach(function(item, index) {
if (item.isCompleted === "true") {
nflGameIDs.sunday[index] = item.game.ID;
}
});
this.nfl_feeds.thurs_data.forEach(function(item, index) {
if (item.isCompleted === "true") {
nflGameIDs.thursday[index] = item.game.ID;
}
});
this.nfl_feeds.mon_data.forEach(function(item, index) {
if (item.isCompleted === "true") {
nflGameIDs.monday[index] = item.game.ID;
}
});
// Check if boxscores have been retrieved on previous tab click for each day
// if not retrieve the boxscores
this.sports_feeds_boxscores_nfl.sun =
this.sports_feeds_boxscores_nfl.sun ||
(await getBoxScores(nflGameIDs.sunday, url, params));
this.sports_feeds_boxscores_nfl.thurs =
(await getBoxScores(nflGameIDs.thursday, url, params));
this.sports_feeds_boxscores_nfl.mon =
this.sports_feeds_boxscores_nfl.mon ||
(await getBoxScores(nflGameIDs.monday, url, params));
}; /* End nflScores Async function */
getBoxScores.js:
try {
const getBoxScores = async (gameIDs, myUrl, params) => {
gameIDs.forEach(function(item) {
promises.push(
axios({
method: "get",
headers: {
Authorization:
"Basic &&*&&^&&=="
},
url: myUrl + item,
params: params
})
);
});
// axios.all returns a single Promise that resolves when all of the promises passed
// as an iterable have resolved. This single promise, when resolved, is passed to the
// "then" and into the "values" parameter.
await axios.all(promises).then(function(values) {
boxScores = values;
});
console.log(`boxScores is ${boxScores.length}`)
return boxScores;
};
module.exports = getBoxScores;
} catch (err) {
console.log(err);
}
I have split up all the sports_feeds_boxscores objects and at a loss as to why they are sharing state??? Sorry for verbosity of the question but it is somewhat complex. That is why I provided the site where you can see devtools that for instance this.sports_feeds_boxscores_nfl.thurs has 14 elements instead of one after the call to API. And if mlb tab is clicked after nfl tab then mlb results include the nfl results. I would really appreciate help in figuring this out. Thanks in advance...
Update:
I have added getBoxScores.js cause it seems as if I am returning the extra stats from this call.
This was my bad. I didnt realize I had created a closure in getBoxScores.js:
let boxScores = [];
let promises = [];
try {
const getBoxScores = async (gameIDs, myUrl, params) => {
gameIDs.forEach(function(item) {
promises.push(
axios({
method: "get",
headers: {
Authorization:
"Basic &&^^&^&&^FGG="
},
url: myUrl + item,
params: params
})
);
});
Moving declarations inside async function quickly solved trouble. URRRRGGGHHH!!!
I am working on VueJs, And In my template section .. I defined a condition to check if the image URL exists or not.
template:
<div v-for="(sub, key) in Work.sub" :key="sub.id" >
<span v-if="Image('https://XXXX.blob.core.windows.net/XXXX/XXXXX-' + key +'.png')" >
<b-img :src="'https://XXXX.blob.core.windows.net/XXXX/XXXXX-' + key +'.png'" />
</span>
<span v-else>
<b-img :src="default_avatar"/>
</span>
</div>
In script:
Image: function(img_url)
{
return axios({
method: "GET",
timeout: 3000,
headers: {
.......................
},
url: img_url
})
.then( function(response){
this.ifImageExist = true;
return this.ifImageExist;
})
.catch( function(error){
this.ifImageExist = false;
return this.ifImageExist;
})
},
For default_avatar it is aleady definded in the data section and no problem with it.
My problem is when the Image function checks if the image URL exists or not. If it exists it provides the image in the given URL, but if it does not exist, the image will be blank!
For example:
when I run the code, I result will be like this:
But I want the first image to filled by default image, not show does not exist icon!
How to solve this problem?
First of all, your function Image() doesn't return a Boolean it returns a Promise (from axios),
so v-if evaluates as true;
To get v-if working with a API call (Axios GET), the simplest way is to turn that Image method in a Async Method
Image: async function(img_url)
{
return axios({
method: "GET",
timeout: 3000,
headers: {
.......................
},
url: img_url
})
.then( function(response){
this.ifImageExist = true;
return this.ifImageExist;
})
.catch( function(error){
this.ifImageExist = false;
return this.ifImageExist;
})
},
If you have all build configured right to work with async function, the method will wait for the response and evaluate to the Boolean which v-if is expecting.
Alternative solution: It looks like the only diff between the v-if and v-else is b-img's src, so an alternative would be to move the logic into JavaScript, mapping Work.sub into an array of image URLs that default to default_avatar only if the URL doesn't resolve.
So your template would be:
<template>
<div v-for="(img, index) in images" :key="index">
<b-img :src="img" />
</div>
</template
You'd add a data property to hold the image URLs:
data() {
return {
images: []
}
}
And a watcher on Work.sub, which sets this.images:
watch: {
'Work.sub': {
immediate: true,
async handler(sub) {
// For each image URL, attempt to fetch the image, and if it returns
// data (image exists), collect the URL. Otherwise, default.
this.images = await Promise.all(Object.keys(sub).map(async (key) => {
const img = sub[key];
if (!img) return this.default_avatar;
const url = `//placekitten.com/${img}`;
const { data } = await axios.get(url);
return data ? url : this.default_avatar;
}));
}
}
}
demo