I'm writing a small website for a newspaper and I'm trying to make the home page to show the latest news and a photo for each. I use Firebase Realtime Database and Firestorage to store the news and the photos. When getting the data and showing it I save all the data first in an object array and then displaying it (so it remains in order).
The problem is that when I'm calling the function to display it the links to the photos don't appear.
I tried outputting the array with console.log(arrayName) and everything looks like it should. But when I try console.log(arrayName[1].sos) it shows nothing.
db.collection("articole").orderBy("data", "asc").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
articolTmp = {
titlu: doc.data().titlu,
subtitlu: doc.data().subtitlu,
id: doc.id,
poza: doc.data().poza,
data: doc.data().data,
sos: ""
};
articole.push(articolTmp);
});
show();
});
function show(){
for(let i = 1; i < articole.length; ++i){
var pathReference = storage.ref('pozeArticole/' + articole[i].poza);
pathReference.getDownloadURL().then(function(url){
articole[i].sos = url;
});
}
showw();
}
function showw(){
// this is the console.log I was talking about
console.log(articole);
for(let i = 1; i < articole.length; ++i){
console.log(articole[i].sos);
var div = document.createElement("div");
var titlu = document.createElement("h3");
var subtitlu = document.createElement("p");
var link = document.createElement("a");
var poza = document.createElement("img");
poza.src = articole[i].sos;
poza.alt = "alt";
link.href = "viz.html?id=" + articole[i].id;
titlu.innerHTML = articole[i].titlu;
subtitlu.innerHTML = articole[i].subtitlu;
document.getElementById("main").appendChild(div);
link.appendChild(titlu);
div.appendChild(link);
div.appendChild(subtitlu);
div.appendChild(poza);
}
Your problem is in the show function. The last line of code which calls showw is executed before the line "above it" which is articole[i].sos = url;. Why is that? Because you are using an asynchronous operation. The code that gets the download url is executed in a different thread. Whenever that code finished, the block you provided (articole[i].sos = url;) will get executed. I recommend reading about asynchronous operations and promises.
So, to solve your problem, you have ti move the showw call inside the block above it. The block should be:
pathReference.getDownloadURL().then(function(url){
articole[i].sos = url;
showw();
});`
Related
I'm trying to make an interface that lets a user upload CSV files and plots these using plotly, only using javascript and obviously the plotly library. I'm close, but my suspicion is that there's an issue with the asynchronous reading of the csv files.
As you can probably see, I'm relatively new to javascript, so any feedback is welcome. I cannot however use any other libraries or packages plotly.
The problem is that the resulting figure only shows the initialized values (1).
EDIT: The heatmap function works on test data, or if I modify specific elements of the data_y object, just not when I update the information from the file.
There's a button that allows uploading of the csv files. On event this code triggers:
<script>
let picker = document.getElementById('picker');
picker.addEventListener('change', event => {0
file_list = event.target.files;
var fig_y = [];
for (let i = 0 ; i< file_list.length ; i++){
if(file_list[i].name == (".DS_Store")){continue}
else {
var ready = read_data(file_list[i]);
fig_y.push(ready);
}
}
console.log(fig_y);
plot_heatmap(fig_y);
}
);
</script>
The data is read using this code.
<script>
function read_data(input){
var xs = 1212; // length of the data
file_contents = [];
var data_y = Array(xs).fill(1);
let file = input;
let reader = new FileReader();
reader.readAsText(file);
reader.onload = function(){
file_contents = reader.result.split('\n');
// open the data file. First two lines contain a description of the data.
for (let j = 2 ; j<file_contents.length-1 ; j++) {
// the relevant data is the third number in the column
var nr = file_contents[j].split(",").map(Number)[2];
data_y[j-2] = nr;
}
}
return data_y;
}
</script>
the code that makes the plotly heatmap.
<script>
function plot_heatmap(data_z){
var data = [
{
z: data_z,
type: 'heatmap'
}
];
Plotly.newPlot('raw_data', data);
};
</script>
OK, so I figured out the answer. It comes from the asynchronous reading of the text files. Putting the plot_heatmap function in the following Timeout function solved the issue (well, maybe it's more a workaround).
setTimeout(() => { plot_heatmap(fig_y); }, 100);
Actually, by changing the length of the timeout, I could catch JS in its act and see half the heatmap filled in with the real values and the other half still with the initialized value!
I have a few lines of JavaScript code that pick up heading texts from separate sections and place them into their respective input fields. They are also executed on single pages using wp_enqueue_script.
It works absolutely fine when setTimeout() is used:
function passengerElevator() {
var getProductName = document.querySelectorAll('[data-id="6657316"]');
getProductName.forEach(function(item) {
var productName = item.querySelector('.lift');
var inputArea = item.querySelector('input[name=product]');
inputArea.value = productName.innerText;
});
var getProductName = document.querySelectorAll('[data-id="e9c06d5"]');
getProductName.forEach(function(item) {
var productName = item.querySelector('.lift');
var inputArea = item.querySelector('input[name=product]');
inputArea.value = productName.innerText;
});
setTimeout(function() { passengerElevator() },3000);
However, there is problem of page size (some pages have more than 10 input fields) and I don't want to set an astronomically high ms to delay the script. So I decided to fire it on DOMContentLoaded:
document.addEventListener("DOMContentLoaded", passengerElevator);
function passengerElevator() {
var getProductName = document.querySelectorAll('[data-id="6657316"]');
getProductName.forEach(function(item) {
var productName = item.querySelector('.lift'); // heading text (ex:Panoramic Lift)
var inputArea = item.querySelector('input[name=product]');
inputArea.value = productName.innerText; //ouput here
});
var getProductName = document.querySelectorAll('[data-id="e9c06d5"]');
getProductName.forEach(function(item) {
var productName = item.querySelector('.lift'); // Heading text (ex:Home Lift)
var inputArea = item.querySelector('input[name=product]');
inputArea.value = productName.innerText; // Output here
});
}
As you may have already guessed, it is not working. Is my code too messy to be executed faster or is there any other problem I am missing?
I know similar questions have been asked previously, however, no existing answer I found was able to help me.
It seems like you try to loop through elements that are still not loaded. Perhaps they are being appended to the page via Ajax, so DOMContentLoaded can't help there.
You can create your own check for those elements using setInterval, so use something like this:
let dataIdCheck = setInterval(() => {
if (document.querySelectorAll('[data-id="6657316"]').length > 0 && document.querySelectorAll('[data-id="e9c06d5"]').length > 0) {
clearInterval(dataIdCheck);
// your code here
}
}, 500);
This code will run every 500 milliseconds and check if those two elements exists, using .length. Once they do exists, we stop the interval and run the code.
I also suggest to do console.log('in') to check that our interval stop running once the elements are found.
I am trying to retrieve data from my database. My code creates an empty array and appends a child to it each time one is added. It then creates the list (ignore the MDL classes) and adds it to the HTML document.
<script>
// get emergencies to array
var firebaseRef = firebase.database().ref('Incidents');
var emergencies = [];
// var emergencies = ['Test', 'Test 2', 'Test 3'];
firebaseRef.on('child_added', function(snap) {
snap.forEach(function (childSnap) {
console.log(childSnap.val());
emergencies.push(childSnap.val());
});
});
var opentag = '<ul class="mdl-list" id="emergenciesList">',
closetag = '</ul>',
array = [];
for (i = 1; i <= emergencies.length; i++) {
array[i] = '<li class="mdl-list__item">' + emergencies[i] + '</li>';
}
var newArray = array.join(" ");
document.getElementById('foo').innerHTML = opentag + newArray + closetag;
</script>
The weird thing is, that at the console.log() statement, the data is retrieved perfectly fine, but after the string manipulation, newArray is undefined. Help!
The problem is caused by the way you've ordered your code. The order in which the lines are executed are not what you think. It's easiest to see this if you reduce it to this:
var firebaseRef = firebase.database().ref('Incidents');
console.log("Before database loading started");
firebaseRef.on('child_added', function(snap) {
console.log("In child_added");
});
console.log("After database loading started");
Now the order in which the logging will be written is:
Before database loading started
After database loading started
In child_added
This is probably not what you expected. The reason the logging shows in this order is that Firebase loads the data asynchronously. So while the on('child_added' lines starts loading the data, it may take some time to get that data from the Firebase servers. Instead of waiting for the data (which would block the ability of your users to interact with your app), the browser continues to execute the statements after the block. Then when the data is available, it calls your callback function.
A common way of dealing with the asynchronicity is by reframing your problems. Right now your code is written as "first load the data, then add it to the HTML". Try instead framing it as "start loading the data. When the data is available, add it to the HTML". That translates into this code:
var firebaseRef = firebase.database().ref('Incidents');
firebaseRef.on('child_added', function(snapshot) {
var ul = document.getElementById("emergenciesList");
if (!ul) {
document.getElementById('foo').innerHTML = '<ul class="mdl-list" id="emergenciesList"></ul>';
ul = document.getElementById("emergenciesList");
}
var li = document.createElement("li");
li.classList.append("mdl-list__item"); // or: li.className = "mdl-list__item"
li.id = snapshot.key;
li.innerText = snapshot.val();
ul.appendChild(li);
});
I removed the loop over the snapshot, because I'm not sure it is needed and it complicates the code. If your data structure needs the loop, you can add it back where it was.
Long story short in another portion of the program I make canvases, convert them to DataURLs, then pass them over to the following portion to use as the icon image of the buttons. Whenever I set this.icon = "/path/to/image.jpg", it pulls it correctly, but since these images are not on disk, I am unsure how to
arrowHandler: function (arrow) {
var list = [];
var library = Ext.getCmp("library");
var buttons = Ext.getCmp("numbered").menu.buttons; //where the dataURLs are pushed in another portion of the program
function btn(num) {
var image = new Image;
image.src = buttons[num].dataURL;
this.xtype = "button";
this.height = 50;
this.width = 50;
this.icon = image; //where putting an actual path works correctly, but this code doesn't
this.num = num;
this.handler = function (btn) {
btn.up("button").menu.Style = this.num;
btn.up("button").fireEvent("selected", this.num);
};
}
for (var i = 0; i <= 0; i++)
library.items.items.push(new btn(i));
},
I am aware the loop is only going thru index 0 - it's like that purposefully for testing.
SOLUTION
The selected correct answer did provide the right way to set the icon with a DataURI, but it wasn't the fix to my issue. Turns out instead of doing
library.items.items.push(new btn(i));
I needed to be doing
library.add(new btn(i));
The error I kept encountering with pushing was "c.render() is not a function". I mention that solely to make it hopefully searchable for anyone else who encounters that error.
Should be the same as data uri, you'll have to convert it first.
https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
var dataURL = canvas.toDataURL();
Here is a button fiddle:
https://fiddle.sencha.com/#view/editor&fiddle/1og6
I am having an interesting issue. The general idea of what I am doing is pulling data from a Firebase database, and populating a table based on that data. Everything runs perfectly during initial population--cells and rows are populated as they should be, but the weird issue is that the scripts seem to execute again randomly. I've logged the incoming data to the console, and can see it print twice after some amount of time.
This second execution does not happen if I am to navigate between pages, or reload the page--in either of those cases everything works as it should. The problem SEEMS to happen when I log back into my computer after locking it??? Does anybody have ANY idea what could be going on here? Relevant portion of script below:
const table = document.getElementById('myTable');
firebase.auth().onAuthStateChanged(firebaseUser => {
if (firebaseUser) {
let user = firebase.auth().currentUser;
let uid = user.uid;
const dbRef = firebase.database().ref().child("data/" + uid);
dbRef.once('value', snap => {
var dataCount = snap.child("secondData").numChildren();
var datalist = snap.child("secondData").val();
var dataArray = Object.keys(datalist).map(function(k) {
return datalist[k]
});
pullAllInfo(dataCount, dataArray);
});
}
});
function pullAllInfo(count, array) {
let k = 0;
let dataArray = [];
for (i = 0; i < count; i++) {
let specificRef = firebase.database().ref().child("secondData/" + array[i]);
specificRef.once('value', snap => {
var optionsTag = array[k];
k++;
var dataId = snap.child("id").val();
var dataName = snap.child("name").val();
var dataCount = snap.child("data").numChildren();
dataArray.push(dataId, dataName, dataCount, optionsTag);
if (k == count) {
buildTable(dataArray);
console.log(dataArray);
}
});
}
}
As you can see from the code above I AM calling .once() for each reference, which would prevent data duplication from the typical .on() call. Just cant seem to figure this one out. ALSO I have an iMac, just for anyone curious about my potential computer unlock diagnosis.
Thanks all!
Most likely, the auth state is changing and setting off your function. Try throwing a log under firebase.auth().onAuthStateChanged like this:
firebase.auth().onAuthStateChanged(firebaseUser => {
console.log( 'auth state changed', firebaseUser );
if (firebaseUser) {
My guess is that you'll see that the AuthState is changing when you log out/log in from your computer.
I solved this issue by creating another global boolean called preLoaded. At the beginning, it is set to false and, once the data is loaded and passed off to build the table, it is set to true. It now looks like this:
if(k == count && preloaded == false){
preloaded = true;
console.log(dataArray);
buildTable(dataArray);
}
All set!