1

I am fairly new to AngularJS and I am practising below exercise with requirement

1.Using the API to get 20 posts and display them on the page along with the user’s name who created the post and display them on the page. For this exercise I am using https://jsonplaceholder.typicode.com/ as the data source. I need to do 2 api calls in same controller

  1. To get list of 20 posts which has userid in it(https://jsonplaceholder.typicode.com/posts)

  2. Based on the above user Id I need to get username (https://jsonplaceholder.typicode.com/users/userId) Please see my work done in plnkr, I am able to display Post but not username.

    Script.js

    var app = angular.module('myApp', []);
    app.controller('myCtrl', function($scope, $http) {
        $http.get("https://jsonplaceholder.typicode.com/posts").then(function(response) {
            $scope.data = response.data;
            var postList = [];
            for (var i = 0; i < 20; i++) {
                var display = {
                    UserName: $http.get("https://jsonplaceholder.typicode.com/users/" + $scope.data[i].userId).then(function(response) {
                        $scope.user = response.data;
                    }),
                    Post: $scope.data[i].title
                }
                postList.push(display);
            }
            $scope.list = postList;
        });
    });
    

    Index.html

    <div ng-repeat="x in list">
        Post:{{ x.Post }}
        UserName:{{x.UserName}}
    </div>
    
1
  • Where is plunker link? Commented Sep 4, 2017 at 23:58

2 Answers 2

1

I believe this area is wrong:

.then(function(response) {
  $scope.data = response.data;
  var postList = [];
  for (var i = 0; i < 20; i++) {

    var display = {
      UserName: $http.get("https://jsonplaceholder.typicode.com/users/"+$scope.data[i].userId).then(function(response){
         $scope.user = response.data;
      }),
      Post: $scope.data[i].title
    }
    postList.push(display);
  }
  $scope.list = postList;
});

where you stored a Promise object in your UserName property and produced unexpected result.

to correct this assign the postList after the request has finished:

.then(function(response) {
  $scope.data = response.data;
  var postList = [];

  for (var i = 0; i < 20; i++) {

    $http.get("https://jsonplaceholder.typicode.com/users/"+$scope.data[i].userId).then(function(response){
      $scope.user = response.data;

      var display = {
        UserName: "",
        Post: $scope.data[i].title
      };

      $scope.list.push(display);
    });
  }

  $scope.list = postList;
});

Once you implemented this you will encounter a new problem:

since you called $http.get() in a loop and actually used the variable i inside .then() by the time .then() executes the value of i is already in its final form which is i = 20 | data.length which every .then() calls will receive.

in order to overcome this problem the best way I know is to format the entire data first before displaying it:

$http.get("https://jsonplaceholder.typicode.com/posts")
  .then(function(response)
  {
    var data     = response.data;
    var postList = [];
    // this will check if formatting is done.
    var cleared  = 0;

    // create a function that checks if data mapping is done.
    var allClear = function () {
      if (postList.length == cleared)
      {
        // display the formatted data
        $scope.list = postList;
      }
    };

    for (var i = 0; i < data.length; i++)
    {
      // create a object that stores the necessary data;
      var obj = {
        // the ID will be needed to store name;
        ID: data[i].userId,
        Post: data[i].title,
        UserName: ""
      };
      var url = "https://jsonplaceholder.typicode.com/users/" + obj.userId;

      $http.get(url).then(function(response)
      {
        // find its entry in the array and add UserName;
        postList.forEach(function (item)
        {
          if (item.ID == response.userId)
          {
            // just add the correct key, but I will assume it is `userName`
            item.UserName = response.userName;
            // break the loop
            return item;
          }
        });

        // increment cleared
        cleared++;
        // call allClear
        allClear();
      });

      postList.push(obj);
    }
  }
);

in this way we are sure that the data is complete before displaying it in the view.

as this solution contains a loop to map the result with its original object, we can actually change postList as an object to make it a bit faster:

// var postList = [];
var postList = {};

// instead of pushing we will use the ID as key
// postList.push(obj);
postList[obj.ID] = obj;

and so in this section:

  $http.get(url).then(function(response)
  {
    // instead of looking for the item in .forEach
    postList[response.userId].userName = response.userName;
    // increment cleared
    cleared++;
    // call allClear
    allClear();
  });

hope that helps.

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

1 Comment

Thanks for the solution and it worked as expected.@masterpreenz
1

The easy solution would be to add the username to the user object and then push it to the scope list when the promise is resolved

var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http) {
    $http.get("https://jsonplaceholder.typicode.com/posts").then(function(response) {
        $scope.data = response.data;
        $scope.list = [];
        for (var i = 0; i < 20; i++) {

            $http.get("https://jsonplaceholder.typicode.com/users/" + $scope.data[i].userId)
                .then(function(response) {
                    var user = {
                        UserName: response.data.username,
                        Post: $scope.data[i].title
                    }
                    $scope.list.push(user);
                });
        }
    });
});

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.