Pregunta

I am using ui-router 0.2.0 (I know it is an older version but we are locked on Angular 1.0.8 for various reasons) and trying to use resolve: {} to ensure controllers are dealing with concrete data and not eventual data (promises).

I currently have the following:

resolve: {
    items: ['listSvc', function (listSvc) {
        console.log('resovling items');
        var items = listSvc.getItems();
        items.then(function () {
            console.log('horray!');
        });
        return items;
    }]
}

Items contains about eight items right now just with a title and my view just does a ng-repeat and displays each title.

The controller:

App.controller('MyCtrl', ['$scope', 'items', function ($scope, items) {
    console.log('MyCtrl');
    $scope.items = items;
}]);

The view:

<div>
    <h1>Items</h1>
     <div data-ng-repeat="item in items">
        {{ item.Title }}
    </div>
</div>

Sometimes I am seeing approximately 2 seconds elapse between when I see 'horray!' and the resulting view. Other times I see 'horray!' but never transition to the expected view. How do I troubleshoot/fix this?

My listSvc is also using Q rather than $q if that matters.

Edit: moved question to the bottom for clairity

Edit:

While the selected answer worked for all my jsbin testing and is correct according to all documentation I have found. This is what fixed my actual problem, I know its nasty and awful, but it is the only thing I could get to work for our application. In the example below, my listSvc is using Q

    resolve: {
        items: ['listSvc', 'currentUser', '$q', '$rootScope', function (listSvc, currentUser, $q, $rootScope) {
            console.log('my => items');
            var deferred = $q.defer();
            listSvc.setUser(currentUser);
            listSvc.getMyItems().then(function (myItems) {
                deferred.resolve(myItems);
                return myItems;
            }).fin(function () {
                $rootScope.$apply();
            });
            return deferred.promise;
        }]
    }
¿Fue útil?

Solución

My listSvc is also using Q rather than $q if that matters.

Yes, yes it does. This is your problem.

Q is a much more complete promise implementation than $q, it has a lot more features. However $q integrates into the AngularJS digest cycle.

  • Informally - it notifies the presentation that something has changed and it needs to refresh.
  • Formally - Q resolves promises using the asap.js module for fast queuing (recently), and $q resolves promises on the evalAsync queue. The informal part is more important here though.

My suggestion would be instead of using tricks like a scope apply in a .finally uniformally - use $q.when(prom) which is a utility method that takes a thenable (generic promise) and turns it into a trusted $q promise. If you don't have anything that runs in $q, you'll still need to call $rootScope.$apply() at the end of your promise chain.

If that doesn't work, you can simply add

.finally(function(){ $scope.apply(); }) 

To the end of your promise chains.

Most good promise libraries implement the A+ specification that allows clean and simply assimilation of one kind of promise into another. Since both Q and $q are Promises/A+ complaint - this means you can interop between Q and $q very easily (since they have similar .then semantics):

when(value) - Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted.

In your case - something like:

var items = $q.when(listSvc.getItems()); // don't forget to inject $q

Or wrap it in a when at any other point in the chain.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top