I've tried to retrieve a saved picture URL from Firebase Firestore, but I'm keep getting this error:
Uncaught (in promise) TypeError: Cannot read property 'picURL' of undefined.
I've tried a code, which you can see below. It's from "imgRef" and down.
HTML:
<div class="container">
<ul class="posts">
</ul>
</div>
JavaScript:
var postDocRef = db.collection('posts').doc(uid).collection('userPosts')
postDocRef.get().then(snapshot => {
setupPosts(snapshot.docs)
})
const posts = document.querySelector('.posts');
const setupPosts = (data) => {
let html = '';
data.forEach(doc => {
var docRefIDpost = docRef.id
const post = doc.data();
const li = `
<li>
<div class="title">${post.title}</div>
<div class="content">${post.content}</div>
<img class="img">
</li>
`;
var imgRef = db.collection('posts').doc(uid).collection('userPosts').doc(docRefIDpost);
imgRef.get().then(function(snapshot) {
const picURL = snapshot.data().picURL
if (picURL.exists) {
console.log(snapshot.data)
console.log(picURL)
var imgpost = document.querySelector(".img");
imgpost.src = picURL
}
})
html += li
})
posts.innerHTML = html;
}
});
The error is telling you that snapshot.data() returned undefined. As you can see from the API documentation, data() will return undefined when the requested document was not found. It's not clear here why, but your code should check for that first before accessing properties.
imgRef.get().then(function(snapshot) {
const data = snapshot.data()
if (data) {
const picURL = data.picURL
}
else {
// decide what you want to do if the document doesn't exist
}
})
Related
i'm having a trouble using the id from a template string item
const elementoParaInserirJogosNaLista = document.getElementById("listaJogos");
function exibirJogosNaTela(listaDeJogos) {
elementoParaInserirJogosNaLista.innerHTML = "";
listaDeJogos.forEach((jogo) => {
elementoParaInserirJogosNaLista.innerHTML += `
<div class="jogo">
<a href="paginajogo.html">
<img class="jogo__imagem" src="${jogo.imagem}" alt="${jogo.titulo}" />
</a>
<h2 class="jogo__titulo">${jogo.titulo}</h2>
<p class="jogo__preco" id="preco">R$${jogo.preco}<a ><img class="jogo__carrinho" id="addCarrinho" src="./images/addcart.png" alt="Adicionar ao carrinho"/></p><a/>
</div>
`;
});
}
i've tried to use the id "addCarrinho" and nothing happens
i'm newb on developing
const botoesAddCarrinho = [];
botoesAddCarrinho = document.querySelectorAll(".jogo__carrinho");
botoesAddCarrinho.forEach((evento) =>
evento.addEventListener("click", addNoCarrinho)
);
function addNoCarrinho () {
console.log('ok')
}
i've changed the selector to by the class, but nothings happens, is like the nothing was selected
i'm using the exibirNaTela on the fetch with the json
let jogos = [];
const endpointDaAPI ="jogos.json"
getBuscarJogosDaAPI();
async function getBuscarJogosDaAPI() {
const respost = await fetch(endpointDaAPI);
jogos = await respost.json();
exibirJogosNaTela(jogos.jogos);
}
I have a little problem or a big one, I don't know. I'm trying to send a form with ajax with Symfony, and native JavaScript, but I don't really know how. I managed to do ajax with GET request to try to find a city (which is included in this form).
So I've got my form, I also want to send 2 arrays 1 for images (multiple images with different input) the input are created with js via CollectionType::class, then I'm putting my images in array which I want to send.
And the other array is for the city I want my product to be in. I've got an input text and via ajax it's searching city then by clicking on the city I've got a function putting it on an array.
but now I find difficulties trying to send it everything I found on the web mostly uses jQuery.. but I want to learn JavaScript so I believe I have to train with native first.
so I tried to send my form, but nothing happened when I submit it, not even an error, it just reload the page, and in my console I've got a warning for CORB issues I think it's due to my browser blocking my request because something is wrong in it?
I'm trying to find a way to send it and save it in my database.
so here's the code:
{% extends "base.html.twig" %}
{% block body %}
<div class="container">
<div class="row">
<div class="col-lg-6 mx-auto mt-5">
{{form_start(form)}}
{{form_errors(form)}}
{{form_row(form.title)}}
{{form_row(form.description)}}
{{form_row(form.surface)}}
{{form_row(form.piece)}}
{{form_row(form.type)}}
<div class="container">
<div class="select-btn">
<span class="btn-text d-flex">
<input type="text" oninput="getData(this.value)" class="rel" name="ville" id="">
<span class="arrow-dwn">
<i class="fa-solid fa-chevron-down"></i>
</span>
</span>
</div>
<ul class="list-items js-result"></ul>
</div>
<button type="button" class="btn btn-primary btn-new opacity-100" data-collection="#foo">ajouter une image</button>
<div id="foo" class="row" data-prototype="{{include ("include/_Addimage.inc.html.twig", {form: form.image.vars.prototype})|e("html_attr")}}" data-index="{{form.image|length > 0 ? form.image|last.vars.name +1 : 0}}">
{% for image in form.image %}
<div class="col-4">
{{ include ("include/_Addimage.inc.html.twig", {form: image}) }}
</div>
{{form_errors(form.image)}}
{% endfor %}
</div>
<div class="col-4 mt-5">
{{form_row(form.submit)}}
</div>
{{form_widget(form._token)}}
{{form_end(form, {render_rest: false})}}
</div>
</div>
</div>
{% endblock %}
here the code of my JavaScript, everything is in my twig file, because as you will see I added eventListener on some input, I didn't see a better way maybe someone can correct me.
{% block javascripts %}
<script type="text/javascript">
/////////////// GET INPUT TEXT VALUE AND SHOW A LIST OF CITIES
function getData(text) {
const param = new URLSearchParams();
param.append('text', text);
const url = new URL(window.location.href);
fetch(url.pathname + "?" + param.toString() + "&ajax=1", {
header: {
"X-Requested-With": "XMLHttpRequest"
}
})
.then(response => response.json())
.then(data => {
handle_result(data);
});
}
////////////////////////////// CREATE MY OPTIONS WITH CITIES NAME
function handle_result(response)
{
let result_div = document.querySelector(".js-result");
let str = "";
for (let i = response.length - 1; i >= 0; i--) {
str += "<option" + ' ' + "onclick=" + "addTag(this.value)" + ' ' + "class=" + "item" + ' ' + "value=" + response[i].ville + ">" + response[i].ville + "</option>";
}
result_div.innerHTML = str;
};
// //////////////////////////// ADD THE CITY NAME IN A CONTAINER WHEN I USER CLICK ON IT
const selectBtn = document.querySelector(".select-btn");
const rel = document.querySelector(".rel");
items = document.querySelectorAll(".item");
rel.addEventListener("click", () => {
selectBtn.classList.toggle("open");
});
function createTag(label) {
const div = document.createElement('div');
div.setAttribute('class', 'tag');
const span = document.createElement('span');
span.innerText = label;
const closeBtn = document.createElement('i');
closeBtn.setAttribute('data-item', label);
closeBtn.setAttribute('onclick', 'remove(this)');
closeBtn.setAttribute('class', 'material-icons');
closeBtn.innerHTML = 'close';
div.appendChild(span);
div.appendChild(closeBtn);
return div;
}
btnText = document.querySelector(".btn-text");
let tags = [];
function addTags()
{
reset();
for (let a of tags.slice().reverse()) {
const tag = createTag(a);
selectBtn.prepend(tag);
}
}
function addTag(value) {
input = document.querySelector('.rel');
console.log(input);
if (tags.includes(value)) {
alreadyExist(value);
}
else {
tags.shift();
tags.push(value);
addTags();
}
input.value = "";
}
function alreadyExist(value) {
const index = tags.indexOf(value);
tags = [
... tags.slice(0, index),
... tags.slice(index + 1)
];
addTags();
}
function reset() {
document.querySelectorAll('.tag').forEach(function (tag) {
tag.parentElement.removeChild(tag);
})
}
function remove(value) {
const data = value.getAttribute('data-item');
const index = tags.indexOf(data);
tags = [
... tags.slice(0, index),
... tags.slice(index + 1)
];
addTags();
}
//////////////////////////////////////////////////////// CREATING IMAGE ARRAY TO SEND WITH AJAX REQUEST ?
images = [];
function image_to_array(value) {
if(!images.includes(value))
{
images.push(value);
}else{
return false;
}
}
const form =
{
title: document.getElementById('product_form_title'),
description: document.getElementById('product_form_description'),
surface: document.getElementById('product_form_surface'),
piece: document.getElementById('product_form_piece'),
type: document.getElementById('product_form_type'),
}
const submit = document.getElementById('submit', () => {
const request = new XMLHttpRequest();
const url = new URL(window.location.href);
const requestData =
`
title=${form.title.value}&
description=${form.description.value}&
surface=${form.surface.value}&
piece=${form.piece.value}&
image=${JSON.stringify(images)}&
type=${JSON.stringify(tags)}&
ville=${tags}
`;
fetch(requestData , url.pathname ,{
header: {
"X-Requested-With": "XMLHttpRequest"
}
})
request.addEventListener('load', function(event) {
console.log(requestData);
});
request.addEventListener('error', function(event) {
console.log(requestData);
});
});
////////////////////////////// CREATE NEW FILE INPUT
const newItem = (e) => {
const collectionHolder = document.querySelector(e.currentTarget.dataset.collection);
const item = document.createElement('div');
item.classList.add('col-4');
item.innerHTML = collectionHolder.dataset.prototype.replace(/__name__/g, collectionHolder.dataset.index);
item.querySelector('.btn-remove').addEventListener('click', () => item.remove());
collectionHolder.appendChild(item);
collectionHolder.dataset.index ++;
}
document.querySelectorAll('.btn-new').forEach(btn => btn.addEventListener('click', newItem));
</script>
{% endblock %}
here my controller but I don't think it is the issue, I didn't finish it since I'm quite lost on the js part
class AdminController extends AbstractController
{
#[Route('/admin/create_product', name: 'create_product', methods: ['POST', 'GET'])]
public function createProduct(EntityManagerInterface $em, SluggerInterface $slugger, Request $request, LieuxRepository $villeRepo, SerializerInterface $serializer): Response
{
$product = new Product;
$ville = new Lieux;
$form = $this->createForm(ProductFormType::class, $product);
$form->handleRequest($request);
$list = $villeRepo->findAll();
$query =$request->get('text');
if($request->get('ajax')){
return $this->json(
json_decode(
$serializer->serialize(
$villeRepo->handleSearch($query),
'json',
[AbstractNormalizer::IGNORED_ATTRIBUTES=>['region', 'departement', 'products']]
), JSON_OBJECT_AS_ARRAY
)
);
}
if($request->isXmlHttpRequest())
{
if ($form->isSubmitted() && $form->isValid()) {
$product->setCreatedAt(new DateTime());
$product->setUpdatedAt(new DateTime());
$product->setVille($form->get('ville')->getData());
$product->setType($form->get('type')->getData());
$em->persist($product);
$em->flush();
}
}
return $this->render(
'admin/create_product.html.twig',
['form' => $form->createView() ]
);
}
hope it's clear, thank you
I'm trying to add links to my navbar for searches that users have made, as well as if the user favorites the link. What I'm currently trying to achieve is that if, if the "past searched" section already contains the current search, don't add the current search to avoid duplicates. I am using localStorage to store this data with a stringified array (alreadySearched) and check if this array includes the current search; my problem is that the function always returns false. The same thing happens for the favorites dropdown. What am I doing wrong?
Here's my code:
// primary movie information (API #1)
var getMovie = function(title) {
$("#result").addClass("hidden")
$("#main").removeClass("hidden");
$("#search-form").trigger("reset");
//format the OMDB api url
var apiUrl = `http://www.omdbapi.com/?t=${title}&plot=full&apikey=836f8b0`
//make a request to the url
fetch(apiUrl)
.then(function(response) {
// request was successful
if (response.ok) {
response.json().then(function(movieData) {
// console.log(movieData)
var movieTitle = movieData.Title
getMovieId(movieTitle);
getSoundTrack(movieTitle);
getTrailer(movieTitle);
var movieObj = {
title: movieTitle,
}
var pastSearches = loadPastSearches();
var alreadySearched = false
if (pastSearches) {
pastSearches.forEach(s => {
if (s.title === movieTitle) {
alreadySearched = true;
}
})
}
if (!alreadySearched) {
for (var item of pastSearches) {
let searchEl = document.createElement("a")
let pastSearchTitle = item.title
$(searchEl).text(pastSearchTitle)
$(searchEl).addClass("past-search-item");
$("#past-search-dropdown").append(searchEl)
$(searchEl).click(function(e) {
e.preventDefault();
let title = pastSearchTitle
getMovie(title)
getQuotes(title)
});
}
}
saveSearch(movieObj)
showMovie(movieData);
});
} else {
alert("Error: title not found!");
}
})
.catch(function(error) {
alert("Unable to connect to CineXScore app");
console.log(error)
});
};
// save past search
var saveSearch = function(movieObj) {
var pastSearches = loadPastSearches();
pastSearches.push(movieObj);
localStorage.setItem("movieObjects", JSON.stringify(pastSearches))
}
loadPastSearches = function() {
var pastSearches = JSON.parse(localStorage.getItem("movieObjects"));
if (!pastSearches || !Array.isArray(pastSearches)) {
var pastSearches = []
}
return pastSearches;
}
// dropdown favorite soundtrack buttons
var saveTrack = function(trackObj) {
var faveTracks = JSON.parse(localStorage.getItem("trackObjects"));
if (!faveTracks || !Array.isArray(faveTracks)) {
var faveTracks = []
}
var alreadySearched = false
if (faveTracks) {
faveTracks.forEach(t => {
if (t.name === trackObj.name) {
alreadySearched = true;
}
})
}
if (!alreadySearched) {
let trackEl = document.createElement("a")
$(trackEl).addClass("fave-track");
$(trackEl).text(trackObj.name);
$(trackEl).attr("href", trackObj.url);
$(trackEl).attr("target", "_blank")
$("#favorite-tracks-dropdown").append(trackEl)
}
faveTracks.push(trackObj);
localStorage.setItem("trackObjects", JSON.stringify(faveTracks))
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- Navigation Menu -->
<nav class="navbar navbar-default navbar-fixed-top">
<a id="logo" class="navbar-brand">CineXScore</a>
<div class="dropdown navbar-brand">Past Searches
<i class="fa fa-caret-down"></i>
<div id="past-search-dropdown" class="dropdown-content">
<a id="clear-searches">Clear</a>
</div>
</div>
<div class="dropdown navbar-brand">Favorite Tracks
<i class="fa fa-caret-down"></i>
<div id="favorite-tracks-dropdown" class="dropdown-content">
<a id="clear-favorites">Clear</a>
</div>
</div>
</nav>
This is a simple implementation that I think might help you.
async function fetchMovie(movieTitle) {
const apiUrl = `http://www.omdbapi.com/?t=${movieTitle}&plot=full&apikey=836f8b0`;
let res = await fetch(apiUrl);
res = await res.json();
const title = res.Title;
saveSearch(title); // Should only pass the string:title
}
fetchMovie('spiderman');
function saveSearch(title) {
if (!localStorage.getItem('movies')) localStorage.setItem('movies', ''); // Initialize the localStorage
// e.g: (In localStorage)
// Avenger,Spiderman,The Antman etc..
// return this string & convert into an array
let movies = localStorage
.getItem('movies')
.split(',')
.filter((n) => n);
// Check if the title is already exists
if (!movies.includes(title)) {
movies.push(title);
}
// Also store in localStorage as a string seperated by commas (,)
movies = movies.join(',');
localStorage.setItem('movies', movies);
}
Why when you are searching for something else is deleting the previous contents ?For example first you search for egg and show the contents but then when you search for beef the program deletes the egg and shows only beef.Thank you for your time code:
const searchBtn = document.getElementById('search-btn');
const mealList = document.getElementById('meal');
const mealDetailsContent = document.querySelector('.meal-details-content');
const recipeCloseBtn = document.getElementById('recipe-close-btn');
// event listeners
searchBtn.addEventListener('click', getMealList);
mealList.addEventListener('click', getMealRecipe);
recipeCloseBtn.addEventListener('click', () => {
mealDetailsContent.parentElement.classList.remove('showRecipe');
});
// get meal list that matches with the ingredients
function getMealList(){
let searchInputTxt = document.getElementById('search-input').value.trim();
fetch(`https://www.themealdb.com/api/json/v1/1/filter.php?i=${searchInputTxt}`)
.then(response => response.json())
.then(data => {
let html = "";
if(data.meals){
data.meals.forEach(meal => {
html += `
<div class = "meal-item" data-id = "${meal.idMeal}">
<div class = "meal-img">
<img src = "${meal.strMealThumb}" alt = "food">
</div>
<div class = "meal-name">
<h3>${meal.strMeal}</h3>
Get Recipe
</div>
</div>
`;
});
mealList.classList.remove('notFound');
} else{
html = "Sorry, we didn't find any meal!";
mealList.classList.add('notFound');
}
mealList.innerHTML = html;
});
}
Beacuse you are using innerHTML , if you want to save the previous contents you should use append or innerHTML + = .
Because everytime you make a search, the html var is populated with new data.
if you move the 'html' variable to the root scope, this should get you there:
// get meal list that matches with the ingredients
let html = ""; // <-- place it outside the function body
function getMealList(){
let searchInputTxt = document.getElementById('search-input').value.trim();
fetch(`https://www.themealdb.com/api/json/v1/1/filter.php?i=${searchInputTxt}`)
.then(response => response.json())
.then(data => {
// let html = ""; // <-- remove it from here
if(data.meals){
data.meals.forEach(meal => {
I'm making a gallery in a modal window and when I try to set a class to the next element, it gives an error "Cannot read property 'classList' of null". How can I fix this?
HTML
<div class="card-page__imgs">
<img id="1" href="assets/img/card-page-main-img.png" class="card-page__main-img gallery-img"
src="assets/img/card-page-main-img.png" alt="Изображение">
<div class="card-page__img-group">
<img id="2" src="assets/img/card-page-img1.png" class="card-page__second-img gallery-img next-img">
<img id="3" src="assets/img/card-page-img2.png" class="card-page__second-img gallery-img">
<img id="4" src="assets/img/card-page-img3.png" class="card-page__second-img gallery-img">
</div>
</div>
JS
const galleryBtnPrev = document.querySelector('.gallery__btn_prev'),
galleryBtnNext = document.querySelector('.gallery__btn_next'),
galleryMainImg = document.querySelector('.gallery__main-img'),
galleryImages = document.querySelectorAll('.gallery-img');
const showNext = () => {
const galleryImagesArr = Array.from(galleryImages);
galleryImagesArr.forEach((el, i) => {
//if(el.classList.contains('next'))
const nextSrc = el.getAttribute('src');
if(el.classList.contains('next-img')) {
galleryMainImg.setAttribute('src', nextSrc);
el.classList.remove('next-img');
// el.classList.add('next-img');
const nextElem =el.nextElementSibling;
console.log(nextElem);
nextElem.classList.add('next-img');
return false;
}
// console.log(el, i);
});
};
You cant do const nextElem = el.nextElementSibling; because you have converted the nodeList to an array so the nextElementSibiling property doesn't work, however you can iterate through a nodeList using the forEach anyway so if you remove the const galleryImagesArr = Array.from(galleryImages); and just use galleryImages in place of galleryImagesArr It will work
EDIT
The error was from trying to read next sibling at when at the end of the array so it was returning null instead of wrapping over to the start, fixed this with simple IF statement
Like this
const galleryBtnPrev = document.querySelector('.gallery__btn_prev'),
galleryBtnNext = document.querySelector('.gallery__btn_next'),
galleryMainImg = document.querySelector('.gallery__main-img'),
galleryImages = document.querySelectorAll('.gallery-img');
const showNext = () => {
galleryImages.forEach((el, i) => {
//if(el.classList.contains('next'))
const nextSrc = el.getAttribute('src');
if(el.classList.contains('next-img')) {
//galleryMainImg.setAttribute('src', nextSrc);
el.classList.remove('next-img');
// el.classList.add('next-img');
var nextElem = el.nextElementSibling
if (nextElem === null) {
nextElem = galleryImages[0]
}
nextElem.classList.add('next-img');
return false;
}
// console.log(el, i);
});
};
showNext()