Why is this const only available within the method that calls it? - javascript

I have this code to create blog posts:
let postsArray = []
const titleInput = document.getElementById("post-title")
const bodyInput = document.getElementById("post-body")
const form = document.getElementById("new-post")
function renderPosts() {
let html = ""
for (let post of postsArray) {
html += `
<h3>${post.title}</h3>
<p>${post.body}</p>
<hr />
`
}
document.getElementById("blog-list").innerHTML = html
}
fetch("https://apis.scrimba.com/jsonplaceholder/posts")
.then(res => res.json())
.then(data => {
postsArray = data.slice(0, 5)
renderPosts()
})
form.addEventListener("submit", function(e) {
e.preventDefault()
const postTitle = titleInput.value
const postBody = bodyInput.value
const data = {
title: postTitle,
body: postBody
}
const options = {
method: "POST",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json"
}
}
fetch("https://apis.scrimba.com/jsonplaceholder/posts", options)
.then(res => res.json())
.then(post => {
postsArray.unshift(post)
renderPosts()
/**
* Challenge: clear the form out!
*/
titleInput.value = ""
bodyInput.value = ""
// form.reset()
})
})
I tried moving
const postTitle = titleInput.value
const postBody = bodyInput.value
to the top (outside of the event listener method) and then defining the data variable:
const data = {
title: postTitle,
body: postBody
}
However it only works when the postTitle and postBody variables are defined within the event listener method. Why is this? Why are titleInput and bodyInput available when defined outside of the method, but these aren't?
Is it because the .value is only available within the addEventListener method?

You can take a reference to an input field (or other HTML element) when the page loads, for example:
const titleInput = document.getElementById("post-title")
But you can't get the value associated with that input field until the value has been provided by the user. It's available at the time the "submit" handler executes (assuming the user typed in a value).
form.addEventListener("submit", function(e) {
e.preventDefault()
const postTitle = titleInput.value
...
});

Related

Spotify API - Audio Player

I am relatively new to Javascript, I am practicing with a program that is able to pull data from Spotify API starting with Genre, then Playlists by Genre, Tracks, Track details, and an audio tag player to play a preview of the track.
I am able to do the Genre, Tracks, Track Details, but can't figure out the audio tag being linked to the track. The two issues, I have:
How to generate the audio tag based on user selection, and then reset it once a new selection is made
How to Get the Preview for the track based on user selection (for now I have the src for the audio tag as a random track.
const APIController = (function() {
const clientId = 'XX';
const clientSecret = 'XX';
//Fetch data from spotify api; private methods
const _getToken = async () => {
const result = await fetch('https://accounts.spotify.com/api/token', {
method: 'POST',
headers: {
'Content-Type' : 'application/x-www-form-urlencoded',
'Authorization' : 'Basic ' + btoa( clientId + ':' + clientSecret)
},
body: 'grant_type=client_credentials'
});
const spot = await result.json();
return spot.access_token;
}
const _getGenres = async (token) => {
const limit = 10;
const result = await fetch(`https://api.spotify.com/v1/browse/categories?country=CA&limit=${limit}`, {
method: 'GET',
headers: { 'Authorization' : 'Bearer ' + token}
});
const spot = await result.json();
return spot.categories.items;
}
const _getPlaylistByGenre = async (token, genreId) => {
const limit = 10;
const result = await fetch(`https://api.spotify.com/v1/browse/categories/${genreId}/playlists?limit=${limit}`, {
method: 'GET',
headers: { 'Authorization' : 'Bearer ' + token}
});
const spot = await result.json();
return spot.playlists.items;
}
const _getTracks = async (token, tracklist) => {
const limit = 30;
const result = await fetch(`${tracklist}?limit=${limit}`, {
method: 'GET',
headers: { 'Authorization' : 'Bearer ' + token}
});
const spot = await result.json();
return spot.items;
}
const _getTrack = async (token, trackdetail) => {
const result = await fetch(`${trackdetail}`, {
method: 'GET',
headers: { 'Authorization' : 'Bearer ' + token}
});
const spot = await result.json();
return spot;
}
const _getPreview = async (token, trackpreview) => {
const result = await fetch(`${trackpreview}`, {
method: 'GET',
headers: { 'Authorization' : 'Bearer ' + token}
});
const spot = await result.json();
return spot.preview;
}
return {
getToken() {
return _getToken();
},
getGenres(token) {
return _getGenres(token);
},
getPlaylistByGenre(token, genreId) {
return _getPlaylistByGenre(token, genreId);
},
getTracks(token, tracklist) {
return _getTracks(token, tracklist);
},
getTrack(token, trackdetail) {
return _getTrack(token, trackdetail);
},
getPreview(token, trackpreview) {
return _getPreview(token, trackpreview);
}
}
})();
// UI Module
const UIController = (function() {
//object to hold references to html selectors
const DOMElements = {
selectGenre: '#select_genre',
selectPlaylist: '#select_playlist',
buttonSubmit: '#btn_submit',
divSongDetail: '#song-detail',
hfToken: '#hidden_token',
divSonglist: '.song-list',
preview:'.songpreview'
}
//public methods
return {
//method to get input fields
inputField() {
return {
genre: document.querySelector(DOMElements.selectGenre),
playlist: document.querySelector(DOMElements.selectPlaylist),
tracks: document.querySelector(DOMElements.divSonglist),
submit: document.querySelector(DOMElements.buttonSubmit),
songDetail: document.querySelector(DOMElements.divSongDetail),
songPreview: document.querySelector(DOMElements.preview)
}
},
// need methods to create select list option
createGenre(text, value) {
const html = `<option value="${value}">${text}</option>`;
document.querySelector(DOMElements.selectGenre).insertAdjacentHTML('beforeend', html);
},
createPlaylist(text, value) {
const html = `<option value="${value}">${text}</option>`;
document.querySelector(DOMElements.selectPlaylist).insertAdjacentHTML('beforeend', html);
},
// need method to create a track list group item
createTrack(id, name) {
const html = `${name} `;
document.querySelector(DOMElements.divSonglist).insertAdjacentHTML('beforeend', html);
},
// need method to create the song detail
createTrackDetail(img, title, artist) {
const detailDiv = document.querySelector(DOMElements.divSongDetail);
// any time user clicks a new song, we need to clear out the song detail div
detailDiv.innerHTML = '';
const html =
`
<div class="songimg">
<img src="${img}" alt=""width="100px" height="100px">
</div>
<div class="songname"><span>
<label for="Genre" style="font-size:20px; color:black" class="form-label">${title}</label>
</span>
</div>
<div class="artistname"><span>
<label for="artist" style="color:blue" class="form-label">${rtist}</label>
</span></div>
`;
detailDiv.insertAdjacentHTML('beforeend', html)
},
createPreview() {
const html = `<audio class="preview" id="newplayer" controls onplay="playtrack()" onpause="pausetrack()" type="audio/mpeg" src="https://p.scdn.co/mp3-preview/ad57003c15e607c0d74c8a25ed7581c770b28a9a?cid=774b29d4f13844c495f206cafdad9c86" preload="auto"></audio>`;
document.querySelector(DOMElements.songPreview).insertAdjacentHTML('beforeend', html);
},
//method to reset fields
resetPreview() {
this.inputField().songPreview.innerHTML = '';
},
resetTrackDetail() {
this.inputField().songDetail.innerHTML = '';
},
resetTracks() {
this.inputField().tracks.innerHTML = '';
this.resetTrackDetail();
const spotpreview=document.getElementById("newplayer").classList;
spotpreview.remove("showme");
document.getElementsByTagName("body")[0].style="background-image:url('hands.jpeg');";
document.getElementsByTagName("body")[0].class="backgrd3";
},
resetPlaylist() {
this.inputField().playlist.innerHTML = '';
this.resetTracks();
},
storeToken(value) {
document.querySelector(DOMElements.hfToken).value = value;
},
getStoredToken() {
return {
token: document.querySelector(DOMElements.hfToken).value
}
}
}
})();
const APPController = (function(UICtrl, APICtrl) {
// get input field object ref
const DOMInputs = UICtrl.inputField();
// get genres on page load
const loadGenres = async () => {
//get the token
const token = await APICtrl.getToken();
//store the token onto the page
UICtrl.storeToken(token);
//get the genres
const genres = await APICtrl.getGenres(token);
//populate our genres select element
genres.forEach(element => UICtrl.createGenre(element.name, element.id));
}
// create genre change event listener
DOMInputs.genre.addEventListener('change', async () => {
//reset the playlist
UICtrl.resetPlaylist();
//get the token that's stored on the page
const token = UICtrl.getStoredToken().token;
// get the genre select field
const genreSelect = UICtrl.inputField().genre;
// get the genre id associated with the selected genre
const genreId = genreSelect.options[genreSelect.selectedIndex].value;
// ge the playlist based on a genre
const playlist = await APICtrl.getPlaylistByGenre(token, genreId);
// create a playlist list item for every playlist returned
playlist.forEach(p => UICtrl.createPlaylist(p.name, p.tracks.href));
});
// create submit button click event listener
DOMInputs.submit.addEventListener('click', async (e) => {
// prevent page reset
e.preventDefault();
// clear tracks
UICtrl.resetTracks();
//get the token
const token = UICtrl.getStoredToken().token;
// get the playlist field
const playlistSelect = UICtrl.inputField().playlist;
// get track endpoint based on the selected playlist
const tracksEndPoint = playlistSelect.options[playlistSelect.selectedIndex].value;
// get the list of tracks
const tracks = await APICtrl.getTracks(token, tracksEndPoint);
// create a track list item
tracks.forEach(el => UICtrl.createTrack(el.track.href, el.track.name))
});
// create song selection click event listener
DOMInputs.tracks.addEventListener('click', async (e) => {
// prevent page reset
e.preventDefault();
UICtrl.resetTrackDetail();
// get the token
const token = UICtrl.getStoredToken().token;
// get the track endpoint
const trackEndpoint = e.target.id;
//get the track object
const track = await APICtrl.getTrack(token, trackEndpoint);
// load the track details
UICtrl.createTrackDetail(track.album.images[2].url, track.name, track.artists[0].name);
});
// create song preview selection click event listener
DOMInputs.songPreview.addEventListener('click', async (e) => {
// prevent page reset
e.preventDefault();
UICtrl.resetPreview();
// get the token
const token = UICtrl.getStoredToken().token;
// get the playlist field
const previewsong = UICtrl.inputField().songPreview;
const previewEndPoint = previewsong.options[previewsong.selectedIndex].value;
// get the list of tracks
const tracks = await APICtrl.getTracks(token, previewEndPoint);
// create a track list item
tracks.forEach(el => UICtrl.createTrack(el.track.href, el.track.name))
});
return {
init() {
console.log('App is starting');
loadGenres();
}
}
})(UIController, APIController);
// will need to call a method to load the genres on page load
APPController.init();

How to pass API data into a variable for later use using Javascript?

I need my code to run through this first & then I need to be able to retrieve the url from the API for later use.
const apiKey = "_____________"
const file = document.getElementById("file")
const img = document.getElementById("img")
const url = document.getElementById("image_url")
file.addEventListener("change", ev => {
const formdata = new FormData()
formdata.append("file", ev.target.files[0])
formdata.append("upload_preset", apiKey)
fetch("https://api.cloudinary.com/v1_1/dj6n2zg0o/image/upload", {
method: "post",
body: formdata
}).then(data => data.json()).then(data => {
// Updates Image on HTML page
img.src = data.url
// Renders URL onto HTML
url.innerText = data.url
// Need to get image_url variable to newFormHandler function
return data.url
})
});
After the images passes through API I need to pass the URL into this form handler:
const newFormHandler = async (event) => {
event.preventDefault();
const name = document.querySelector('#name').value.trim();
const initial_price = document.querySelector('#initial_price').value.trim();
const description = document.querySelector('#description').value.trim();
if (name && initial_price && description) {
const response = await fetch(`/api/products`, {
method: 'POST',
body: JSON.stringify({ name, initial_price, description }),
headers: {
'Content-Type': 'application/json',
},
});
if (response.ok) {
document.location.replace('/');
} else {
alert('Failed to create project');
}
}
};
document
.querySelector('.new-product-form')
.addEventListener('submit', newFormHandler);
Any advice would be greatly appreciated
I figured it out. It turns out I needed a global variable to define the data & use in the form handler.
let storedData
file.addEventListener("change", ev => {
const formdata = new FormData()
formdata.append("file", ev.target.files[0])
formdata.append("upload_preset", apiKey)
fetch("https://api.cloudinary.com/v1_1/dj6n2zg0o/image/upload", {
method: "post",
body: formdata
}).then(data => data.json()).then(data => {
img.src = data.url
url.innerText = data.url
// Need to get image_url variable to newFormHandler function
storedData = data
console.log(storedData)
return data.url
})
});
The below was modified:
const newFormHandler = async (event) => {
event.preventDefault();
// Need url from storedData variable
const {url} = storedData
// const image_url = storedData.url
// From Mini Project
const name = document.querySelector('#name').value.trim();
const initial_price = document.querySelector('#initial_price').value.trim();
const description = document.querySelector('#description').value.trim();
// Added image_url: url
if (name && initial_price && description) {
// Mini Project - const response = await fetch(`/api/projects`,
const response = await fetch(`/api/products`, {
method: 'POST',
// Need to be key value pairs
body: JSON.stringify({ name, initial_price, description, image_url: url }),
headers: {
'Content-Type': 'application/json',
},
});
if (response.ok) {
document.location.replace('/');
} else {
alert('Failed to create project');
}
}
};
document
.querySelector('.new-product-form')
.addEventListener('submit', newFormHandler);

How to update page after fetch POST request

I am creating a simple chat room and am expecting the page to update when I enter a message into a text box and press enter.
The room is receiving messages from a URL, but I have to reload the page to see my new message whenever I post it.
How do I display these messages without having to reload the page?
fetch GET:
fetch('https://curriculum-api.codesmith.io/messages', { 'method': 'GET' })
.then(response => response.json())
.then(data => addText(data))
fetch POST:
textBox.addEventListener('keydown', (e) => {
if (e.code === 'Enter') {
const func = async function postReply() {
const currentDate = new Date()
const message = textBox.value;
textBox.value = ''
return await fetch('https://curriculum-api.codesmith.io/messages',
{
method: 'POST',
body: JSON.stringify({
'message': message,
'created_at': currentDate.toUTCString(),
'created_by': 'Nate & Dewey'
})
})
.then(response => response.json())
// .then(json => addText(json.message))
}
func()
// $('#result').load('https://curriculum-api.codesmith.io/messages');
}
})
addText() function (used by GET)
function addText(data) {
let messageNum = 0
let current = data[messageNum]
while (current) {
const text = document.createElement('div');
border.appendChild(text);
text.innerText = current.message + ' (' + current.created_by + ')'
messageNum++
current = data[messageNum]
}
}

How can I get error message of weatherapi using fetch

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))
};

Can't read value of dynamically generated HTML objects

Datos() loads info converted to HTML objects into an HTML file with the class of each one already defined. I need save() to read the values of the inputs generated but when I try to do it using document.querySelector() to find them by their class, it apparently returns null and gives me this error:
infoPersonal.js:67 Uncaught TypeError: Cannot read property 'value' of null
at HTMLSpanElement. (infoPersonal.js:67)
Am I doing something wrong?
(I would add a snippet code but it doesn't work because of some imports, I hope the javascript code is enough)
const datos = () => {
const token = localStorage.getItem('token')
if (token) {
return fetch('https://janfa.gharsnull.now.sh/api/auth/me', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
authorization: token,
},
})
.then(x => x.json())
.then(user => {
const datalist = document.querySelectorAll(".data")
datalist.forEach(function(el) {
var divInfo = document.createElement("div")
divInfo.setAttribute("class", `divInfo-${el.getAttribute("data-target")}`)
var divInput = document.createElement("div")
divInput.setAttribute("class", `divInput-${el.getAttribute("data-target")}`)
divInput.hidden = true
var input
var icon
const template = '<p>' + user[el.getAttribute("data-target")] + '</p>'
input = document.createElement("input")
input.type = "text"
input.setAttribute("class", `input-${el.getAttribute("data-target")}`)
input.value = user[el.getAttribute("data-target")]
icon = document.createElement("span")
icon.setAttribute("class", "flaticon-diskette")
icon.setAttribute("data-target", `${el.getAttribute("data-target")}`)
divInfo.innerHTML = template
divInput.appendChild(input)
divInput.appendChild(icon)
el.appendChild(divInfo)
el.appendChild(divInput)
}
})
}).then(() => {
save()
})
}
}
const save = () => {
const saves = document.querySelectorAll(".flaticon-diskette")
saves.forEach(function(save) {
const input = document.querySelector(`.input-${save.getAttribute("data-target")}`)
save.addEventListener('click', () => {
console.log(input.value)
})
})
}
That's the corrected script, have a good day!
const datos= () => {
const token = localStorage.getItem('token')
if (token) {
return fetch('https://janfa.gharsnull.now.sh/api/auth/me', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
authorization : token,
},
})
.then( x => x.json())
.then( user =>{
const datalist = document.querySelectorAll(".data")
datalist.forEach( function(el){
var divInfo= document.createElement("div")
divInfo.setAttribute("class", `divInfo-${el.getAttribute("data-target")}`)
var divInput = document.createElement("div")
divInput.setAttribute("class", `divInput-${el.getAttribute("data-target")}`)
divInput.hidden= true
var input
var icon
const template = '<p>' + user[el.getAttribute("data-target")] + '</p>'
input = document.createElement("input")
input.type = "text"
input.setAttribute("class" , `input-${el.getAttribute("data-target")}`)
input.value = user[el.getAttribute("data-target")]
icon = document.createElement("span")
icon.setAttribute("class", "flaticon-diskette")
icon.setAttribute("data-target", `${el.getAttribute("data-target")}`)
divInfo.innerHTML = template
divInput.appendChild(input)
divInput.appendChild(icon)
el.appendChild(divInfo)
el.appendChild(divInput)
}
})
}).then (() =>{
save()
})
}
}
const save = () =>{
const saves= document.querySelectorAll(".flaticon-diskette")
saves.forEach(function(save){
const input = document.querySelector(`.input-${save.getAttribute("data-target")}`)
save.addEventListener('click', () =>{
console.log(input.value)
})
})
}

Categories