user3621357
user3621357

Reputation: 65

AngularJS not waiting for HTTP request to be done

Hello guys I have the following code here :

angular.module('todomvc')
.factory('todoStorage', function ($http) {
    'use strict';

    var STORAGE_ID = 'todos-angularjs';

    return {
        get: function () {
            $http({method: 'GET', url: '/api/todo.php'}).
            success(function(data, status, headers, config) {
              // this callback will be called asynchronously
              // when the response is available
              return JSON.stringify(data);
            }).
            error(function(data, status, headers, config) {
              // called asynchronously if an error occurs
              // or server returns response with an error status.
            });
        },

        put: function (todos) {
            debugger;
            localStorage.setItem(STORAGE_ID, JSON.stringify(todos));
        }
    };
});

as well as

angular.module('todomvc')
.controller('TodoCtrl', function TodoCtrl($scope, $routeParams, $filter, todoStorage, $http) {
    'use strict';

    var todos = $scope.todos = todoStorage.get();

    $scope.newTodo = '';
    $scope.editedTodo = null;

    $scope.$watch('todos', function (newValue, oldValue) {
        $scope.remainingCount = $filter('filter')(todos, { completed: false }).length;
        $scope.completedCount = todos.length - $scope.remainingCount;
        $scope.allChecked = !$scope.remainingCount;
        if (newValue !== oldValue) { // This prevents unneeded calls to the local storage
            todoStorage.put(todos);
        }
    }, true);

The issue i have is that the HTTP request is not completing before the code at $scope.$watch is being executed therefore it is calling .length on undefined. I am a total n00b to Angular and wanted to use this TodoMVC to get it working however im not sure what i can do to halt the whole process instead of wrapping the rest of the code within the success callback from the http request.

Thanks in advance

Upvotes: 0

Views: 1419

Answers (3)

andybn
andybn

Reputation: 56

$http is wrapped in a promise. You could return the promise from the service:

return $http({method: 'GET', url: '/api/todo.php'})

In the controller you can use then method to specify the behaviour when the request is completed:

 $scope.newTodo = '';
 $scope.editedTodo = null;

todoStorage.get().then(function(result) {

    $scope.todos = result.data;

    $scope.$watch('todos', function (newValue, oldValue) {
        $scope.remainingCount = $filter('filter')($scope.todos, { completed: false }).length;
        $scope.completedCount = todos.length - $scope.remainingCount;
        $scope.allChecked = !$scope.remainingCount;
        if (newValue !== oldValue) { // This prevents unneeded calls to the local storage
            todoStorage.put($scope.todos);
        }
    }, true);
 });

A good example about how to chain promises:

http://codepen.io/willh/pen/fCtuw

Upvotes: 0

Mukund Kumar
Mukund Kumar

Reputation: 23211

use this code:

angular.module('todomvc')
    .factory('todoStorage', function ($http) {
        'use strict';

    var STORAGE_ID = 'todos-angularjs';

    return {
        get: function () {
            return $http({method: 'GET', url: '/api/todo.php'})
        },

        put: function (todos) {
            debugger;
            localStorage.setItem(STORAGE_ID, JSON.stringify(todos));
        }
    };
});


angular.module('todomvc')
.controller('TodoCtrl', function TodoCtrl($scope, $routeParams, $filter, todoStorage, $http) {
    'use strict';

    $scope.newTodo = '';
    $scope.editedTodo = null;
     todoStorage.get()
         .then(function(d){
          $scope.todos=d;
     })
         .catch(function(e){
          console.error(e);
      })

    $scope.$watch('todos', function (newValue, oldValue) {
        $scope.remainingCount = $filter('filter')(todos, { completed: false }).length;
        $scope.completedCount = todos.length - $scope.remainingCount;
        $scope.allChecked = !$scope.remainingCount;
        if (newValue !== oldValue) { // This prevents unneeded calls to the local storage
            todoStorage.put(todos);
        }
    }, true);

Upvotes: 0

PSL
PSL

Reputation: 123739

Issue

  • #1 you need to return promise from your get method of your factory, and use $http.then instead of http's custom promise method success.
  • #2 you need to chain it though to assign value to the scope property.
  • #3 when you watch asyncronously assigned properties you need to have a null check because the watch is going to run when the controller is set up.
  • #4 I am not sure if you should do JSON.stringify the response because looks like you are in need of an array data?

In your factory

 return {
    get: function () {
      return  $http({method: 'GET', url: '/api/todo.php'}). //Return here
        then(function(response) { //
            return response.data;
        }, function(response) {
          // called asynchronously if an error occurs
          // or server returns response with an error status.
        });
    },

In your controller:-

   var todos;
   todoStorage.get().then(function(data) {
       todos = $scope.todos = data
   });

and

   $scope.$watch('todos', function (newValue, oldValue) {
       if(!newValue) return;
        $scope.remainingCount = $filter('filter')(todos, { completed: false }).length;
        $scope.completedCount = todos.length - $scope.remainingCount;
        $scope.allChecked = !$scope.remainingCount;
        if (newValue !== oldValue) { // This prevents unneeded calls to the local storage
            todoStorage.put(todos);
        }
    }, true);

Upvotes: 2

Related Questions