I want to build a website which returns products from a database and when click on See More, should return product details from server in another html page. The problem is that when I click See More, productID%20=%20null :(.
productDetails = second html page.
productDetails = div - in index.html where products are returned from server
<script>
var productsUrlList = 'https://finalonlineshop.firebaseio.com/.json';
async function getProductsFromServer() {
var productsResponse = await fetch(productsUrlList)
var products = await productsResponse.json();
return products;
}
async function showProducts(productsPromise) {
var products = await productsPromise;
var generatedHTML = '';
var productsIDs = Object.keys(products);
productsIDs.forEach(productID => {
var product = products[productID];
generatedHTML += getGeneratedHTMLForProduct(productID, product);
});
document.getElementById('categories').innerHTML = generatedHTML;
}
function getGeneratedHTMLForProduct(productID, product) {
var generatedHTML = `
<div id = categoriesDiv>
<img class = "categoryImage" src = ${product.Image} />
<div class = "categoryName">${product.Name}</div>
<div class = "categoryPrice">$ ${product.Price}</div>
<br>
<button id = "seeMore" onclick = "seeMore('${productID}')">See
more</button>
</div>
`;
return generatedHTML;
}
function seeMore (productID) {
window.location = `./productDetails.html?productID = ${productID}`;//issue
}
function getProductIDFromUrl () {
var params = new URLSearchParams(window.location.search);
var productID = params.get('productID');
return productID;
}
async function getDetailsFromServer(productID) {
var detailsResponse = await fetch(`https://finalonlineshop.firebaseio.com/Products/details/${productID}.json`);
var details = await detailsResponse.json();
return details;
}
async function seeDetails(detailsPromise) {
var details = await detailsPromise;
var generatedHTML = `
<div id = "detailsAboutProduct">
<img src = "${details.Image}" /> //Cannot read property "Image" of null
<div>${details.Name}</div>
<div>${details.Details}</div>
<div>$ ${details.Price}</div>
<div>${details.Qty}</div>
<button id = "addToCart" onclick = "addToCart();">Add to
cart</button>
</div>
`;
document.getElementById('details').innerHTML = generatedHTML;
}
</script>
get rid of the spaces around the = in the URL, that gets encoded as %20. You should also use encodeURIComponent() to escape any special characters in the product ID.
function seeMore (productID) {
window.location = `./productDetails.html?productID=${encodeURIComponent(productID)}`;
}
Your query to your firebase app seems to be wrong.
you are fetching : https://finalonlineshop.firebaseio.com/Products/details/${productID}.json
which return null
but you have to fetch: https://finalonlineshop.firebaseio.com/${productID}.json
instead and it will return the right object
Related
I'm trying to add a new item on an array that displays in a container on HTML. It's supposed to get an image file and an url from input, check the selected option and push each one to its specified array
Here is the relevant html:
<input type="file" name="comic-cover" id="comic-cover" required />
<input type="url" name="comic-url" id="comic-url" placeholder="url" required />
<select name="publisher-list" id="publisher">
<option value="publisher" disabled>Publisher</option>
<option value="dc">DC Comics</option>
<option value="marvel">Marvel</option>
</select>
<button type="submit" class="btn-submit">Add</button>
<h2 class="comic-publisher">DC Comics</h2>
<div class="dc" id="block-dc">
</div>
<h2 class="comic-publisher">Marvel</h2>
<div class="marvel" id="block-marvel">
</div>
and the js:
var comicCovers = ["imgs/Dark Knights of Steel-000.jpg", "imgs/Hawkeye-000.jpg"]
var trailers = ["https://www.youtube.com/watch?v=WspmgrmEgn4", "https://www.youtube.com/watch?v=ssj0P0uY08U"]
var publishers = [0, 1];
var i = 0;
var blockDC = document.querySelector("#block-dc");
var blockMarvel = document.querySelector("#block-marvel");
render()
var publisher = document.querySelector("select").value;
document.querySelector("select") = function renderPublisher() {
publisher = document.querySelector("select").value;
return publisher;
}
// add new comics to the list //
document.querySelector(".btn-submit") = function addOnList() {
var newCover = document.querySelector("#comic-cover");
var newTrailer = document.querySelector("#comic-url");
if (newCover.endsWith(".jpg") & newTrailer.startsWith("https://")) {
if (publisher == "dc") {
publisher.push(0);
} else {
publisher.push(1)
}
comicCovers.push(newCover.value);
trailers.push(newTrailer.value);
newCover.value = "";
newTrailer.value = "";
render()
}
}
function render() {
for (i; i < comicCovers.length; i++) {
// creates the comic covers using js var //
var comicCover = document.createElement("img");
comicCover.setAttribute("src", `${comicCovers[i]}`);
// creates trailer button //
var trailerButton = document.createElement("button");
// //
var container = document.createElement("div");
container.setAttribute("class", "container");
container.appendChild(trailerButton);
container.appendChild(comicCover);
blockDC.appendChild(container);
trailerButton.appendChild(document.createTextNode("Trailer"));
trailerButton.setAttribute("class", "trailer-button");
trailerButton.setAttribute("onclick", `openTrailer("${trailers[i]}")`);
if (publishers[i] == 0) {
blockDC.appendChild(container);
} else {
blockMarvel.appendChild(container);
}
}
}
I tried to use if (newCover != "" & newTrailer != "") and even changed the input type from file to url. What am I doing wrong?
You're tring to push values inside the variable called publisher which is just a string and not an array.
var publisher = document.querySelector("select").value;
publisher.push(0);
You're assigning a function to an html element by writing:
document.querySelector("select") = function renderPublisher() {
publisher = document.querySelector("select").value;
return publisher;
}
which is not correct.
Also The variables newCover and newTrailer are not strings.
var newCover = document.querySelector("#comic-cover");
var newTrailer = document.querySelector("#comic-url");
so you can't use the methods startsWith and endsWith for them, you have to access the elements' value instead. In order to get the name of the selected file you need to use the input tag's files attribute which returns an array of the selected files, we want the first file so we'll get the first item's name attribute.
var newCover = document.querySelector("#comic-cover").value;
var newTrailer = document.querySelector("#comic-url")?.files[0]?.name;
Overall first you need to grab the values provided inside the inputs, then push them inside the array related to the selected block(Marvel or DC), then you simply need to create the img, video and button tags and append them as a child to the selected block.
Url and file type validation should be handled also on both server and client side and it's gotta be much more than just checking if the fileName ends with ".jpg" or the url starts with "https://".
You need to store the uploaded image and video somewhere on the server. In order to do so, you'll have to attach an eventListener to the input tags so that whenever a file is selected, you'll send an upload request to the server, there you'll check if the file is valid and if so, you'll store the file somewhere on the server, then you'll pass the url of the uploaded photo/video as src to the img/video tags.
const publishers = {
dc: {
comicCovers: [],
trailers: []
},
marvel: {
comicCovers: [],
trailers: []
}
}
const blockDC = document.querySelector("#block-dc");
const blockMarvel = document.querySelector("#block-marvel");
const blocksDivs = {
dc: blockDC,
marvel: blockMarvel
};
const addButton = document.querySelector(".btn-submit");
// add new comics to the list //
addButton.addEventListener("click", () => {
const publisher = document.querySelector("#publisher").value;
const newCoverFileName = document.querySelector("#comic-cover")?.files[0]?.name;
const newTrailerUrl = document.querySelector("#comic-url").value;
if (newCoverFileName?.endsWith(".jpg") && newTrailerUrl?.startsWith("https://")) {
publishers[publisher].comicCovers.push(newCoverFileName);
publishers[publisher].trailers.push(newTrailerUrl);
var container = document.createElement("div");
var coverImg = document.createElement("img");
var playTrailerButton = document.createElement("button");
playTrailerButton.innerHTML = "play trailer"
coverImg.src = "http://yourserveraddress/imgs/" + newCoverFileName;
//test photo
coverImg.src = "https://picsum.photos/200/300"
container.appendChild(coverImg);
container.appendChild(playTrailerButton);
blocksDivs[publisher].appendChild(container);
playTrailerButton.setAttribute("class", "trailer-button");
playTrailerButton.addEventListener("click", () => {
var videoExists = document.getElementById(publishers[publisher].trailers.length)
if (!videoExists) {
var video = document.createElement('video');
video.setAttribute("id", publishers[publisher].trailers.length)
video.style.backgroundColor = "aliceblue"
video.setAttribute("src", newTrailerUrl);
video.setAttribute("width", "200")
video.setAttribute("height", "200")
container.appendChild(video);
playTrailerButton.innerHTML = "close Trailer"
} else {
container.removeChild(videoExists)
playTrailerButton.innerHTML = "play Trailer"
}
})
}
})
<input type="file" name="comic-cover" id="comic-cover" required />
<input type="url" name="comic-url" id="comic-url" placeholder="url" required />
<select name="publisher-list" id="publisher">
<option value="publisher" disabled>Publisher</option>
<option value="dc">DC Comics</option>
<option value="marvel">Marvel</option>
</select>
<button type="submit" class="btn-submit">Add</button>
<h2 class="comic-publisher">DC Comics</h2>
<div class="dc" id="block-dc">
</div>
<h2 class="comic-publisher">Marvel</h2>
<div class="marvel" id="block-marvel">
</div>
I have a method in views.py that adds user to the following list,
I'm trying to make it work whenever I click on the button "follow" that I have added in JavaScript
How do I make my Follow button in JavaScript go to my views.py method onclick ?
views.py method.
def follow(request, profile_to_follow):
try:
user_to_follow = User.objects.get(username=profile_to_follow)
user_to_following = Profile.objects.get(user=request.user.id)
except User.DoesNotExist:
return JsonResponse({"error": "Profile not found."}, status=404)
if request.method == "PUT":
user_to_following.following.add(user_to_follow)
return HttpResponse(status=204)
urls.py
# API Routes
path("posts", views.compose_post, name="compose_post"),
path("posts/all_posts", views.show_posts, name="show_posts"),
path("posts/<int:post_id>", views.post, name="post"),
path("profile/<str:profile>", views.display_profile, name="profile"),
path("<str:profile_posts>/posts", views.display_profile_posts, name="profile_posts"),
path("follow/<str:user_to_follow>", views.follow, name="follow"),
JavaScript
function load_user_info(user_clicked_on){
document.querySelector('#page-view').style.display = 'none';
document.querySelector('#posts-view').style.display = 'none';
document.querySelector('#show-posts').style.display = 'none';
document.querySelector('#load-profile').style.display = 'block';
fetch(`/profile/${user_clicked_on}`)
.then(response => response.json())
.then(profile => {
const profile_element = document.createElement('div');
const followers = document.createElement('div');
const following = document.createElement('div');
const follow_button = document.createElement('button');
follow_button.innerHTML += "Follow";
followers.innerHTML = 'Followers: ' + profile.followers;
following.innerHTML = 'Following: ' + profile.following;
profile_element.appendChild(followers);
profile_element.appendChild(following);
profile_element.appendChild(follow_button);
profile_element.classList.add('profile_element');
follow_button.addEventListener("click", () => {
follow_user(user_clicked_on)
});
document.querySelector('#user-profile').appendChild(profile_element);
});
document.querySelector('#user-profile').innerHTML = `<h3>${user_clicked_on.charAt(0).toUpperCase() + user_clicked_on.slice(1)} Profile</h3>`;
}
function follow_user(user_to_follow){
// to make this function work with the one in views.py
fetch(`/profile/${user_to_follow}`, {
method: 'PUT',
})
}
So I'm working on a section of my Website which has a TODO List built in not I want the data to load just like other data that are loading alongside the page. I tried having onload in Body Tag in my HTML and I had also tried to $(document).ready(function(){}) as well however neither of them seemed to work nor was I getting any errors in my console. But the function is working as it is also link to another function which runs on click of a button which I don't want to keep happening. I want it to be seamless. Is there any reason why this might not be working.
Thanks for any help in advance.
My HTML and Javascript Code:
function create() {
unfinished_task_container = document.getElementById("main_container");
unfinished_task_container.innerHTML = "";
var user = firebase.auth().currentUser;
var uid;
if (user != null) {
uid = user.uid;
}
task_array = [];
firebase
.database()
.ref("/Users/" + uid + "/todo/")
.once("value", (snapshot) => {
snapshot.forEach((childSnapshot) => {
var childKey = childSnapshot.key;
var childData = childSnapshot.val();
task_array.push(Object.values(childData));
});
for (var i = 0; i < task_array.length; i++) {
task_key = task_array[i][0];
task_title = task_array[i][1];
//Data
task_container = document.createElement("div");
task_container.setAttribute("class", "task_container");
task_container.setAttribute("data-key", task_key);
task_data = document.createElement("div");
task_data.setAttribute("id", "task_data");
title = document.createElement("p");
title.setAttribute("id", "task_title");
title.setAttribute("contenteditable", false);
title.innerHTML = task_title;
//Tools
task_tool = document.createElement("div");
task_tool.setAttribute("id", "task_tool");
task_done_button = document.createElement("button");
task_done_button.setAttribute("id", "task_done_button");
task_done_button.setAttribute(
"onclick",
"task_done(this.parentElement.parentElement, this.parentElement)"
);
task_done_button.setAttribute("onclick", "task_done()");
fa_done = document.createElement("i");
fa_done.setAttribute("class", "fa fa-check");
unfinished_task_container.append(task_container);
task_container.append(task_data);
task_data.append(title);
task_container.append(task_tool);
task_tool.append(task_done_button);
task_done_button.append(fa_done);
}
});
}
<body onload="create();">
<div class="dentistToDo">
<p class="divlable">Your Personal To Do List</p>
<input id="input_box" placeholder="What needs to be done?" />
<button id="input_button" onclick="add_task()">Add Task</button>
<div class="container" id="main_container">
<!-- DIV for TODO List -->
</div>
</div>
I am currently trying to build a fullstack application using javascript(node.js). My goal is to create a weather API with dummy data and everything was going well until I reached the end portion where I am trying to get the users search results to show up below the search box. I've tried using a fetch API from my main js file to retrieve data from my index.js file (which contains the server, imported data, etc.) but I continue to get an error message from the console stating that 'res' is not defined. 'res.data.city' is what I am using to acquire the input information from the user and render it to the screen but I keep getting told that it is not defined; I believe that it should be coming from the index.js file. Here is my code. (PLEASE be nice guys. I am a new developer and am just trying to better myself at programming.)
script.js file (main javascript file that is linked to html document)
const form = document.querySelector('form');
const textInput = document.getElementById('city');
let city = textInput.value;
textInput.addEventListener('input', (e) => {
city = e.target.value;
})
const getData = () => {
fetch('http://localhost:3000/weather/?city=' + city)
.then(res => {
return res.json();
})
.then(resData => {
console.log(resData)
}).catch((error) => {
console.log(error)
});
};
form.addEventListener('submit', (e) => {
e.preventDefault();
getData()
let location = document.querySelector('.cityName');
let celsius = document.querySelector('.celsius')
let fahrenheit = document.querySelector('.fahrenheit')
let error = document.querySelector('.error')
if(res.data.city) {
location.innerHTML = 'City: ' + res.data.city;
celsius.innerHTML = 'Temperature (C): ' + res.data['temperature (C)']
fahrenheit.innerHTML = 'Temperature (F): ' + res.data['temperature (F)'];
} else {
error.Message.innerHTML = 'SORRY! This city is not currently in our
database :{'
}
textInput.value = '';
})
index.js(file with the server running)
const express = require('express')
const app = express();
const cors = require('cors')
const fakeWeatherData = require('./data.js')
app.use(cors());
app.get('/weather', (req, res) => {
let cityName = req.query.city.toLowerCase();
for(let i = 0; i < fakeWeatherData.length; i++)
if(!cityName) {
res.send({"status": "error", "message": "Please enter a city name"})
} else if (cityName === fakeWeatherData[i].city.toLowerCase()) {
return res.send(fakeWeatherData[i])
}
res.send({"status": "error", "message": "Sorry! This city isn't in the database :{"})
})
index.html(main file that will be rendered to broswer)
<body>
<div class='main'>
<form>
<label for='city'>City:</label>
<div class='city-box'>
<input type='text' placeholder='Please enter a city' id='city' name='city' require>
</div>
<button type='submit'>CLICK</button>
</form>
</div>
<div class='error'></div>
<div class='weather'>
<h1 class='cityName'></h1>
<h4 class='celsius'></h4>
<h4 class='fahrenheit'></h4>
</div>
<script src="./js/script.js"></script>
</body>
</html>
#GaloisGirl is correct, res is not defined because you are not defining res within the scope of the anonymous function where you set the submit event listener.
Given that you're not already using async/await, seems best to stick with promise notation so it would look something like this:
form.addEventListener('submit', (e) => {
e.preventDefault();
let location = document.querySelector('.cityName');
let celsius = document.querySelector('.celsius')
let fahrenheit = document.querySelector('.fahrenheit')
let error = document.querySelector('.error')
getData()
.then((res) => {
if(res.data.city) {
location.innerHTML = 'City: ' + res.data.city
celsius.innerHTML = 'Temperature (C): ' + res.data['temperature (C)']
fahrenheit.innerHTML = 'Temperature (F): ' + res.data['temperature (F)']
} else {
error.Message.innerHTML = 'SORRY! This city is not currently in our database'
}
textInput.value = ''
})
})
And you'll also want to return the promise from getData
const getData = () => {
return fetch('http://localhost:3000/weather/?city=' + city)
...OTHER STUFF
I am instantiating a new variable from a class. The class has one constructor, city and then fetches jazz clubs through the foursquare API.
When I hard-code the city name into the instantiated class, it works fine. But when I want to feed it a dynamic value (a query from the search bar which I grab through the DOM), it won't recognise the city. Here is the code:
The Class:
class Venues {
constructor(city) {
this.id = '...';
this.secret = '...';
this.city = city;
}
async getVenues() {
const response = await fetch(`https://api.foursquare.com/v2/venues/search?near=${this.city}&categoryId=4bf58dd8d48988d1e7931735&client_id=${this.id}&client_secret=${this.secret}&v=20190309`);
const venues = await response.json();
return venues;
}
}
const input = document.getElementById('search-input').value;
const button = document.getElementById('button');
const jazzClubs = new Venues(input);
button.addEventListener('click', (e) => {
e.preventDefault();
getJazzVenues();
})
function getJazzVenues() {
jazzClubs.getVenues()
.then(venues => {
console.log(venues);
})
.catch(err => {
console.log(err);
});
}
Anyone knows why the the input variable's value is not recognised by the newly instantiated jazzClubs variable?
Also, if you have tips on how to structure this code better or neater, I'd welcome any suggestions (the class definition is in a separate file already).
Many thanks guys!
Adam
You need to make sure, the following statements are triggered after the button click.
const input = document.getElementById('search-input').value;
const jazzClubs = new Venues(input);
Also your code looks too complex. Use simpler code using jquery.
Try something like this:
$(document).ready(function() {
$("#search-button").click(function() {
var searchval = $("#search-input").val();
var id = "xxx";
var secret = "yyy";
alert(searchval);
var url = "https://api.foursquare.com/v2/venues/search?near=" + searchval + "&categoryId=4bf58dd8d48988d1e7931735&client_id=" + id + "&client_secret=" + secret + "&v=20190309";
alert(url);
$.ajax({
url: url,
dataType: 'json',
success: function(data) {
var venues = data.response.venues;
alert(venues);
$.each(venues, function(i, venue) {
$('#venue-result').append(venue.name + '<br />');
});
}
});
});
});
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<label>City:</label>
<input type="text" id="search-input" />
<button type="button" id="search-button">Search</button>
<div id="venue-result"></div>
</body>
</html>