I'm new in learning of building apps with API and node.js and stuck with a problem.
UI is updated from city.value input and when I fetch API, search query return a random img, however when I hardcode value in fetch url the image is correct.
Help please, what I'm missing/not seeing?
const city = document.getElementById('city').value;
const messageBody = document.getElementById('content');
const postImage = new Image();
fetch(`https://api.unsplash.com/search/photos?query=${city}&client_id=${client_id}`)
.then(res => res.json())
.then(
result => {
const imgurl = result.results.map(
hit => hit.urls.regular
);
postImage.src = imgurl[0];
console.log(imgurl[0])
},
error => {
console.log(error);
}
);
const updateUI = (items) => {
let html = items.map((item => {
return `
<p>
${item.content}
<img src='${postImage.src}' width='400'>
</p>`;
})).join(" ");
messageBody.innerHTML = html;
}
Related
I am working on a project using the Poke API.
My problem is when trying to filter the data from a search input,
I get the error: Cannot read properties of undefined (reading 'map')
When someone types in the search bar, I'm using filter to return a new array that matches either a name, id number, or type. I can see that a new array is returned, but the program breaks when trying to display the filtered data on the page.
It looks like the error occurs because the filter cannot iterate over a promise used to map specific data from the original array. But I'm not sure how to move forward.
I'm grateful to anyone who can point me in the right direction. Thanks for your help.
JS
const searchBar = document.getElementById('searchBar');
const pokemonData = [];
const getPokemonData = async () => {
for (let i = 1; i <= 151; i++) {
const url = `https://pokeapi.co/api/v2/pokemon/${i}`;
const res = await fetch(url);
const data = await res.json();
pokemonData.push(data);
}
Promise.all(pokemonData).then( (results) => {
const pokemon = results.map( (data) => ({
name: data.name,
id: data.id,
image: data.sprites['front_shiny'],
type: data.types.map((type) => type.type.name)
}));
//console.log(pokemon);
displayPokemon(pokemon);
});
}
const displayPokemon = (pokemon) => {
//console.log(pokemon);
const pokeDexContainer = document.querySelector('.pokedex');
const generateHtml = (pokemon).map( (mon) => {
return `
<li class="poke-card">
<image class="poke-image" src="${mon.image}" alt="${mon.name}"/>
${
( ids => {
if (ids < 10) {
return `<h2 class="poke-id">00${ids}</h2>`
}
if (ids >= 10 && ids < 100) {
return `<h2 class="poke-id">0${ids}</h2>`
}
if (ids >= 100) {
return `<h2 class="poke-id">${ids}</h2>`
}
})(mon.id)
}
<h1 class="poke-name">${mon.name}</h1>
//error message points here
//Cannot read properties of undefined (reading 'map')
${mon.type.map( (types) => {
return `<span class="poke-type ${types}">${types}</span>`
} ).join('')}
</li>
`
}).join('');
pokeDexContainer.innerHTML = generateHtml;
}
getPokemonData();
searchBar.addEventListener('keyup', (event) => {
//console.log(event.target.value);
const searchString = event.target.value.toLowerCase();
const searchNumber = event.target.value;
const filteredPokemon = pokemonData.filter( (mon) => {
return (
mon.name.toLowerCase().includes(searchString) ||
mon.id == searchNumber ||
mon.type == searchString
);
});
console.log(filteredPokemon);
displayPokemon(filteredPokemon);
});
HTML
<!-- search bar -->
<div id="search">
<input type="text" name="searchBar" id="searchBar" placeholder="search pokedex"/>
</div>
<!-- pokemon list container -->
<ul class="pokedex"></ul>
I figured it out, in case this may be helpful to someone else.
The displayPokemon function would break when passing in filteredPokemon. filteredPokemon was iterating over the pokemonData array which holds the raw data from the Poke API.
Instead filteredPokemon needs to iterate over the pokemon map within the getPokemonData function, which is how displayPokemon displays the correct data.
So, we push the pokemon map to a new array using the spread operator and iterate over new array in filteredPokemon.
let pokemonResults = [];
const getPokemonData = async () => {
let pokemonData = [];
for (let i = 1; i <= 151; i++) {
const url = `https://pokeapi.co/api/v2/pokemon/${i}`;
const res = await fetch(url);
const data = await res.json();
pokemonData.push(data);
}
Promise.all(pokemonData).then( (results) => {
const pokemon = results.map( (data) => ({
name: data.name,
id: data.id,
image: data.sprites['front_shiny'],
type: data.types.map((type) => type.type.name)
}));
pokemonResults.push(...pokemon);
displayPokemon(pokemon);
});
}
Note: Since pokemon types returns an array, it needs to be converted to a string in order to get the correct search results.
searchBar.addEventListener('keyup', (event) => {
const searchString = event.target.value.toLowerCase();
const searchNumber = event.target.value;
const filteredPokemon = pokemonResults.filter( (mon) => {
return (
mon.name.toLowerCase().includes(searchString) ||
mon.id == searchNumber ||
JSON.stringify(mon.type).toLowerCase().includes(searchString)
);
});
displayPokemon(filteredPokemon);
});
I'm unable to get an error response for the web app, it displays if there's a valid response but doesn't display anything on a bad request. How can I get the error message either on the console or as json string
Here's the code below
const getWeather = () => {
let city = document.querySelector("input").value;
fetch(
`http://api.weatherapi.com/v1/current.json?key=ca7ec552bc034514a9792135211812&q=${city}&aqi=no`
)
.then((data) => data.json())
.then((data) => displayWeather(data));
};
document.querySelector("button").addEventListener("click", () => {
getWeather();
document.querySelector("input").innerText = "";
});
const displayWeather = (data) => {
const localInfo = data.location.localtime;
const name = data.location.name;
const icon = data.current.condition.icon;
const text = data.current.condition.text;
const temp = data.current.temp_c;
const humidity = data.current.humidity;
const country = data.location.country;
const windSpeed = data.current.wind_kph;
const code = data.current.condition.code;
const error =data.error.message;
console.log(name, icon, text, temp, humidity, country, windSpeed, code, error);
Live code at this time of writing this: https://github.com/samuelajala01/my-weather-app/blob/master/script.js
The weather API will only send error sub object if there is error, otherwise it will just send current and location sub objects, hence you just had to add a check for existence of error object in response data
Something like this :
document.querySelector("button").addEventListener("click", () => {
getWeather();
document.querySelector("input").innerHTML = " ";
});
const displayWeather = (data) => {
if (data.error) {
alert(data.error.message);
} else {
const localInfo = data.location.localtime;
const name = data.location.name;
const icon = data.current.condition.icon;
const text = data.current.condition.text;
const temp = data.current.temp_c;
const humidity = data.current.humidity;
const country = data.location.country;
const windSpeed = data.current.wind_kph;
const code = data.current.condition.code;
console.log(name, icon, text, temp, humidity, country, windSpeed, code);
}
}
const getWeather = () => {
let city = document.querySelector("input").value;
fetch(
`http://api.weatherapi.com/v1/current.json?key=ca7ec552bc034514a9792135211812&q=${city}&aqi=no`
)
.then((data) => data.json())
.then((data) => displayWeather(data))
};
I've been building an app with Firebase & React Native primarily using Firestore. I started to use Firestore and its been great, but for some reason when writing to Firestore, it is only working on the first attempt (when i remove the app, rebuild, and perform my write).
I tried to do the exact same thing except write to Firestore and everything works as expected.
I am also receiving no error!
Here is what I am doing:
export const addBrandProduct = (postObj) => {
return () => {
firebase
.firestore()
.collection('brandProducts')
.add(postObj)
.then((docRef) => {
console.log("Document written with ID: ", docRef.id);
Actions.categories();
})
.catch(error => {
console.error("Error adding document: ", error);
});
};
};
For more of a reference, here is my component code that calls addBrandProduct()
onUploadImages = () => {
let photo =
Platform.OS === 'ios'
? this.state.images.map(img => img.uri.replace('file://', ''))
: this.state.images.map(img => img.uri);
photo.forEach((image, i) => {
const sessionId = new Date().getTime();
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
let uploadBlob = null;
let mime = 'image/jpg';
const imageRef = firebase
.storage()
.ref('brandProducts/')
.child(`${this.props.userData.uid}`)
.child(`${sessionId}-${i}`);
fs.readFile(image, 'base64')
.then(data => {
return Blob.build(data, {type: `${mime};BASE64`});
})
.then(blob => {
uploadBlob = blob;
return imageRef.put(blob, {contentType: mime});
})
.then(() => {
uploadBlob.close();
return imageRef.getDownloadURL();
})
.then(url => {
//if this is the last uploaded image, post data to db
if (i === this.state.images.length - 1) {
const urls = {
...this.state.urls,
[i]: url,
};
const postObj = {
...this.state.postObj,
urls: urls,
};
this.props.addBrandProduct(postObj);
} else {
this.setState({
urls: {
...this.state.urls,
[i]: url,
},
});
}
})
.catch(error => {
console.log(error);
});
});
};
Basically, I am uploading a maximum of 3 images along with some data for it. In order to ensure I am uploading them all prior to adding the post data (writing to firestore) I am using a forEach and on the last upload, when it completes, I am calling the action to write the post data.
Edition
Hum addBrandProduct is a function that create another function.
So when you call this.props.addBrandProduct(postObj) nothing is sent to firestore, you just create a new function that should be called.
Maybe you can go out this stuff and call firebase directly, ensuring that everything works and then go back to the redux way if you still want to use it. I also make it parallelized instead of sequentials. Hope it help, hard to find the real problem when it can come from anywhere.
onUploadImages = () => {
let photo = Platform.OS === 'ios'
? this.state.images.map(img => img.uri.replace('file://', ''))
: this.state.images.map(img => img.uri);
Promise.all( photo.map( image => {
const sessionId = new Date().getTime();
const Blob = RNFetchBlob.polyfill.Blob;
//This is kind useless
//const fs = RNFetchBlob.fs;
//This is not used
//window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
//This is not adviced
//window.Blob = Blob;
let uploadBlob = null;
let mime = 'image/jpg';
const imageRef = firebase
.storage()
.ref('brandProducts/')
.child(`${this.props.userData.uid}`)
.child(`${sessionId}-${i}`);
return fs.readFile(image, 'base64')
.then(data => {
return RNFetchBlob.polyfill.Blob.build(data, {type: `${mime};BASE64`});
})
.then(blob => {
uploadBlob = blob;
return imageRef.put(blob, {contentType: mime});
})
.then(() => {
uploadBlob.close();
return imageRef.getDownloadURL();
});
))
.then( results => {
//results is, here, [ urlFromFirst, urlFronSecond, ...]
const urls = { ...this.state.urls};
results.forEach( (r, i) => urls[i] = r );
const postObj = {
...this.state.postObj,
urls
};
return firebase
.firestore()
.collection('brandProducts')
.add(postObj)
})
.then( docRef => {
console.log("Document written with ID: ", docRef.id);
})
.catch(error => {
console.error(error);
});
};
So I'm doing a Javascript technical exercise and I have it almost complete, I'm trying to output data from an API url and displaying it on my html page.
I have everything mostly how I want however I cannot display the title from the api that says Keep What You Want from stepNumber.
I only need stepNumber 3 to display Keep What You Want instead of Keep What You Like.
How do I do that with my current code?
Javascript
fetch('https://uqnzta2geb.execute-api.us-east-1.amazonaws.com/default/FrontEndCodeChallenge')
.then(function (response) {
return response.json();
})
.then(function (data) {
appendData(data);
})
.catch(function (err) {
console.log('This is an error', err);
});
function appendData(data) {
let mainContainer = document.getElementById("testdata");
let titleContainer = document.getElementById("testdata");
let bodyContainer = document.getElementById("testdata");
data
.filter(item => ["1", "2", "3", "4"].includes(item.stepNumber))
.sort((a, b) => +a.stepNumber - b.stepNumber)
.forEach(({id, stepNumber, versionContent}) => {
const div = document.createElement("div");
const divTwo = document.createElement("span");
const divThree = document.createElement("p");
const {title, body} = versionContent.slice().pop();
div.innerHTML = stepNumber;
divTwo.innerHTML = versionContent[0].title;
divThree.innerHTML = body;
mainContainer.appendChild(div);
titleContainer.appendChild(divTwo);
bodyContainer.appendChild(divThree);
});
}
I'm facing yet another issue.
I'm using firebase db to store text and firebase storage to store files. And here comes my issue.
Q: How to fetch a correct image from storage when fetching particular element from database?
Here's my attempt:
const storageRef = firebase.storage().ref('companyImages/companyImage' + 123);
^^^^^^^^^^^^ I dont have access to id yet :(
const task = storageRef.put(companyImage);
task.on('state_changed', () => {
const percentage = (snap.bytesTransferred / snap.totalBytes) * 100;
// ^^^^^^^^^^^^ not sure if i even need this
}, (err) => {
console.log(err);
}, () => {
firebase.database().ref('offers').push(values);
^^^^^^^^^^^^^^^ now I could retrieve id from it with .key but its too late
});
As you can see, first what Im doing is uploading the image and when it's succesful, Im starting to upload the data to database.
Still, it doesnt work as it is supposed to. When uploading image I have to name it with a correct id to retrieve it easily later, in components.
It may look a lil bit complex but will appreciate any kind of help. Any suggestion or hint.
Should I firstly upload data to DB and then image to the storage?
You can generate the push ID before you upload the file – you can also just save the download URL of the returned snapshot at task.snapshot.downloadURL so you don't have to retrieve the file from storage using the storage ref.
const offerRef = firebase.database().ref('offers').push();
const storageRef = firebase.storage().ref(`companyImages/${offerRef.key}`);
const task = storageRef.put(companyImage);
task.on('state_changed', (snap) => {
const percentage = (snap.bytesTransferred / snap.totalBytes) * 100;
}, (error) => {
console.log(err);
}, () => {
offerRef.set(values);
});
I would suggest using .getDownloadURL(). Then push all your uploadedfileDownloadURL's into an object or array and then store that into your database. So in the future you can access this object or array from, lets say your user/ProfilePHotos, and then in your app level code you can just use the DownloadURL as a uri links inside an image tag!
In this example I am using react-native, I upload multiple photos, save the download URL each time in an array, then set the array to firebase under the users account.
export const userVehiclePhotoUploadRequest = (photos, user, year) => dispatch => {
console.log('Inside vehiclePhotoUpload Actions', photos, user)
let referenceToUploadedPhotos = [];
return new Promise((resolve, reject) => {
photos.map(ele => {
let mime = 'application/octet-stream'
let uri = ele.uri
let uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri
let sessionId = new Date().getTime()
let uploadBlob = null
let imageRef = firebase.storage().ref('vehicleImages/' + `${user.account.uid}`).child(`${sessionId}`)
fs.readFile(uploadUri, 'base64')
.then((data) => {
return Blob.build(data, { type: `${mime};BASE64` })
})
.then((blob) => {
uploadBlob = blob
return imageRef.put(blob, { contentType: mime })
})
.then(() => {
uploadBlob.close()
return imageRef.getDownloadURL()
})
.then((url) => {
referenceToUploadedPhotos.push(url)
console.log('ARRAY OF URLS WHILE PUSHING', referenceToUploadedPhotos)
resolve(url)
})
.catch((error) => {
reject(error)
})
})
})
.then(() => {
//I did this to not go home until photos are done uploading.
let vehicles;
firebase.database().ref('users/' + user.account.uid + `/allVehicles/allVehiclesArray`).limitToFirst(1).once('value').then(function (snapshot) {
// ******** This method is straight from their docs ********
// ******** It returns whatever is found at the path xxxxx/users/user.uid ********
vehicles = snapshot.val();
}).then(() => {
console.log('ARRAY OF URLS BEFORE SETTING', referenceToUploadedPhotos)
// let lastVehicle = vehicles.length - 1;
firebase.database().ref('users/' + user.account.uid + `/allVehicles/allVehiclesArray/` + `${Object.keys(vehicles)[0]}` + `/photosReference`).set({
referenceToUploadedPhotos
}).then(() => {
dispatch(loginRequest(user.account))
})
})
})
};
And then in your code, lets say inside a map of the user's information...
{ ele.photosReference !== undefined ? dynamicAvatar = { uri: `${ele.photosReference.referenceToUploadedPhotos[0]}` } : undefined }