Prashant Agrawal
Prashant Agrawal

Reputation: 670

How can I check whether array of objects is empty or not?

var app = angular.module('app',['ui.bootstrap']);

app.controller("ListCtrl", function ($scope, $http) {

    $scope.submit = function () {
        $scope.loading = true;
        $scope.error = false;
        $http.get('http://www.omdbapi.com/?s=' + $scope.search + '&r=json')
               .then(function (res) {
                    var titles = [];
                    angular.forEach(res.data.Search, function(item){
                       $http.get('http://www.omdbapi.com/?t=' + item.Title + '&y=&plot=full&r=json').then(function(res){
                       if (res.data.Poster === "N/A") {
                         res.data.Poster = "http://placehold.it/350x450/FF6F59/FFFFFF&text=Image+not+Available!!";
                       }
                        titles.push(res.data); 
                      });  
                    });

                   $scope.movie = titles;
                   $scope.results = true;
                   $scope.error = false;
                   $scope.loading = false;


                   if (titles.length==0) {           // not working
                       $scope.results = false;
                       $scope.error = true;
                   }


               })

I have been tried several things like :

Object.getOwnPropertyNames(titles).length === 0) 
obj == null 

None of them seems to work...

Upvotes: 2

Views: 128

Answers (4)

LetterEh
LetterEh

Reputation: 26696

Just as an aside, but beneficial for my practice and your future help:

Part of the problem you were having was scope-management (JS-scope, not Angular $scope), part of it was concurrency-management, and part of it appeared to be plain old scope-formatting making it hard to see where all control blocks start and end (that gets miserable when it's not just if/else, but with callbacks/promises, too).

This is a small example of how you might consider tackling these problems, through a quick refactor of your issues:

function pluck (key) {
  return function pluckFrom(obj) { return obj[key]; };
}

angular.module("app", ["ui.bootstrap"]);

angular.moule("app").service("omdbService", ["$http", function ($http) {
  function getSearch (search) {
    var searching = $http.get("http://www.omdbapi.com/?s=" + search + "&r=json")
      .then(pluck("data"));
    return searching;
  }

  function getMovie (title) {
    var searching = $http.get("http://www.omdbapi.com/?t=" + title + "&y=&plot=full&r=json")
      .then(pluck("data"));
    return searching;
  }

  return {
    getSearch: getSearch,
    getMovie: getMovie,
    getPlaceholderPoster: function () { return "http://placehold.it/350x450/FF6F59/FFFFFF&text=Image+not+Available!!"; }
  };
}]);



angular.moule("app").controller("ListCtrl", ["$scope", "$q", "omdbService", function ($scope, $q, omdb) {
  function loadMovie (movie) {
    return omdb.getMovie(movie.Title)["catch"](function () { return undefined; });
  }
  function movieExists (movie) { return !!movie; }
  function updatePoster (movie) {
    movie.Poster = movie.Poster || omdb.getPlaceholderPoster();
    return movie;
  }
  function setResults (movies) {
    $scope.movie = movies; // $scope.movies, instead?
    $scope.results = true;
    $scope.error = false;
    $scope.loading = false;
  }

  function handleError () {
    $scope.results = false;
    $scope.error = true;
  }

  $scope.submit = function () {
    $scope.loading = true;
    $scope.error = false;

    omdb.getSearch($scope.search)
      .then(pluck("Search"))
      .then(function (movies) { return $q.all(movies.map(loadMovie)); })
      .then(function (movies) { return movies.filter(movieExists).map(updatePoster); })
      .then(setResults, handleError);
  };

}]);

There are 8000 valid ways of handling this, and everyone will see it a little differently.
This is also not really how I'd tackle it in production, but not too far off...

Moving all endpoint-calls out to a service which is responsible for those means that any controller in your system (with that module as a dependency) can access them.

Doing small things per function and letting Array.prototype methods do the iterating (IE8 can be shimmed if needed) means that each function is super-specific.

By wrapping the controller/service functions in arrays, and naming their dependencies, they're now minification friendly.

The body of submit() is less than 10 lines, and deals with all kinds of crazy async stuff, but I know that I've handled errors like one of the movies returning a 404 (my code should still fire, with the remaining movies, the code of others might not -- most code would either never trigger success or would fail all the way through the program, if the server threw an error for a movie). Now, I'm not checking that the server is sending the right kind of data for a "movie", but that's different.

Upvotes: 0

Vijay
Vijay

Reputation: 3023

This is happening because of incorrect scope:

var titles = []; is defined inside the .then

and you are checking the length outside of .then

since titles is not available outside .then it would not work. (undefined.length==0)

Solution:

.then(function (res) {
                    var titles = [];
                    angular.forEach(res.data.Search, function(item){
                       $http.get('http://www.omdbapi.com/?t=' + item.Title + '&y=&plot=full&r=json').then(function(res){
                       if (res.data.Poster === "N/A") {
                         res.data.Poster = "http://placehold.it/350x450/FF6F59/FFFFFF&text=Image+not+Available!!";
                       }
                        titles.push(res.data); 
                      }); 
               $scope.movie = titles;
               $scope.results = true;
               $scope.error = false;
               $scope.loading = false;


               if (titles.length==0) {           // now this will work
                   $scope.results = false;
                   $scope.error = true;
               }  
    });//titles will not be available after this.

Upvotes: 2

Miguel
Miguel

Reputation: 20633

$http.get() is async so the statement if (titles.length==0) { gets executed right away.

Have a counter to determine when all the Promises get resolved and then perform the check. Move the if statement inside the callback.

 var count = res.data.Search.length;

 angular.forEach(res.data.Search, function(item){
   $http.get('http://www.o....rest of code').then(function(res) {
      // rest of code

      titles.push(res.data); 
      if (!count-- && !titles.length) {
          $scope.results = false;
          $scope.error = true;
        }
      }
    });
 });

Upvotes: 0

keno
keno

Reputation: 93

In your case, the check

titles.length 

will be executed before the

titles.push

because you use an asynchronous request which will return later. You need to wrap your statements into the answer-block of the request.

Upvotes: 0

Related Questions