在app.config中注入服务


168

我想将服务注入app.config,以便可以在调用控制器之前检索数据。我这样尝试过:

服务:

app.service('dbService', function() {
    return {
        getData: function($q, $http) {
            var defer = $q.defer();
            $http.get('db.php/score/getData').success(function(data) {
                defer.resolve(data);            
            });
            return defer.promise;
        }
    };
});

配置:

app.config(function ($routeProvider, dbService) {
    $routeProvider
        .when('/',
        {
            templateUrl: "partials/editor.html",
            controller: "AppCtrl",
            resolve: {
                data: dbService.getData(),
            }
        })
});

但是我得到这个错误:

错误:未知提供程序:EditorApp的dbService

如何更正设置并注入此服务?


3
尽管您已经看到了什么,但是一种方法可以实现您的预​​期目标,并且AngularJS花了很多时间来启用这种功能。复习有关如何实现此目标的答案。
Brian Vanderbusch 2014年

Answers:


131

Alex提供了无法执行您要尝试执行的操作的正确原因,因此+1。但是您遇到此问题是因为您没有完全使用解决方案的设计方式。

resolve接受服务或函数的字符串,返回要注入的值。由于您要执行后者,因此需要传递一个实际函数:

resolve: {
  data: function (dbService) {
    return dbService.getData();
  }
}

框架何时解决 data,它将注入dbService函数,以便您可以自由使用它。您完全不需要注入config模块即可完成此操作。

祝您好胃口!


2
谢谢!但是,如果执行此操作,则会得到:错误:服务中的“未定义”不是对象(评估“ $ q.defer”)。
dndr

1
注入发生在传递给的顶级函数中.service,因此移动$q$http那里。
Josh David Miller

1
@XMLilley解析中的字符串实际上是服务的名称,而不是服务上的特定功能。以您的示例为例,您可以这样做pageData: 'myData',但随后必须pageData.overview从控制器中调用。仅当服务工厂返回promise而不是API时,字符串方法才有用。因此,您当前的操作方式可能是最好的方法。
乔什·大卫·米勒

2
@BrianVanderbusch我必须承认您对我们完全不同的地方感到困惑。OP遇到的实际问题是他将服务注入到config块中,这无法完成。解决方案是将服务注入解决方案。虽然您的答案提供了有关服务配置的许多详细信息,但我看不到它与OP遇到的错误有任何关系,并且您对OP的问题的解决方案是完全相同的:您将服务注入了resolve函数,并且不是配置功能。您能否详细说明我们在这里的不同之处?
2014年

1
@JoshDavidMiller使用我演示的方法,可以在状态激活之前配置服务,这样就可以在配置阶段抛出/处理错误,从而有可能改变在应用程序引导之前实例化其他配置值的方式。例如,确定用户的角色以使应用程序编译正确的功能。
Brian Vanderbusch 2014年

140

将您的服务设置为自定义AngularJS提供程序

尽管接受的答案说什么,你居然CAN做什么你打算这样做,但你需要将其设置为一个可配置的提供者,因此它在配置阶段是可用的服务。首先,你的改变Service到提供者如下所示。此处的主要区别在于,在设置了的值之后defer,您可以将defer.promise属性设置为返回的Promise对象$http.get

提供者服务:(提供者:服务配方)

app.provider('dbService', function dbServiceProvider() {

  //the provider recipe for services require you specify a $get function
  this.$get= ['dbhost',function dbServiceFactory(dbhost){
     // return the factory as a provider
     // that is available during the configuration phase
     return new DbService(dbhost);  
  }]

});

function DbService(dbhost){
    var status;

    this.setUrl = function(url){
        dbhost = url;
    }

    this.getData = function($http) {
        return $http.get(dbhost+'db.php/score/getData')
            .success(function(data){
                 // handle any special stuff here, I would suggest the following:
                 status = 'ok';
                 status.data = data;
             })
             .error(function(message){
                 status = 'error';
                 status.message = message;
             })
             .then(function(){
                 // now we return an object with data or information about error 
                 // for special handling inside your application configuration
                 return status;
             })
    }    
}

现在,您有了一个可配置的自定义提供程序,只需要注入它即可。此处的主要区别是缺少“可注射的提供者”。

配置:

app.config(function ($routeProvider) { 
    $routeProvider
        .when('/', {
            templateUrl: "partials/editor.html",
            controller: "AppCtrl",
            resolve: {
                dbData: function(DbService, $http) {
                     /*
                     *dbServiceProvider returns a dbService instance to your app whenever
                     * needed, and this instance is setup internally with a promise, 
                     * so you don't need to worry about $q and all that
                     */
                    return DbService('http://dbhost.com').getData();
                }
            }
        })
});

使用您的已解析数据 appCtrl

app.controller('appCtrl',function(dbData, DbService){
     $scope.dbData = dbData;

     // You can also create and use another instance of the dbService here...
     // to do whatever you programmed it to do, by adding functions inside the 
     // constructor DbService(), the following assumes you added 
     // a rmUser(userObj) function in the factory
     $scope.removeDbUser = function(user){
         DbService.rmUser(user);
     }

})

可能的选择

以下替代方法是类似的方法,但是允许在中定义.config,将服务封装到应用程序上下文中的特定模块中。选择适合您的方法。另请参阅以下第三种替代方法的注释以及有用的链接,以帮助您掌握所有这些内容

app.config(function($routeProvider, $provide) {
    $provide.service('dbService',function(){})
    //set up your service inside the module's config.

    $routeProvider
        .when('/', {
            templateUrl: "partials/editor.html",
            controller: "AppCtrl",
            resolve: {
                data: 
            }
        })
});

一些有用的资源

  • John Lindquist在egghead.io上有5分钟的出色解释和演示,这是免费的课程之一!我基本上修改了他的演示,使其$http针对此请求进行了具体化
  • 查看Providers上的AngularJS开发人员指南
  • 在clevertech.biz也有关于factory/ service/ 的出色解释。provider

该提供程序为您提供了比该.service方法更多的配置,这使其更适合作为应用程序级别的提供程序,但是您也可以通过将其注入$provideconfig 来将其封装在config对象本身中,如下所示:


2
谢谢,我一直在寻找这样的例子。详细的答案和重要的链接!
cnlevy

1
没问题!这是我最喜欢的答案。当当前接受的答案已经得到回答并获得18票时,我就回答了。不错的几个徽章!
Brian Vanderbusch 2014年

如果您的Codepen样本有效,那将非常有用。例如,$ provide.service('dbService',function(){没有注入$ http而是在其主体中使用它。按它的样子,我无法让您的技术2起作用。在启动时如此难以从一个角度程序中删除文件加载配置数据。
伯纳德·

自从这篇文章以来,我已经学到了一两件事。答案在理论上是正确的,但有1或2件事(您指出了1件事)应该解决。感谢您的评论。我将审查并更新答案。此刻暂时删除了Codepen ...从来没有机会完成它。
Brian Vanderbusch 2014年

5
我认为您在此处提供的信息不正确。您可以使用提供程序,但是在配置阶段,您无法使用$get调用结果。相反,您想在提供程序实例上添加方法,并this在调用时返回$get。实际上,在您的示例中,您只能使用服务...在提供程序中,您也不能注入$http。顺便说一句,这//return the factory as a provider, that is available during the configuration phase是误导性/不正确的信息
Dieterg

21

简短答案:您不能。AngularJS不允许您将服务注入配置中,因为不能确保它们已正确加载。

参见以下问答: AngularJS依赖项在module.config内部的注入

模块是配置和运行块的集合,这些块在引导过程中应用于应用程序。以最简单的形式,该模块包含两种块的集合:

配置块 -在提供者注册和配置阶段执行。只有提供者和常量可以注入配置块中。这是为了防止在服务完全配置之前意外实例化服务。


2
实际上,可以做到这一点。提供一个简短解释的答案。
Brian Vanderbusch 2014年

5

我认为您不应该能够做到这一点,但是我已经成功地将服务注入了一个config区块。(AngularJS v1.0.7)

angular.module('dogmaService', [])
    .factory('dogmaCacheBuster', [
        function() {
            return function(path) {
                return path + '?_=' + Date.now();
            };
        }
    ]);

angular.module('touch', [
        'dogmaForm',
        'dogmaValidate',
        'dogmaPresentation',
        'dogmaController',
        'dogmaService',
    ])
    .config([
        '$routeProvider',
        'dogmaCacheBusterProvider',
        function($routeProvider, cacheBuster) {
            var bust = cacheBuster.$get[0]();

            $routeProvider
                .when('/', {
                    templateUrl: bust('touch/customer'),
                    controller: 'CustomerCtrl'
                })
                .when('/screen2', {
                    templateUrl: bust('touch/screen2'),
                    controller: 'Screen2Ctrl'
                })
                .otherwise({
                    redirectTo: bust('/')
                });
        }
    ]);

angular.module('dogmaController', [])
    .controller('CustomerCtrl', [
        '$scope',
        '$http',
        '$location',
        'dogmaCacheBuster',
        function($scope, $http, $location, cacheBuster) {

            $scope.submit = function() {
                $.ajax({
                    url: cacheBuster('/customers'),  //server script to process data
                    type: 'POST',
                    //Ajax events
                    // Form data
                    data: formData,
                    //Options to tell JQuery not to process data or worry about content-type
                    cache: false,
                    contentType: false,
                    processData: false,
                    success: function() {
                        $location
                            .path('/screen2');

                        $scope.$$phase || $scope.$apply();
                    }
                });
            };
        }
    ]);

服务方法名称为dogmaCacheBuster,但是.config您已经编写了cacheBuster(答案中未定义)和 dogmaCacheBusterProvider(不再使用)。你能澄清一下吗?
diEcho

@ pro.mean我想我正在演示将服务插入配置块的技术,但是已经有一段时间了。cacheBuster被定义为config函数的参数。关于dogmaCacheBusterProvider,这是Angular在命名约定方面做的一件很聪明的事情,而我早已将其遗忘了。这可能使您更接近stackoverflow.com/a/20881705/110010
kim3er

在这另一个参考。我知道附加提供者无论我们在.provider()配方中定义什么。如果我用.factory('ServiceName').service('ServiceName')配方定义了某种东西并想在.config block中使用其方法之一,该怎么办,将参数设置为ServiceNameProvider,但它停止我的应用程序。
diEcho

5

您可以使用$ inject服务在配置中注入服务

app.config(function($ provide){

    $ provide.decorator(“ $ exceptionHandler”,function($ delegate,$ injector){
        返回函数(异常,原因){
            var $ rootScope = $ injector.get(“ $ rootScope”);
            $ rootScope.addError({message:“ Exception”,reason:exception});
            $ delegate(例外,原因);
        };
    });

});

资料来源:http : //odetocode.com/blogs/scott/archive/2014/04/21/better-error-handling-in-angularjs.aspx


5

**使用angular.injector明确要求其他模块提供服务**

只是为了详细说明kim3er的答案,您可以提供服务,工厂等,而无需将其更改为提供程序,只要它们包含在其他模块中即可...

但是,我不确定*Provider(它是在处理服务或工厂后由角度内部生成的)是否总是可用(可能取决于首先加载的内容),因为角度延迟加载了模块。

请注意,如果要重新注入值,则应将它们视为常量。

这是一种更明确,也可能更可靠的方式+ 工作的插棒

var base = angular.module('myAppBaseModule', [])
base.factory('Foo', function() { 
  console.log("Foo");
  var Foo = function(name) { this.name = name; };
  Foo.prototype.hello = function() {
    return "Hello from factory instance " + this.name;
  }
  return Foo;
})
base.service('serviceFoo', function() {
  this.hello = function() {
    return "Service says hello";
  }
  return this;
});

var app = angular.module('appModule', []);
app.config(function($provide) {
  var base = angular.injector(['myAppBaseModule']);
  $provide.constant('Foo', base.get('Foo'));
  $provide.constant('serviceFoo', base.get('serviceFoo'));
});
app.controller('appCtrl', function($scope, Foo, serviceFoo) {
  $scope.appHello = (new Foo("app")).hello();
  $scope.serviceHello = serviceFoo.hello();
});

2

使用$ injector调用config中的服务方法

我遇到了类似的问题,并通过使用$ injector服务解决了它,如上所示。我尝试直接注入服务,但最终导致对$ http的循环依赖。该服务显示有错误的模态,我正在使用ui-bootstrap模态,该模版也依赖于$ https。

    $httpProvider.interceptors.push(function($injector) {
    return {
        "responseError": function(response) {

            console.log("Error Response status: " + response.status);

            if (response.status === 0) {
                var myService= $injector.get("myService");
                myService.showError("An unexpected error occurred. Please refresh the page.")
            }
        }
    }

感谢那些帮助了很多
埃雷兹

2

一个很容易做到的解决方案

注意:它仅用于异步调用,因为服务未在配置执行时初始化。

您可以使用run()方法。范例:

  1. 您的服务称为“ MyService”
  2. 您想将其用于在提供程序“ MyProvider”上的异步执行

您的代码:

(function () { //To isolate code TO NEVER HAVE A GLOBAL VARIABLE!

    //Store your service into an internal variable
    //It's an internal variable because you have wrapped this code with a (function () { --- })();
    var theServiceToInject = null;

    //Declare your application
    var myApp = angular.module("MyApplication", []);

    //Set configuration
    myApp.config(['MyProvider', function (MyProvider) {
        MyProvider.callMyMethod(function () {
            theServiceToInject.methodOnService();
        });
    }]);

    //When application is initialized inject your service
    myApp.run(['MyService', function (MyService) {
        theServiceToInject = MyService;
    }]);
});

1

好吧,我为此付出了一些努力,但实际上我做到了。

我不知道答案是否由于角度的某些变化而过时了,但是您可以通过以下方式实现:

这是您的服务:

.factory('beerRetrievalService', function ($http, $q, $log) {
  return {
    getRandomBeer: function() {
      var deferred = $q.defer();
      var beer = {};

      $http.post('beer-detail', {})
      .then(function(response) {
        beer.beerDetail = response.data;
      },
      function(err) {
        $log.error('Error getting random beer', err);
        deferred.reject({});
      });

      return deferred.promise;
    }
  };
 });

这是配置

.when('/beer-detail', {
  templateUrl : '/beer-detail',
  controller  : 'productDetailController',

  resolve: {
    beer: function(beerRetrievalService) {
      return beerRetrievalService.getRandomBeer();
    }
  }
})

0

最简单的方法: $injector = angular.element(document.body).injector()

然后使用它来运行invoke()get()


真是可笑!不幸的是,它不适用于大多数未将应用程序绑定到DOM的单元测试。
rixo 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.