App.config dosyasına hizmeti enjekte edin


168

Denetleyici çağrılmadan önce verilerin alınabilmesi için app.config dosyasına bir hizmet enjekte etmek istiyorum. Ben böyle denedim:

Hizmet:

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;
        }
    };
});

Yapılandırma:

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

Ama bu hatayı alıyorum:

Hata: Bilinmeyen sağlayıcı: EditorApp'dan dbService

Kurulum nasıl düzeltilir ve bu hizmet enjekte edilir?


3
Zaten gördüklerimi rağmen orada olduğunu ne amaçladığınız ulaşmak için bir yol ve angularjs bu tür bir işlev sağlayan çok zaman harcadı. Buna nasıl ulaşacağım konusundaki cevabımı gözden geçirin.
Brian Vanderbusch

Yanıtlar:


131

Alex, yapmaya çalıştığın şeyi yapamamanın doğru nedenini sağladı, bu yüzden +1. Ancak bu sorunla karşılaşıyorsunuz çünkü tam olarak kullanmıyorsanız bunların nasıl tasarlandığını çözüyor.

resolvehizmetin dizesini veya enjekte edilecek değeri döndüren bir işlevi alır. İkincisini yaptığınızdan, gerçek bir işlevi geçmeniz gerekir:

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

Çerçeve dataçözülmeye başladığında dbService, işlevi içine enjekte eder , böylece serbestçe kullanabilirsiniz. Bunu configbaşarmak için bloğa enjekte etmeniz gerekmez .

Afiyet olsun!


2
Teşekkürler! Ancak, bunu yaparsam alırım: Hata: 'undefined' bir nesne ('$ q.defer' değerlendiriyor) hizmetinde değil.
dndr

1
Enjeksiyon geçirilen üst düzey fonksiyonda gerçekleşir .service, bu yüzden hareket ettirin $qve $httporada.
Josh David Miller

1
@XMLilley Çözümdeki dize, aslında hizmetin belirli bir işlevi değil, bir hizmetin adıdır. Örneğinizi kullanarak bunu yapabilirsiniz pageData: 'myData', ancak daha sonra pageData.overviewkumandanızdan aramanız gerekir. String yöntemi, yalnızca hizmet fabrikası bir API yerine bir söz verdiyse yararlıdır. Şu anda bunu yapma şekliniz muhtemelen en iyi yoldur.
Josh David Miller

2
@BrianVanderbusch Tam olarak aynı fikirde olmadığımızı düşündüğümüz bir karışıklığa itiraf etmeliyim. Gerçek sorun OP karşılaştı diye bir yapılandırma bloğu içine bir hizmet enjekte olmasıydı yapılamaz . Çözüm, servisi çözüme enjekte etmektir . Cevabınız hizmet yapılandırması hakkında çok fazla ayrıntı sağlasa da, OP'nin karşılaştığı hatayla herhangi bir şekilde nasıl ilişkili olduğunu görmüyorum ve OP'nin sorununa çözümünüz tamamen aynıydı : servisi çözüm işlevine enjekte ettiniz ve yapılandırma işlevi değil . Burada katılmadığımız yeri ayrıntılı olarak açıklayabilir misiniz?
Josh David Miller

1
@JoshDavidMiller Gösterdiğim yöntemi kullanarak, durum etkinleştirme işleminden önce bir hizmet yapılandırılabilir, böylece yapılandırma aşamasında hatalar atılabilir / işlenebilir, bu da uygulama önyüklemesinden önce diğer yapılandırma değerlerini nasıl örnekleyeceğinizi potansiyel olarak değiştirebilir. Örneğin, uygulamanın doğru özellikleri derlemesini sağlamak için kullanıcının rolünü belirleme.
Brian Vanderbusch

140

Hizmetinizi özel bir AngularJS Sağlayıcısı olarak ayarlama

Kabul edilen cevabın söylediklerine rağmen, aslında ne yapmak istediğinizi YAPABİLİRSİNİZ , ancak yapılandırma aşamasında servis olarak kullanılabilmesi için yapılandırılabilir bir sağlayıcı olarak ayarlamanız gerekir. İlk olarak, Servicebir sağlayıcıya değiştirin Aşağıda gösterildiği gibi. Buradaki en önemli fark, değerini deferayarladıktan sonra, defer.promiseözelliği döndürülen söz verilen nesneye ayarlamanızdır $http.get:

Hizmet Sağlayıcı Hizmeti: (sağlayıcı: hizmet tarifi)

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;
             })
    }    
}

Şimdi, yapılandırılabilir bir özel Sağlayıcınız var, sadece enjekte etmeniz gerekiyor. Burada önemli fark eksik "enjekte edilebilir üzerinde Sağlayıcı" olmasıdır.

yapılandırma:

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();
                }
            }
        })
});

çözümlenmiş verileri kullanın 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);
     }

})

Olası Alternatifler

Aşağıdaki alternatif benzer bir yaklaşımdır, ancak .confighizmetin uygulamanız bağlamında belirli bir modül içinde kapsüllenerek tanımın gerçekleşmesine izin verir . Size en uygun yöntemi seçin. Ayrıca, tüm bu şeyleri askıya almanıza yardımcı olacak 3. alternatif ve yararlı bağlantılar hakkında notlar için aşağıya bakın.

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: 
            }
        })
});

Birkaç yardımcı Kaynak

  • John Lindquist'in 5 dakikalık mükemmel bir açıklaması ve gösterimi var. egghead.io'da bunun sahiptir ve ücretsiz derslerden biridir! Temelde $httpbu istek bağlamında gösterisini değiştirerek gösterisini değiştirdim
  • Sağlayıcılar hakkındaki AngularJS Geliştirici kılavuzunu görüntüleyin
  • Clevertech.biz hakkında factory/ service/ hakkında da mükemmel bir açıklama var provider . .

Sağlayıcı, .serviceyöntem üzerinde biraz daha fazla yapılandırma sağlar , bu da bir uygulama düzeyi sağlayıcısı olarak daha iyi olmasını sağlar, ancak aşağıdaki $providegibi yapılandırma enjekte ederek bunu yapılandırma nesnesinin içinde kapsülleyebilirsiniz :


2
Teşekkür ederim, böyle bir örnek arıyordum; ayrıntılı cevap ve harika bağlantılar!
Haziran'da cnlevy

1
sorun değil! Bu benim SO'ya en sevdiğim cevap. Şu anda kabul edilen cevap zaten cevaplandığında ve 18 oy aldığımda bunu cevapladım. Birkaç rozet için iyi!
Brian Vanderbusch

Codepen örnekleriniz işe yaradıysa gerçekten yararlı olacaktır. Örneğin, $ sağla.service ('dbService', function () {$ http enjekte edilmemiş, ancak kendi vücudunda kullanmaktadır. başlangıçta bir Açısal programda bir kaldırma dosyasından yapılandırma verilerini yüklemek çok zor
Bernard

@Alkaline Bu yazıdan bir iki şey öğrendim. Cevap teoride doğrudur, ancak düzeltilmesi gereken 1 veya 2 şey (1 belirttiğiniz) vardır. Yorum için teşekkürler. Cevabı inceleyip güncelleyeceğim. Şu an kod başlığını sildik ... hiç bitirme şansımız olmadı.
Brian Vanderbusch

5
Burada verdiğiniz bilgilerin yanlış olduğunu düşünüyorum. Bir sağlayıcı kullanabilirsiniz, ancak yapılandırma aşamasında $getçağrının sonucu ile çalışmazsınız . Bunun yerine sağlayıcı örneğine yöntemler eklemek ve thisaradığınızda geri dönmek istiyorsunuz $get. Aslında örneğinizde sadece bir servis kullanabilirsiniz ... Bir sağlayıcıda da servis enjekte edemezsiniz $http. Ve btw bu //return the factory as a provider, that is available during the configuration phaseyanıltıcı / yanlış bilgi
Dieterg

21

Kısa cevap: yapamazsınız. AngularJS, doğru bir şekilde yüklendiklerinden emin olamadığından hizmetleri yapılandırma içine enjekte etmenize izin vermez.

Bu soruya ve cevaba bakın: module.config içindeki değerin AngularJS bağımlılığı enjeksiyonu

Bir modül, bootstrap işlemi sırasında uygulamaya uygulanan bir yapılandırma ve çalıştırma blokları koleksiyonudur. En basit şekliyle modül, iki tür bloktan oluşur:

Yapılandırma blokları - sağlayıcı kayıtları ve yapılandırma aşamasında yürütülür. Yapılandırma bloklarına yalnızca sağlayıcılar ve sabitler enjekte edilebilir. Bu, hizmetlerin tam olarak yapılandırılmadan önce yanlışlıkla başlatılmasını önlemektir.


2
aslında, bu yapılabilir. Kısaca açıklayan bir cevap vermek.
Brian Vanderbusch

5

Bunu yapabileceğinizi sanmıyorum, ancak bir configbloğa başarılı bir şekilde hizmet verdim . (Açısal JS 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();
                    }
                });
            };
        }
    ]);

Servis yöntemi adıdır dogmaCacheBuster ama .configyazdığınız CACHEBUSTER (yanıtında yerde tanımlanmamış) ve dogmaCacheBusterProvider (daha fazla kullanılmaz). Bunun üzerine açıklık getirecek misiniz?
diEcho

@ pro.mean Sanırım bir yapılandırma bloğuna servis eklemek için kullandığım tekniği gösteriyordum, ama bir süredir. cacheBusteryapılandırma işlevinin bir parametresi olarak tanımlanır. Bununla ilgili olarak dogmaCacheBusterProvider, çok uzun zamandır unuttuğum, Angular'ın adlandırma kurallarıyla yaptığı akıllıca bir şey. Bu sizi yaklaştırabilir, stackoverflow.com/a/20881705/110010 .
kim3er

bu başka bir referans. Tarifte tanımladığımız her şeyi Sağlayıcıya eklediğimi biliyorum .provider(). bir şey tanımlarsam .factory('ServiceName')veya .service('ServiceName')reçete edersem ve yöntemlerinden birini .config blokta kullanmak istersem , parametreyi ServiceNameProvider olarak ayarlar, ancak uygulamamı durdurur.
diEcho

5

Yapılandırmanızdaki bir hizmeti enjekte etmek için $ inject hizmetini kullanabilirsiniz

App.config (function ($ sağlamak) {

    $ serve.decorator ("$ exceptionHandler", işlev ($ delegate, $ injector) {
        dönüş işlevi (istisna, neden) {
            var $ rootScope = $ injector.get ("$ rootScope");
            $ rootScope.addError ({message: "Exception", nedeni: exception});
            $ delegate (istisna, neden);
        };
    });

});

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


5

** Angular.injector kullanarak açıkça diğer modüllerden hizmet isteyin **

Kim3er'in cevabını detaylandırmak için , diğer modüllere dahil oldukları sürece, sağlayıcılara değiştirmeden hizmetler, fabrikalar vb.

Ancak, *Provideraçısal tembel yük modülleri olarak (bir hizmeti veya fabrikayı işledikten sonra dahili olarak açısal olarak yapılan) her zaman kullanılabilir olup olmadığından emin değilim (ilk yüklenene bağlı olabilir).

Değerleri yeniden enjekte etmek isterseniz sabit olarak kabul edilmesi gerektiğini unutmayın.

İşte bunu yapmanın daha açık ve muhtemelen daha güvenilir bir yolu + çalışan bir dalgıç

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

Config içindeki hizmet yöntemlerini çağırmak için $ injector kullanma

Benzer bir sorun yaşadım ve yukarıda gösterildiği gibi $ enjektör servisini kullanarak çözdüm. Doğrudan hizmeti enjekte denedim ama $ http dairesel bir bağımlılık ile sona erdi. Hizmet hata ile bir modal görüntüler ve ben de $ https bağımlılığı olan ui-bootstrap modal kullanıyorum.

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

Çok yardımcı oldu teşekkürler
Erez

2

Bunu yapmak çok kolay bir çözüm

Not : Servis, yapılandırma yürütmesinde hizmet başlatılmadığından, yalnızca bir asenkron çağrısı içindir.

run()Yöntemi kullanabilirsiniz . Misal :

  1. Hizmetinize "MyService" adı verilir
  2. Bir sağlayıcı "MyProvider" üzerinde bir asenkron yürütmesi için kullanmak istiyorsunuz

Senin kodun :

(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

Bununla biraz uğraştım, ama aslında yaptım.

Açısal değişikliklerden dolayı cevapların modası geçmiş olup olmadığını bilmiyorum, ancak bu şekilde yapabilirsiniz:

Bu sizin hizmetiniz:

.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;
    }
  };
 });

Ve bu yapılandırma

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

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

0

En kolay yol: $injector = angular.element(document.body).injector()

Sonra çalıştırmak için kullanın invoke()veyaget()


Ne hack! Ne yazık ki, uygulamanın DOM ile bağlı olmadığı çoğu birim testinde çalışmaz.
rixo
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.