I have the following JavaScript method for loading the messages in a mini chat app. The idea is that I have two HTML classes to position the message either on left or right depending on who sent it. The HTML and CSS are fine and work properly. My question is when it comes down to getting the messages from Firestore.
I have the following messages in order from first sent to last: Hello, hyd?, Good, Hello Sir, hey
But somehow strangely they are not ordered properly when displayed on the screen, I get first the messages sent from other user then it displays my messages. I am not sure where I messed up the code for this behavior but I somehow believe it has to do with the if statement where I check who sent the message. The document of a message in Firestore has the following structure:
{
createdAt: ....,
message: ....,
from: ....,
to: ....
}
let divMessagesLeft = document.getElementById("firstmessages--main-div-id");
let devMessagesRight = document.getElementById("firstmessages--main-div-id-2");
async function loadMessages() {
firebase.auth().onAuthStateChanged(async function (user) {
if (user) {
let messagesReference = firebase
.firestore()
.doc(`/chats/${user.displayName}`)
.collection(`/chats/${userChat.username}/messages`)
.orderBy("createdAt");
await messagesReference
.get()
.then(function (snapshot) {
snapshot.forEach(function (doc) {
console.log(doc.data().message);
if (doc.data().from === userChat.username || doc.data().to === user.displayName) {
const message = `<div class="messagee">${doc.data().message}</div>`;
divMessagesLeft.innerHTML += message;
} else {
const message = `<div class="messagee messagee-right">${doc.data().message}</div>`;
devMessagesRight.innerHTML += message;
}
});
});
} else {
console.log("Not logged in");
}
});
}
loadMessages();
<div id="chat-messages-main-div-id" class="chat-messages-main-div">
<div id="first-message-div-id" class="first-message-div">
<div id="firstmessages--main-div-id" class="firstmessages--main-div">
<!-- <div class="messagee">Hello</div> -->
</div>
</div>
<div id="first-message-right-id-2" class="first-message-right">
<div id="firstmessages--main-div-id-2" class="firstmessages--main-div">
<!-- <div class="messagee messagee-right">Hi</div> -->
</div>
</div>
</div>
I would try to replace the "||" operator with "&&", which is a more correct check to do.
(it will fail if you'll try messaging yourself, and you should think how you want to implement this scenario).
Also I think you need to add "desc" to the "order by" method.
Related
So, I'm fetching data from an API and using states. At the beginning, I don't get data from API and render the page with using the states with the data I wrote in the file. (I'm trying to say that I'm using static data at the first load). It works well. No problem at all. Then, when I get an input from user I connect with the API, do all the fetching and stuff and update the states. Everything seems normal. Data is received from API, states are updated, no error on console, first map function is rendered etc. but the second map function isn't. But surprisingly, when I change anything in the file and save it (you know that live server updates the page but doesn't reload), it applies the changes that I've done, and it also renders the second map function with using data I received earlier.
My first map function is this:
<div id="hourlyForecast">
{ weatherData.hourly.map((item, index) => {
/* ----- I'm getting a long string from API and these 2 lines are to get the part that i need from that string ------ */
let startFrom = item.dt_txt.length - 8;
let iNeed = item.dt_txt.toString().substr(startFrom, 5);
return (
<div className="hourly" key={index}>
<div className="hour">{iNeed}</div>
<div className="image">
<img
src={`/icons/${item.weather[0].icon}.png`}
alt="weather-icon"
/>
</div>
<div className="degrees">
{Math.round(item.main.temp)}°C
</div>
<div className="wind">
<img
src="wind.png"
alt="wind-icon"
style={{ transform: `rotate(${item.wind.deg}deg)` }}
/>{
{item.wind.speed} m/s
</div>
</div>
)
})
}
</div>
It is working, I guess. After that, I have this second map function:
<div id="daily">
{weatherData.daily.map((item, index) => {
return (
<div className="daily" key={index}>
<div className="day">
/* ------ Api gives me timestamp and this function returns me the day in human words :) ----- */
{giveMeDay(item.dt * 1000)}
</div>
<div className="dailyDegrees">
{Math.round(item.temp)}°C
</div>
<div className="dailyDesc">
{item.desc}
</div>
<div className="img">
<img src={`./icons/${item.icon}.png`} alt="weather-icon" />
</div>
</div>
);
})
}
</div>
It is also working, it should. I mean, they are not that complex.
So, all with that, here are what happens:
At first load I use static data, and it renders the second map function
(IMAGE) Rendered component with static data
When I enter an input it triggers the API Call, and it should re-render, but it does not (IMAGE) Empty Component even though API Call works
But when I change anything in the file after the API Call and save it, live server updates and the second map function is rendered. Let's say I change "°C" in the second map function with "°F" and save it. Then, everything works. (IMAGE) File updated without page being reloaded
I guess that's all I can say. I just don't understand the problem. Surely would appreciate any help.
Here is the part that I do the API stuff: (It can be a mess 'cause gotta do 3 API calls and didn't want to use async function due to my lack of experience with it)
var datam = {};
const work = (e) => {
e.preventDefault();
try {
fetch(
`${ApiUrl}weather?q=${e.target.cityName.value}&appid=${ApiKey}&units=metric&lang=en`
)
.then((data) => {
return data.json();
})
.then((gelen) => {
console.log(gelen);
if (gelen.cod === 200) {
datam = {
degrees: Math.round(gelen.main.temp),
description: gelen.weather[0].description,
feels_like: gelen.main.feels_like,
city: `${e.target.cityName.value}, ${gelen.sys.country}`,
min: gelen.main.temp_min,
max: gelen.main.temp_max,
icon: gelen.weather[0].icon,
lat: gelen.coord.lat,
lon: gelen.coord.lon,
};
} else {
alert("Couldn't get the data");
}
})
.then(() => {
console.log(datam);
fetch(
`${ApiUrl}forecast?lat=${datam.lat}&lon=${datam.lon}&units=metric&appid=${ApiKey}&lang=en`
)
.then((fivedays) => fivedays.json())
.then((veri) => {
console.log(veri);
datam.hourly = [];
if (veri.cod === "200") {
for (let i = 0; i < 8; i++) {
datam.hourly[i] = veri.list[i];
}
console.log(datam);
}
})
.then(() => {
datam.daily = [];
fetch(
`${ApiUrl}onecall?lat=${datam.lat}&lon=${datam.lon}&exclude=current,hourly,minutely,alerts&units=metric&appid=${ApiKey}&lang=en`
)
.then((donus) => donus.json())
.then((yanit) => {
console.log(yanit);
for (let i = 0; i < 6; i++) {
datam.daily[i] = {};
datam.daily[i]["temp"] = yanit.daily[i + 1].temp.day;
console.log("1");
datam.daily[i].desc =
yanit.daily[i + 1].weather[0].description;
datam.daily[i].icon = yanit.daily[i + 1].weather[0].icon;
datam.daily[i].dt = yanit.daily[i + 1].dt;
}
});
})
.then(() => {
console.log(datam);
// ------------ weatherData is the main state I'm using -----------
setWeatherData(datam);
// ------------ searched state is the important for only the first load. If they do the search i change what is being rendered -----------
setSearched(true);
});
//
});
} catch (error) {
alert(error);
}
};
The problem is this here: (VIDEO) Everything Works but doesn't appear until VS Code recompile
I'm trying to change the page number of an API's endpoint using a variable instead of the actual number. It's a list of products, in each page it displays 8 itens. I was able to render it in the HTML, but I also need to make an expansive product list (which I'm having trouble with aswell).
But I cant make it work. The number is changing in the console.log, but not on the URL I'm fetching. Weirdly, it does use the variable number I placed.
Here is the full JS code:
let page = 1
const btnPage = document.querySelector('#nextPage')
btnPage.addEventListener('click', () => {
page++
console.log(page)
})
function fetchData() {
fetch(`https://frontend-intern-challenge-api.iurykrieger.vercel.app/products?page=${page}`)
.then(response => {
if(!response.ok) {
throw Error('ERROR');
}
return response.json();
}).then(data => {
console.log(data.products);
const html = data.products
.map(product => {
return `
<div class="products-item">
<img src="https:${product.image}" alt="Imagem" class="products-item-img"/>
<div class="products-item--info">
<h4>${product.name}</h4>
<p id="product-description">${product.description}</p>
<p>De: R$${product.oldPrice}</p>
<h3>Por: R$${product.price}</h3>
<p>ou 2x de R$${parseFloat((product.price) / 2)}</p>
<button>Comprar</button>
</div>
</div>
`;
//HTML generated inside the div class "products-wraper"
})
.join("");
document.getElementById('products').insertAdjacentHTML('afterbegin', html);
//.insertAdjacentHTML ratter than .innerHTML to avoid corrupting references
}).catch(error => {
console.log(error);
});
}
fetchData()
And the HTML linked that is been rendered:
<main>
<div class="products">
<div class="products-wraper" id="products"></div>
<button class="products-plus" id="nextPage">Ainda mais produtos aqui!</button>
</div>
</main>
I'm using that button to try and change the page number.
Can anyone help?
Looking at the code that has been posted, I don't see how fetchData() is called, aside from on page load.. On page load, page will always be 1, as a user hasn't had a chance to hit the button yet..
Should fetchData() be called within the event listener callback for clicking on the button?
So what im trying to do is query a Minecraft server with javascript, and with the response i get back with the api, i want to grab the .playerlist and put the response in this url (https://cravatar.eu/avatar/ {name} /100.png) for each person connected
If someone knows a better way to achieve this, i would very much appreciate your input!
Im also pretty new to javascript, so not fully know what im doing :/
Heres the HTML that i have (i know it may be messy, its also not fully my code)
<div class="card"> <div class="icon"><img src="https://cdn.worldvectorlogo.com/logos/minecraft-1.svg"></div><div class="header">
<div class="image"> <img src="https://res.cloudinary.com/lmn/image/upload/e_sharpen:100/f_auto,fl_lossy,q_auto/v1/gameskinnyc/u/n/t/untitled-a5150.jpg" alt="" /> </div>
<h2>Server Status</h2>
</div>
<div id="rest">Loading...</div>
<img src="https://cravatar.eu/avatar/" $face "/>
</div>
And here is the javascript
//Query api at this address
var url = "https://api.minetools.eu/query/play.aydaacraft.online/25565";
$.getJSON(url, function(r) {
//data is the JSON string
if(r.error){
$('#rest').html('Server Offline.');
return false;
}
var p1 = '';
if(r.Players > 0 ){ p1 = '<br>'+r.Playerlist; }
// Text to display below
$('#rest').html('Total Online: '+r.Players+p1);
// Trying to add playerlist to html url
$('#face').html+p1;
});
Since you've pasted jQuery code, I'll submit my answer in jQuery. However, I do recommend you learn primitive JavaScript and not focus your attention just on jQuery... it's become something of a meme on StackOverflow.
Starting off, you really should be wrapping your code in $(document).ready this'll only run the code when the page has loaded.
$(document).ready(() => {
// The document is ready, let's run some code!
});
Then add your AJAX request as normal inside this bracket.
$(document).ready(() => {
let url = "https://api.minetools.eu/query/play.aydaacraft.online/25565";
$.getJSON(url, response => {
});
});
Okay, whilst writing this, I checked the URL provided by OP and saw that it was timing out so I've grabbed a sample response from the Minetools' documentation.
{
"MaxPlayers": 200,
"Motd": "A Minecraft Server",
"Playerlist": [
"Connor",
"Kamil",
"David"
],
"Players": 3,
"Plugins": [],
"Software": "CraftBukkit on Bukkit 1.8.8-R0.2-SNAPSHOT",
"Version": "1.8.8",
"status": "OK"
}
So in your JSON response, you can see that Playerlist is a array which can contain multiple things in one variable. You can also iterate through an array, which is what we'll be doing to build the image URLs.
We iterate through an array using forEach.
$(document).ready(() => {
let url = "https://api.minetools.eu/query/play.aydaacraft.online/25565";
$.getJSON(url, response => {
response.Playerlist.forEach(playerName => {
console.log(playerName);
});
});
});
//Console:
//Connor
//Kamil
//David
Now that we're iterating through the player list we can start assembling the URLs for these images and adding them to your document's body.
I've cleaned up your HTML, take note of the new div#user-images I've added. This'll be the place where jQuery will add your images from the forEach loop.
<div class="card">
<div class="icon">
<img src="https://cdn.worldvectorlogo.com/logos/minecraft-1.svg">
</div>
<div class="header">
<div class="image">
<img src="https://res.cloudinary.com/lmn/image/upload/e_sharpen:100/f_auto,fl_lossy,q_auto/v1/gameskinnyc/u/n/t/untitled-a5150.jpg" alt="" />
</div>
<h2>Server Status</h2>
</div>
<!-- This div tag will need to hide when there is no error, or say when there is. -->
<div id="rest">Loading...</div>
<!-- The user images will be added inside this div. -->
<div id="user-images"></div>
</div>
Now we have our HTML ready we can start using the jQuery function appendTo to add elements into our div#user-images.
$(document).ready(() => {
let url = "https://api.minetools.eu/query/play.aydaacraft.online/25565";
$.getJSON(url, response => {
response.Playerlist.forEach(playerName => {
$(`<img src="https://cravatar.eu/avatar/${playerName}" />`).appendTo("#user-images");
});
});
});
Your div#user-images should start filling up with the images of players from the Playerlist array.
I noticed you added a simple way of showing whether or not there's an error with the API. We can interact with div#rest to show/hide or change text depending on the success of the response.
$(document).ready(() => {
let url = "https://api.minetools.eu/query/play.aydaacraft.online/25565";
$.getJSON(url, response => {
if(response.error){
$("#rest").html("The server is offline!");
}else{
//There is no error, hide the div#rest
$("#rest").hide();
response.Playerlist.forEach(playerName => {
$(`<img src="https://cravatar.eu/avatar/${playerName}" />`).appendTo("#user-images");
});
}
});
});
And that's it really. I hope this gives you some understanding of arrays, and iterating through them, as well as some DOM functions from jQuery.
Objective:
I have a button that runs a function to load more items from my Mongoose DataBase and add them to a table row. I use get to get and return data from my server side. And am following pointers from this post, but I am still unable to render what I need.
Client side code:
<main role="main">
<div class="container-fluid ">
<div class="card-columns" id="test" value="<%=deals%>">
<tbody>
<tr id="content"><% include partials/row.html %></tr>
</tbody>
</div>
</div>
<div class="container" style="max-width: 25rem; text-align: center;">
<a id="8-reload" class="btn more" onclick="loadMore(8)"></a>
</div>
<script >
const loadMore = async function(x){
const response = await fetch(`/${x}`);
if (!response.ok)
throw oops;
const data =await response.text();
console.log(data);
console.log($("#content"));
await document.getElementById('content').insertAdjacentHTML('beforeend',data);
}
</script>
Server JS request:
app.get(`/:count`, (req, res) => {
const count = parseInt(req.params.count);
database.find({}, (err, found) => {
if (!err){
res.render("more",{items:found},(err,html)=>{
if(!err){
res.send(html);
}else{
console.log(err);
}
});
} else {
console.log(err);
}
}).skip(count).limit(25);
});
when running the function nothing happens and browser console log reads the long string of html. and this for the place I want to append to:
jQuery.fn.init {}
proto: Object(0)
No errors on server console log. What am I missing?
UPDATE I tried Appending instead of to content to test and lo and behold I AM appending html, just not all of my content. int only inserts the opening and closing of the entire html template none of the content between it. Weird.
Okay looks like the big problem was two things. The first issue was I was appending to a place that the content couldn't append to. The second issue was My template started with Table rows with more content in them which would not allow other stuff to render. Once I moved my jquery to a parent id and removed the table rows everything came in fine!
I just cant seem to get the snapshot docs to show me the data from the server.
i have checked the collection. it is called "creaciones" without uppercase. I have 1 document and I have files written already. I've made no spelling mistakes whatsoever. I made this work before and now i cant.
db.collection('usuarios').get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc){
console.log(doc.data);
});
setupGrilla(snapshot.docs);
});
//Setup grilla
const setupGrilla = (data) => {
let html = '';
data.forEach(doc => {
const grilla = doc.data();
const creacion = `
<div>
<img src='jpg/${grilla.tipoCreacion}.png' alt='tipoCreacion'>
<h2>${grilla.nombreCreacion}</h2>
<img src='Imagenes/${grilla.nombreFoto}' alt='nombrefoto' class='imagen'>
<span>piezas: ${grilla.piezas}</span>
<span class='separador'></span>
<span>tiempo: ${grilla.tiempo} minutos</span>
<p>padre: ${grilla.ayuda} </p>
<p class='puntos'>Puntos: ${grilla.puntos} </p>
</div>
`;
html += creacion;
});
}
//get Data
db.collection('creaciones').get().then(snapshot => {
setupGrilla(snapshot.docs);
console.log(snapshot.docs);
});
I expect it to show fetch the database data.
db.collection('usuarios').get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc){
console.log(doc.data);
});
setupGrilla(snapshot.docs);
});
That code is just what I have tried before. No need to look into that because I don't have it written at the moment.
You are calling setupGrilla with a snapshot.docs argument, but snapshot is never defined.
Try querySnapshot.docs instead, or rename querySnapshot in snapshot.
You are also passing the wrong argument to your method
db.collection('usuarios').get().then(function(snapshot) {
snapshot.forEach(function(doc){
console.log(doc.data);
});
setupGrilla(snapshot); // <-- Here
});