Question

Here's my View

<div ng-repeat="blog in blogs">
<h3>{{blog.title}}</h3>
<h4>{{blog.post}}</h4>
</div>

My Controller Code:

   demoApp.controller('myController', ['$scope', 'blogService', function ($scope,   
    blogService)
    {
        $scope.blogs = blogService.getBlogs();
    }]);

My code in the service. This includes asynchronous call to Parse.com javascript sdk.

this.getBlogs = function () 
{        
   var BlogPost = Parse.Object.extend("BlogPost");
   var blogPost = new BlogPost();
   var blogs  = [];
    var query = new Parse.Query(BlogPost);
    query.find({
        success: function(results) {
            console.log(results[0].get("title"));
            for (var i = 0; i < results.length; i++)
            {
                blogs.push(
                    {
                        title : results[i].get("title"),
                        post : results[i].get("post")
                    });
            }
            console.log(blogs);
        },
        error: function(error) {
            alert("Error: " + error.code + " " + error.message);
        }
    });
    return blogs;
}; });

The console log console.log(blogs); is showing returned data perfectly. Just the view is not getting updated after this value is fetched.

Was it helpful?

Solution

The code in the callbacks executes "outside of Angular's world", which means the $digest loop will not be triggered and changes will not be reflected in the DOM.

You need to use $apply:

$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). Because we are calling into the angular framework we need to perform proper scope life cycle of exception handling, executing watches.

Inject $rootScope in your service and for example:

success: function(results) {
  $rootScope.$apply(function () { 
    console.log(results[0].get("title"));
    for (var i = 0; i < results.length; i++)
    {
        blogs.push(
            {
                title : results[i].get("title"),
                post : results[i].get("post")
            });
    }
    console.log(blogs);
  });
},

OTHER TIPS

Your "success" callback executes asynchronously and angular doesn't know when it happens. To inform angular about the success callback you could wrap it in $scope.$apply

success: function(results) {
    $scope.$apply(function() {
        console.log(results[0].get("title"));
        for (var i = 0; i < results.length; i++)
        {
            blogs.push(
                {
                    title : results[i].get("title"),
                    post : results[i].get("post")
                });
        }
        console.log(blogs);
    });
}

Make your Service return Promise!

.factory('factoryReturningPromise', function (){
   ...
  return query.find()

  .then(function(results){
     ...
    // make sure the return value is a resolved promise
    return Parse.Promise.as(blog);
  },

  function(error){

    // we are here because of query error - return rejected promise
    return Parse.Promise.error(error);
  });
})

Then you only need to wrap it into local $scope.$apply inside your controller:

.controller('myController', function ($scope, factoryReturningPromise) {
   $scope.$apply(function(){

      blogService.getBlogs()

      .then(function(blogs){

        $scope.blogs = blogs;
      }, function (error) {

        ... and deal with errors ... show bad news to user etc
      });
   });
})

That way only local digest loops with all the watches will be checked.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top