Resolving breeze query/Q promise with $route resolve stops page

烂漫一生 提交于 2019-12-21 21:26:48

问题


I'm having problems executing a breeze query with angular resolve before the view is rendered. I'm trying to get some data from the server before the view is rendered with breeze. I'm using

$routeProvider.when('/countries', { templateUrl: 'App/partials/countries.html', controller: Ctrl, resolve: Ctrl.resolve }).

controller and service snippets:

function Ctrl($scope, Q, datacontext, countries) {

//...

}

function getCountries(forceRefresh) {

            var query = entityQuery.
                from("countries").
                orderBy("name");

            return manager.executeQuery(query).
            then(getSucceeded);

        }

        function getSucceeded(data) {
            return data.results;
        }

this makes my view never render:

Ctrl.resolve = {
    countries: function (datacontext) {
        return datacontext.getCountries();
    }
}

whereas if I create a timer that takes longer time, it works. I've tried wrapping it with $q but I can't seem to get it to work.

this will render the view because of the timeout:

Ctrl.resolve = {
    countries: function (datacontext) {
        return datacontext.getCountries();


    },
    delay: function ($q, $timeout) {
        var delay = $q.defer();
        $timeout(delay.resolve, 6000);
        return delay.promise;
    }
}

If anyone can help me with this, it would be great. I'm not sure if I'm doing something wrong or if there are limitations with Q promises or breeze in resolve.


回答1:


I feel your pain. One of the mistakes in the $q/route resolve implementation is that the resolve does not honor the Q.then method. That violates the generally accepted practice of supporting interchangeable promise implementations (sigh).

But that's not the real problem. The real problem is that Angular doesn't know when your async method is resolved. You have to call $rootScope.$apply manually in both success and failure callbacks. Angular's $http and $q do this (or equivalent) for you. Getting it right yourself is a PITA.

As I said, we feel your pain. Very soon we will release a tiny Q plug-in that converts a Q promise to $q promise and should alleviate that pain. At least it does for me :-).

In a nutshell, find a place near where your app starts and extend Q something like this:

angular.module('app').factory('util', ['$q', '$rootScope', util]);

function util($q, $rootScope) {

   extendQ();

   ... other utilities here ...

   // Monkey patch to$q method into Q.js' promise prototype
   // ex usage: return manager.executeQuery(qry).to$q(succ, fail);
   function extendQ() {
       var promise = Q.defer().promise;
       var fn = Object.getPrototypeOf(promise);
       if (fn.to$q) return; // already extended
       fn.to$q = function (success, fail) {
           return to$q(this, success, fail);
       };
   }

   function to$q(qPromise, success, fail) {
       var d = $q.defer();
       qPromise
           .then(function (data) {
               if (data === undefined) {
                   logger.logError("Programming error: no data. " +
                   "Perhaps success callback didn't return a value or " +
                    "fail callback didn't re-throw error");
                   // If an error is caught and not re-thrown in an earlier promise chain
                   // you will arrive here with data === undefined. 
                   // Neglecting to re-throw is a common, accidental omission.
                   // To be safe, have every success callback return something
                   // and trap here if data is ever undefined
               }
               d.resolve(data);
               $rootScope.$apply();// see https://groups.google.com/forum/#!topic/angular/LQoBCQ-V_tM
           })
          .fail(function (error) {
              d.reject(error);
              $rootScope.$apply();// see https://groups.google.com/forum/#!topic/angular/LQoBCQ-V_tM
          });

       if (success || fail) {
           d.promise = d.promise.then(success, fail);
       }
       return d.promise;
   }

Now use it like this (based on your example):

function getCountries() {

    var query = breeze.EntityQuery.from("countries").orderBy("name");

    return manager.executeQuery(query).to$q(getSucceeded);

    function getSucceeded(data) { return data.results; }
}

Addendum

I have been asked:

"Why so complicated? Why don't you use $q.when(qPromse) to convert Q promise to $q promise?"

I tried that ... and couldn't make it work. Specifically, I tried this:

function to$q(qPromise, success, fail) {
    var promise = $q.when(qPromise);
    if (success || fail) {
       promise = promise.then(success, fail);
    }
    return promise;
}

I believe there is something wrong with $q.when.



来源:https://stackoverflow.com/questions/17724519/resolving-breeze-query-q-promise-with-route-resolve-stops-page

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!