hummmingbear
hummmingbear

Reputation: 2384

Deferred Promises with AJAX in Angular

I'm trying to send data to my view from an AJAX call to my API. I am able to successfully hit my API and get data, but I was having problems with the view rendering before the AJAX call came back.

I'm trying to wrap my AJAX call in a Promise but it's not working. Here's my layout

Controller

.controller('DashCtrl', function($scope, Tweets) {
  $scope.tweets = Tweets.all()
})

Factory doing ajax call

.factory('Tweets', function($http) {
  $http.get('http://localhost:3000/tweets')
    .success(function(data) {
      var tweets = data
      debugger
  })

  return {
    all: function() {
      //should return the results of the AJAX call when it's complete
    }
  }
});

I've tried making wrapping the ajax call into a function and using .then(function(payload){ return payload.data }) - Payload.data has my data but its never returned when I call the function. I'm new to angular, so I would appreciate any help or insight.

Upvotes: 0

Views: 587

Answers (3)

Rebornix
Rebornix

Reputation: 5270

You should define your factory as

.factory('Tweets', function($http) {
  return {
    all: function() {
      return $http.get('http://localhost:3000/tweets')
        .then(function(response) {
          return reponse.data;
      })
    }
  }
});

Then change your controller to

.controller('DashCtrl', function($scope, Tweets) {
  Tweets.all().then(function(data) {
    $scope.tweets = data;
  });
})

Upvotes: 2

Loading data with ngResource or from factory promise callback are viable options, but there's one more way nobody mentioned yet: resolve data to controller via route definition. This approach allows to write simplistic controllers that don't know how to load data at all. In most cases it will be more than enough if you don't need to load data dynamically, like pagination or infinite scroll.

You will need to define route and resolve function:

angular
.module('app', ['ngRoute'])
.config(function ($routeProvider) {
  $routeProvider
  .when('/', {
    controller: 'ctrl',
    controllerAs: 'view',
    templateUrl: 'view.html',
    resolve: {
      tweets: function (Tweets) {
        return Tweets.all();
      }
    }
  })
})

The tweets property on resolve will inject loaded data into controller as tweets, all you have to do is just assign received data:

.controller('ctrl', function (tweets) {
  this.tweets = tweets;
});

In addition, here's how Tweets service might look like:

.factory('Tweets', function ($timeout) {
  function all () {
    return $timeout(function () {
      return ["hey", "there"];
    });
  }
  return {
    all: all
  };
})

Basically, it exposes methods that return promise, returning some data ($timeout returns promise too, so I've used it instead of $http for example purpose).

Full example on JS Bin.

Upvotes: 1

AJcodez
AJcodez

Reputation: 34166

Use the $resource service. The docs don't mention it, but comments in the source do.

$resolved: true after first server interaction is completed (either with success or rejection), false before that.

So in the controller:

$scope.tweets = $resource('/tweets').query()

And in the view:

<div ng-if="tweets.$resolved">

Upvotes: 1

Related Questions