Home > Back-end >  Firestore forEach returns one item
Firestore forEach returns one item

Time:12-25

I'm creating a project / to-do appliction with firestore. I want to return all the current projects where the active user has been assigned to.

In a console.log(doc.id, doc.data()), I get the two projects where he has been signed up for. But when I want to show them both on the home screen, I only see one project.

I don't know what I'm doing wrong Anyone that can help me?

My function:

const returnProjects = async () => {
  const list = document.querySelector<HTMLDivElement>('#projectList');
  const projects = query(collectionGroup(db, 'projects'));
  const querySnapshot = await getDocs(projects);
  querySnapshot.forEach((doc) => {
    const deadline = doc.data().Deadline;
    const fireBaseTime = new Date(
      deadline.seconds * 1000   deadline.nanoseconds / 1000000,
    );
    const formatOptions = {
      format: 'dd MMM  yy', 
    };
    console.log(doc.id, '>', doc.data());
    const newElemement = document.createElement('div');
    if (list) list.appendChild(newElemement).setAttribute('class', 'projectCard');
    const card = document.querySelector<HTMLDivElement>('.projectCard');
    if (card) { card.innerHTML = `
    <h4>${doc.id, doc.data().Name}</h4>
    <p>${fireBaseTime.toLocaleDateString('eng-BE', formatOptions)}</p>
    <span>3</span>
    `; }
  });
};

my Html:

    <main>
      <div id="dashboard" >
        <div class='dashboardUtils'>
          <h3 id="dashboardName"></h3>
          <span id="currentDate"></span>
        </div>
        <button id="editDashboard" ></button>
      </div>
      <div id='dashboardEdits-form' >
        <form id='dashboardEdits' class='edit-form'>
          <div id='practicalDisplayname'>
            <label for='displayname' class='form-label'>Username</label>
            <input type='text' class='form-input' id="displaynameInput" name='displayname'></input>
          </div>
        </form>
        <button id="confirmEdits" >Save edits</button>
      </div>
      <div id='amountMessage'>
        <h1 id='amountProjects'></h1>
      </div>
      <div id='projectList'></div>
    </main>

A screenshot: console.log returns two projects but renders one

CodePudding user response:

The problem is that for each document in the results you do:

const card = document.querySelector<HTMLDivElement>('.projectCard');
if (card) { card.innerHTML = `
<h4>${doc.id, doc.data().Name}</h4>
<p>${fireBaseTime.toLocaleDateString('eng-BE', formatOptions)}</p>
<span>3</span>
`; }

While you're creating a new project card for each result, the querySelector always returns the first card for the HTML. From the MDN documentation on querySelector:

An Element object representing the first element in the document that matches

So for the second document, you're replacing the innerHTML that you set for the first document.


To solve the problem, since you already have a reference to the element you just generated, add the innerHTML to that instead of looking it up with a query selector:

querySnapshot.forEach((doc) => {
  ...
  const newElement = document.createElement('div');
  if (list) list.appendChild(newElemement).setAttribute('class', 'projectCard');
  newElement.innerHTML = `
  <h4>${doc.id, doc.data().Name}</h4>
  <p>${fireBaseTime.toLocaleDateString('eng-BE', formatOptions)}</p>
  <span>3</span>
  `; 
})

CodePudding user response:

The problem you are facing here is caused by the following behavior:

https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild#:~:text=The appendChild() method of the Node interface adds a node to the end of the list of children of a specified parent node. If the given child is a reference to an existing node in the document, appendChild() moves it from its current position to the new position.

What this means is that appendChild will remove the node if already present in the DOM, which is what we are seeing here. This can be easily solved by creating a deep clone of the node first using cloneNode, before appending it to the target div.

  • Related