0

We just started 3. semester two weeks ago and are learning basic event/Dom/fetch manipulation.

I am supposed to fetch data from https://jsonplaceholder.typicode.com/users and get all names and phone numbers.

So here is what I did:

/* 
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

var userID = document.getElementById("userID");
const url1 = "https://jsonplaceholder.typicode.com/users/";
const url2 = "https://jsonplaceholder.typicode.com/users";
//JSON.stringify(data)
function getUser() {
  fetch(url1 + userID.value)
    .then(res => res.json())
    .then(data => {
      var all = "<p>" + "Name: " + data.name + "</p>";
      all += "<p>" + "Phone: " + data.phone + "</p>";

      document.getElementById("singleUser").innerHTML = all;
    });
}

//***************Code that needs look on is right here ********************

function getAllUsers() {
  fetch(url2)
    .then(res => res.json())
    .then(data => {
      for (var i = 0; i < data.length; i++) {
        console.log(data[i]);
        var all = "<p>" + "Name: " + data[i].name + "</p>";
        all += "<p>" + "Phone: " + data[i].phone + "</p>";
        document.getElementById("allUsers").innerHTML = all;
      }

      //for (var key in data) {
      //    if(data.hasOwnProperty(key)) {
      //        document.getElementById("allUsers").innerHTML = data[key].name;
      //    }
      //}

      data.forEach(function(key) {
        var users = "<p>" + "Name: " + key.name + "</p>";
        users += "<p>" + "Phone: " + key.phone + "</p>";
        document.getElementById("allUsers").innerHTML = users;
        console.log(key.name);
      });
    });
}
<h1>Hello World!</h1>
<h1>Test if deployed on Tomcat via Travis Ci</h1>
<h6>Calculator Client</h6>

<input type="number" id="firstNumber"><br>
<input type="number" id="secondNumber"><br>
<div id="operations">
  <button name="opr">+</button>
  <button name="opr">-</button>
  <button name="opr">X</button>
  <button name="opr">/</button>
  <br>
</div>
<button id="clear">Clear</button>
<p id="result"></p>

<h6>Dynamic UI manipulation with data obtained via fetch</h6>

<input type="number" id="userID">
<button onClick="getUser()">Get user</button>
<button onClick="getAllUsers()">Get all</button>
<br>
<div id="singleUser"></div>
<div id="allUsers"></div>

<script src="calculator.js" type="text/javascript"></script>
<script src="fetch.js" type="text/javascript"></script>

As you can see I managed to get the single users, which wasn't a problem, but trying to get multiple data rows seems to be a hassle to me. I tried 3 different ways all of them keep only giving me the last person in the array, though if I print it to the console it gives me all of it.

Please help me.

1
  • It's because you keep replacing the content of allUsers with HTML representing a single user. You might want to add the new content each time through the loop instead of replacing. Commented Feb 13, 2019 at 17:37

5 Answers 5

2

You need to initialize your result string outside the loop and set the value of the HTML after the loop is finished.

/* 
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

var userID = document.getElementById("userID");
const url1 = "https://jsonplaceholder.typicode.com/users/";
const url2 = "https://jsonplaceholder.typicode.com/users";
//JSON.stringify(data)
function getUser() {
  fetch(url1 + userID.value)
    .then(res => res.json())
    .then(data => {
      var all = "<p>" + "Name: " + data.name + "</p>";
      all += "<p>" + "Phone: " + data.phone + "</p>";

      document.getElementById("singleUser").innerHTML = all;
    });
}

//***************Code that needs look on is right here ********************

function getAllUsers() {
  fetch(url2)
    .then(res => res.json())
    .then(data => {
      // Traditional for loop
      var all = "";
      for (var i = 0; i < data.length; i++) {
        //console.log(data[i]);
        all += "<p>" + "Name: " + data[i].name + "</p>";
        all += "<p>" + "Phone: " + data[i].phone + "</p>";
      }
      document.getElementById("allUsers").innerHTML = all;

      // Enhanced for loop
      var users = "";
      data.forEach(function(key) {
        users += "<p>" + "Name: " + key.name + "</p>";
        users += "<p>" + "Phone: " + key.phone + "</p>";
      });
      document.getElementById("allUsers").innerHTML = users;

      // Mapped to string
      document.getElementById("allUsers").innerHTML = users.map(user => {
        return "<p>" + "Name: " + user.name + "</p>" +
               "<p>" + "Phone: " + user.phone + "</p>";
      }).join(""); 
    });
}
<h1>Hello World!</h1>
<h1>Test if deployed on Tomcat via Travis Ci</h1>
<h6>Calculator Client</h6>

<input type="number" id="firstNumber"><br>
<input type="number" id="secondNumber"><br>
<div id="operations">
  <button name="opr">+</button>
  <button name="opr">-</button>
  <button name="opr">X</button>
  <button name="opr">/</button>
  <br>
</div>
<button id="clear">Clear</button>
<p id="result"></p>

<h6>Dynamic UI manipulation with data obtained via fetch</h6>

<input type="number" id="userID">
<button onClick="getUser()">Get user</button>
<button onClick="getAllUsers()">Get all</button>
<br>
<div id="singleUser"></div>
<div id="allUsers"></div>

<script src="calculator.js" type="text/javascript"></script>
<script src="fetch.js" type="text/javascript"></script>

Sign up to request clarification or add additional context in comments.

Comments

2

All your problem are here (normally): document.getElementById("allUsers").innerHTML = all;

The line document.getElementById("allUsers").innerHTML allow you to directly have access to the string content of the DOM element. In your case you need to append multiple row to this DOM, so you just need to replace the affectation to a append affectation. Simply change = to +=.

Here are the modification you need to do:

for (var i = 0; i < data.length; i++) {                   
    console.log(data[i]);
    var all = "<p>" + "Name: " + data[i].name + "</p>";
    all += "<p>" + "Phone: " + data[i].phone + "</p>";
    document.getElementById("allUsers").innerHTML += all;
} 

You can also do a more elegent solution:

let html = "";

for (let i = 0; i < data.length; i++) {                   
    console.log(data[i]);

    html += "<p>" + "Name: " + data[i].name + "</p>";
    html += "<p>" + "Phone: " + data[i].phone + "</p>";
}

document.getElementById("allUsers").innerHTML = html;

I don't have tested the code, but it should work!

Just for your knowledge, you should also learn where to use var and let:

  • var should be used for global variables because they can be reached from anywhere with window.hello (if you variable has "hello" has name).
  • let should be used for local variables because they only can be reached in the same scope.

1 Comment

Got a lot of answers, all with same or similar answers, thanks for bonus info :)
1

It's a simple logic error, related to how you output the data. You have put:

document.getElementById("allUsers").innerHTML = users;

within your loop. This means that every time your loop runs, it sets the value of "allUsers" to the users string, which replaces anything which was in that space previously. Additionally, each time your loop runs, you also reset the users string and only include the user fetched from the current loop iteration into it. By doing both these things you discard all the previous data. This happens fast enough that all you actually see on screen is the last one (even though, technically, all the others were displayed for a nanosecond before being over-written!).

You just need to make sure users persists for the whole lifetime of the loop (and that you append to it rather than over-writing it), and that you don't try to put that data into your <div> until you've collected all of it.

Here's a fixed version:

/* 
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

var userID = document.getElementById("userID");
const url1 = "https://jsonplaceholder.typicode.com/users/";
const url2 = "https://jsonplaceholder.typicode.com/users";
//JSON.stringify(data)
function getUser() {
  fetch(url1 + userID.value)
    .then(res => res.json())
    .then(data => {
      var all = "<p>" + "Name: " + data.name + "</p>";
      all += "<p>" + "Phone: " + data.phone + "</p>";

      document.getElementById("singleUser").innerHTML = all;
    });
}

//***************Code that needs look on is right here ********************

function getAllUsers() {
  fetch(url2)
    .then(res => res.json())
    .then(data => {
      var users = ""; //declare empty string
      data.forEach(function(key) {
        //append to the string
        users += "<p>" + "Name: " + key.name + "</p>";
        users += "<p>" + "Phone: " + key.phone + "</p>";
        console.log(key.name);
      });
      //wait until the string contains everyone before we add it to the page!
      document.getElementById("allUsers").innerHTML = users;
    });
}
<h1>Hello World!</h1>
<h1>Test if deployed on Tomcat via Travis Ci</h1>
<h6>Calculator Client</h6>

<input type="number" id="firstNumber"><br>
<input type="number" id="secondNumber"><br>
<div id="operations">
  <button name="opr">+</button>
  <button name="opr">-</button>
  <button name="opr">X</button>
  <button name="opr">/</button>
  <br>
</div>
<button id="clear">Clear</button>
<p id="result"></p>

<h6>Dynamic UI manipulation with data obtained via fetch</h6>

<input type="number" id="userID">
<button onClick="getUser()">Get user</button>
<button onClick="getAllUsers()">Get all</button>
<br>
<div id="singleUser"></div>
<div id="allUsers"></div>

<script src="calculator.js" type="text/javascript"></script>
<script src="fetch.js" type="text/javascript"></script>

Comments

1

In your loop, setting innerHTML replaces the content each time. If you want to append, consider creating new nodes.

// Create new paragraph
var name = document.createElement("p");
// Set text content to name value
name.appendChild(document.createTextNode(data[i].name));
// Append paragraph to #allUsers element
document.getElementById("allUsers").appendChild(name);
// Create new paragraph
var phone = document.createElement("p");
// Set text content to phone value
phone.appendChild(document.createTextNode(data[i].phone));
// Append paragraph to #allUsers element
document.getElementById("allUsers").appendChild(phone);

2 Comments

This seems a bit too much to do compared to other answers, but thanks for an alternative way to do it :)
@JesperChristensen keep in mind if you want to set HTML code directly make sure to sanitize your input so you don't get pwned.
1

You have a problem in your loop. See the below code which take the appending to the DOM out of that loop.

  var users = ""; // So that you will not initialize it every time you iterate the loop
  data.forEach(function(key) {
    users += "<p>" + "Name: " + key.name + "</p>";
    users += "<p>" + "Phone: " + key.phone + "</p>";        
    console.log(key.name);
  });
  document.getElementById("allUsers").innerHTML = users; // So that you will change the DOM/ UI only once with all the date.

Analyse:

Since you are initializing and changing your users variable in each iteration it will only have the value for that iteration. That is why you ended up having the last persons name. (This will be corrected if you are appending to a different DOM location each time, but too complex)

1 Comment

Thank you so much, it all makes sense now :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.