创建工厂时使用异步数据

时间:2014-02-15 22:43:03

标签: javascript angularjs

在我的一个应用程序中,我想使用$ resource来清理读/写操作,但是在我加载首选项文档之前,url有一个我无法知道的前缀。我不知道如何干净地延迟工厂的创建,直到ajax回来。

这类似于“不要初始化应用程序直到某些ajax数据回来”问题,但我能找到的所有示例(使用$route.resolve)只会延迟控制器,而不是工厂。

这是我的理想jsFiddle

我认为GitHub pull #3295可能只是神奇地使上面的小提琴工作,但我正在寻找替代方案,直到它合并。

我当前的修复程序为here,但这会导致多个$resource实例,并且每个需要工厂的路由或首选项文档都会生成resolve子句。

2 个答案:

答案 0 :(得分:1)

注入Angular Ui路由器,并将您的Doc重构为提供者工厂而不是服务工厂

小提琴:http://jsfiddle.net/LongLiveCHIEF/5kSbC/

  1. 将您的工厂更改为提供商(仍然使用.factory方法),并使用ui.router处理您的首选项服务。
  2. 在您将路由器发送到应用程序或激活应用程序状态启动之前,您的首选项将配置您的前prefix工厂的docs(现在重构为提供程序)。
  3. 然后,在控制器实例化之前,通过异步承诺解析解析Doc工厂数据。
  4. 然后,这将自动使数据可用于$state(ui.router)

    ui.router中的.when()允许您匹配路由模式,然后在触发路由处理之前执行一些特殊配置。我实际上只是昨晚发布了一个小应用程序,它有类似的挑战需要解决(我需要在初始化应用程序或处理路由之前使用一些用户特定信息播种视图)。你可以在这里看到我的实际代码:

    https://github.com/NormalGravity/evsvillas.com/blob/master/app/src/app.js

    查看第22-38行。在决定发送给应用程序的路由之前,他们等待承诺解决(使用配置数据对单个种子进行播种)。

    一些有用的阅读内容:

    • 关于ProvidersServices的AngularJS开发人员指南(请注意不同应用阶段的可用性)。这实际上不是你的想法。这两个指南讨论了如何以不同的方式使用factoryserviceprovider,并将应用阶段转换为服务或提供商(请考虑$http vs $httpProvider
    • Angular Ui Router wiki Page on the StateManger。这详细介绍了允许路由前工厂配置的概念

答案 1 :(得分:-1)

你是对的,延迟服务提供目前不是“Angular native”能力。

解决方法是从配置函数中获取$ provide提供程序。

现在谈论足够的代码(评论解释其余的):

HTML

<!-- test will be holding the lazy registered value -->
<body ng-app="App" ng-controller="TestController">
   {{test}}
</body>

JS (更新后的评论)

var App = angular.module('App', []);

// prepare the external reference holder
var provide;

// use a config function to externalize the $provide provider
App.config(function($provide) {
    provide = $provide;
});

// delayed $provide use with a timeout
App.run(function($timeout) {
    // async function with one of the followings
    // - $timeout
    // - $q
    // - $http
    // which actually register the value (could be a factory or what you want)
    // at t+1000ms
    $timeout(function() {
        // here I provide a simple object but you can pass a $resource !
        provide.value('Test', {a:1});
    },1000);
});

// watch the existence of the service
// OR broadcast an event from $rootScope
// OR the way you want to tell the service now exists
App.controller('TestController', function($injector, $scope) {
    var unwatch = $scope.$watch(function() { 
        return $injector.has('Test');
    }, function(has) {
        if(has) {
            $scope.test = $injector.get('Test');
            unwatch();
        }
    });
});

这里的应用程序不是延迟加载的,而是依赖于异步数据的值。 延迟整个应用程序加载的简单数据不利于用户体验。 加载应用程序,显示页面,告诉用户正在获取数据,最后显示提取的数据。

<强>更新

如果您想要一种更“面向承诺”的方法,请考虑以下代码来包含在承诺中注册的值:

JS

var App = angular.module('App', []);

App.factory('MyLazyFactory', function($timeout) {
    // here I delay with $timeout but
    // it can be delayed with $http !
    // ($timeout returns a promise, resolved with the inner returned value)
    return $timeout(function() {
        // here I return an object but
        // it can be a $resource !
        return {a:1};
    },1000);
});

App.controller('TestController', function(MyLazyFactory, $scope) {
    MyLazyFactory.then(function(myFactory) {
        // here I really get the value returned by the timeout callback
        $scope.test = myFactory;
    });
});

如果不清楚,请继续发表评论。

<强>更新 (以解决由Brian Vanderbusch指出的$ resource和promise包装的特殊情况)

使用面向承诺的方法,您将需要两个级别的承诺包装,您将在我的jsFiddle版本中看到。

JS

angular.module('myApp',["ngResource"])

.service("LazyPrefs", [ "$http", function($http) {
    return $http.post("/echo/json/", "json={\"prefix\":\"/echo/json/test\"}")
        .then(function(response) { return response.data; });
}])

.factory("LazyDoc", [ "$resource", "LazyPrefs", function($resource, LazyPrefs) {
    return LazyPrefs.then(function(Prefs) {
        console.log("Using " + Prefs.prefix + " as prefix");
        return $resource(Prefs.prefix + "/:docId", { docId:"@id" });
    });
}])

.controller("MainCtrl", [ "$scope", "$http", "LazyDoc", function($scope, $http, LazyDoc) {
    LazyDoc.then(function(Doc) {
        $scope.doc = new Doc({ name: "Steve", photo: "profile.jpg" });
    });
}]);