简体   繁体   中英

Angular Controller Loading Before Data Is Loaded

I am writing a simple app that loads movie info from an API. After this loading, I am attempting to use Angular to display the movies in a simple list view. I am correctly loading the movies, but it seems like the angular controller is created and sends the movie array to the view before the movie array is populated. I am unsure how to get around this.

var movieList = [];
var app = angular.module('top250', []);

// immediately make a call to the server to get data (array of movies from text file)
$.post('/', {}, function(data) {
    init(data);
});

app.controller('MovieController', function() {
    // should be setting this.movies to an array of 250 movies
    this.movies = movieList;
});

function init(data) {
    // cycle through array, use title to retrieve movie object, add to array to be sent to view

    $.each(data, function(index, value) {
        var title = value.split(' (')[0];
        title = title.split(' ').join('+');
        var url = 'http://www.omdbapi.com/?t=' + title + '&y=&plot=short&r=json';

        $.getJSON(url, function(data) {
            console.log('in get json', data);
            var movieObj = data;
            storeMovie(movieObj);
        });

    });
}

function storeMovie(movieObj) {
    movieList.push(movieObj);
}

And my HTML (although I'm certain this isn't the problem:

<body ng-controller="MovieController as MovieDB">

  <div class="row">
    <div class="large-12 columns">
      <h1>IMDB Top 250 List</h1>
    </div>
  </div>

  <div class="row">
    <div class="large-12 columns" id="movie-list">

    <div class="list-group-item" ng-repeat="movie in MovieDB.movies">
      <h3>{{movie.Title}} <em class="pull-right">{{movie.Plot}}</em></h3>


    </div>
  </div>

  <script src="js/foundation.min.js"></script>
  <script>
    $(document).foundation();
  </script>
</body>

First I transformed your ajax calls to an angular factory:

app.factory('MoviesService', function($http, $q) {

  function getMovie(value) {
    var title = value.split(' (')[0];
    title = title.split(' ').join('+');
    var url = 'http://www.omdbapi.com/?t=' + title + '&y=&plot=short&r=json';
    return $http.get(url).then(function(res){ return res.data; });
  }

  return $http.post('/').then(function(res) {
    return $q.all(res.data.map(getMovie));
  });
});

Then I can consume it like so:

app.controller('MovieController', function(MoviesService) {

    var self = this;

    MoviesService.then(function(movies) {
      self.movies = movies;
    });
});
  1. don't use jquery
  2. use angular $http or $resource
  3. using $http, you set scope var to the data inside promise, and life will be good

You need to wait for you init method to complete:

function init(data, complete) {

    $.each(data, function(index, value) {
        var title = value.split(' (')[0];
        title = title.split(' ').join('+');
        var url = 'http://www.omdbapi.com/?t=' + title + '&y=&plot=short&r=json';

        $.getJSON(url, function(data) {
            console.log('in get json', data);
            var movieObj = data;
            storeMovie(movieObj);

        }).always(function(){ // count competed ajax calls,
                              // regardless if they succeed or fail
            if(index === data.length -1)
                complete(); // call callback when all calls are done
        });

    });


}

Now you can do this:

app.controller('MovieController', function() {
    $.post('/', {}, function(data) { 
        init(data, function(){
            this.movies = movieList;
        });
    });
});

Personally I would just keep the movieList inside of the init method and send it with the callback when you're done, but that's just a preference.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM