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はこのタイプの機能を有効にするために多くの時間を費やしました。これを達成する方法に関する私の答えを確認してください。
ブライアンヴァンダーブッシュ2014

回答:


131

アレックスは、あなたがしようとしていることを実行できないという正しい理由を提供したので、+ 1。しかし、十分に使用していないためにこの問題が発生しています。

resolveサービスの文字列、または注入される値を返す関数のいずれかを取ります。後者を実行しているので、実際の関数を渡す必要があります。

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

フレームワークがresolve dataに入るとdbService、関数にが挿入されるので、自由に使用できます。configこれを達成するためにブロックに注入する必要はまったくありません。

どうもありがとう!


2
ありがとう!ただし、これを実行すると、次のエラーメッセージが表示されます。エラー: 'undefined'は、サービス内のオブジェクト( '$ q.defer'を評価する)ではありません。
dndr 2013

1
インジェクションは、に渡されるトップレベルの関数で発生する.serviceので、移動$q$httpてください。
Josh David Miller

1
@XMLilley解決の文字列は、実際にはサービスの名前であり、サービスの特定の関数ではありません。あなたの例を使うと、あなたはそうすることができpageData: 'myData'ますが、それからあなたはpageData.overviewあなたのコントローラーから呼ばなければなりません。文字列メソッドは、サービスファクトリがAPIではなくpromiseを返した場合にのみ役立つでしょう。ですから、あなたが現在行っている方法はおそらく最良の方法です。
Josh David Miller

2
@BrianVanderbusch正確にあなたが私たちが同意していないと感じる場所についての混乱を認めなければなりません。OPが遭遇した実際の問題は、彼がサービスを構成ブロックに注入したことです。これは実行できません。解決策は、サービスをresolveに挿入することです。あなたの答えはサービス構成について多くの詳細を提供しましたが、OPが発生したエラーにそれがどのように関連しているかはわかりません、そしてOPの問題の解決策はまったく同じでした:サービスをresolve関数に挿入し、config関数ではありません。ここで私たちが反対するところについて詳しく説明していただけますか?
Josh David Miller、

1
@JoshDavidMiller私が示した方法を使用すると、状態のアクティブ化の前にサービスを構成でき、構成フェーズ中にエラーがスロー/処理され、アプリケーションのブートストラップの前に他の構成値をインスタンス化する方法が変更される可能性があります。たとえば、アプリケーションに適切な機能をコンパイルさせるためのユーザーの役割の決定。
ブライアンヴァンダーブッシュ2014

140

サービスをカスタムAngularJSプロバイダーとして設定する

受け入れ答えが言うにもかかわらず、あなたが実際にCANあなたがしようとして何ができますが、まず、あなたを変え...それは構成フェーズ中にサービスとして利用できるように、設定可能なプロバイダとして、それを設定する必要がありService、プロバイダに以下に示すように。ここでの主な違いは、の値を設定した後、によって返されるpromiseオブジェクトにプロパティをdefer設定することです。defer.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行い、サービスをアプリのコンテキスト内の特定のモジュール内にカプセル化することができます。適切な方法を選択してください。これらすべてのコツをつかむのに役立つ3番目の代替リンクと役立つリンクに関するメモについては、以下も参照してください。

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分間の優れた説明とデモンストレーションを行っています。これは無料のレッスンの1つです!私は基本的に彼のデモンストレーション$httpをこのリクエストのコンテキストで具体的にすることで変更しました
  • プロバイダーに関するAngularJS開発者ガイドを見る
  • 優れた説明もありますfactory/ service/ provider clevertech.bizでは

プロバイダーは、.serviceメソッドよりも少し多くの構成を提供します。これにより、アプリケーションレベルのプロバイダーとしてより優れていますが、次の$provideようにconfigに挿入することで、構成オブジェクト自体にこれをカプセル化することもできます。


2
ありがとう、私はそのような例を探していました。詳細な回答と素晴らしいリンク!
cnlevy 2014年

1
問題ない!これが私のお気に入りの回答です。私は、現在受け入れられている回答がすでに回答されていて、18票を得たときにこれに回答しました。いくつかのバッジに適しています!
ブライアンヴァンダーブッシュ2014

コードペンのサンプルが機能した場合、それは本当に役に立ちます。たとえば、$ provide.service( 'dbService'、function(){には$ httpが挿入されていませんが、本体で使用されています。現状では、テクニック2を機能させることができませんでした。非常にイライラしています。起動時にAngularプログラムの削除ファイルから構成データを読み込むのが難しい
Bernard

@アルカリこの投稿以来、私は1つまたは2つのことを学びました。理論的には正しい答えですが、修正する必要のあるものが1つまたは2つあります(1つは指摘しました)。コメントをありがとう。回答を確認して更新します。とりあえずコードペンを削除しました...終了する機会はありませんでした。
ブライアンヴァンダーブッシュ2014

5
ここに入力した情報は正しくないと思います。プロバイダーを使用できますが、構成フェーズでは$get呼び出しの結果を処理しません。代わりに、プロバイダーインスタンスにメソッドを追加し、thisを呼び出すとすぐに戻ります$get。実際、あなたの例では、サービスを使用することができます...プロバイダーでは、のようなサービスを注入することもできません$http。そして、これ//return the factory as a provider, that is available during the configuration phaseは誤解を招く/不正確な情報です
Dieterg

21

短い答え:できません。AngularJSは、サービスが正しくロードされているかどうかを確認できないため、サービスを構成に挿入することを許可しません。

この質問と回答を参照してください: module.config内の値のAngularJS依存性注入

モジュールは、ブートストラッププロセス中にアプリケーションに適用される構成および実行ブロックのコレクションです。最も単純な形式のモジュールは、2種類のブロックのコレクションで構成されています。

構成ブロック -プロバイダーの登録および構成フェーズ中に実行されます。構成ブロックに注入できるのはプロバイダーと定数のみです。これは、サービスが完全に構成される前に、サービスが誤ってインスタンス化されるのを防ぐためです。


2
実際には、これを行うことができます。簡単に説明する答えを提供します。
ブライアンヴァンダーブッシュ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ですが、cacheBuster(回答のどこにも定義されていません)とdogmaCacheBusterProvider(これ以上は使用されません).configが記述されています 。これについて明確にしていただけますか?
diEcho

@ pro.mean構成ブロックにサービスを挿入するために使用した手法を示していたと思いますが、それは久しぶりです。cacheBusterは、config関数のパラメーターとして定義されます。に関してはdogmaCacheBusterProvider、Angularが命名規則で行う賢いことであり、私は長い間忘れていました。これにより、あなたに近づくかもしれません、stackoverflow.com / a / 20881705/110010
kim3er 2017年

この別の参照。私がレシピで定義するものは何でもプロバイダーを追加することを知りました.provider().factory('ServiceName')またはで何かを定義し.service('ServiceName')、そのメソッドの1つを.config blockで使用したい場合、パラメーターをServiceNameProviderに設定しますが、アプリケーションが停止します。
diEcho 2017年

5

$ injectサービスを使用して、構成にサービスを挿入できます

app.config(function($ provide){

    $ provide.decorator( "$ exceptionHandler"、function($ delegate、$ injector){
        return関数(例外、原因){
            var $ rootScope = $ injector.get( "$ rootScope");
            $ rootScope.addError({message: "Exception"、reason:exception});
            $ delegate(exception、cause);
        };
    });

});

ソース:http : //odetocode.com/blogs/scott/archive/2014/04/21/better-error-handling-in-angularjs.aspx


5

** angular.injectorを使用して他のモジュールからのサービスを明示的に要求する**

kim3erの答えを詳しく説明するために、他のモジュールに含まれている限り、プロバイダーに変更せずにサービスやファクトリーなどを提供できます...

ただし、*Provider(サービスまたはファクトリーを処理した後、angularによって内部的に作成されます)が常に利用できるかどうかはわかりません(最初に読み込まれるものに依存する場合があります)。

値を再注入する場合は、定数として扱う必要があることに注意してください。

これは、より明確で、おそらくより信頼できる方法+ 機能するプランカーです

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を使用して構成でサービスメソッドを呼び出す

同様の問題があり、上記の$ injectorサービスを使用して解決しました。サービスを直接インジェクトしてみましたが、$ httpへの循環依存が発生しました。サービスにエラーのあるモーダルが表示され、$ httpsに依存するui-bootstrapモーダルを使用しています。

    $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.")
            }
        }
    }

多くを助けてくれてありがとう
Erez

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.