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

답변:


131

Alex는 당신이하려는 일을 할 수없는 올바른 이유를 제공했습니다. + 1. 그러나 디자인을 잘 사용하지 않아서이 문제가 발생합니다.

resolve서비스 문자열 또는 주입 할 값을 반환하는 함수를받습니다. 후자를하고 있기 때문에 실제 기능을 전달해야합니다.

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

프레임 워크가 해결 data되면 dbService자유롭게 사용할 수 있도록 함수에 삽입 합니다. 이를 위해 config블록에 전혀 주입 할 필요가 없습니다 .

많이 드세요!


2
감사! 그러나이 작업을 수행하면 다음과 같은 결과가 나타납니다. 오류 : 'undefined'는 서비스에서 객체 ( '$ q.defer'평가)가 아닙니다.
dndr

1
주사제로 전달 최상위 함수 발생 .service하므로 이동 $q하고 $http있다.
Josh David Miller

1
@XMLilley resolve의 문자열은 실제로 서비스 이름이며 서비스의 특정 기능은 아닙니다. 예제를 사용하면 할 수 pageData: 'myData'있지만 pageData.overview컨트롤러에서 호출 해야합니다. 문자열 메소드는 서비스 팩토리가 API 대신 약속을 리턴 한 경우에만 유용합니다. 따라서 현재하고있는 방식이 아마도 가장 좋은 방법 일 것입니다.
조쉬 데이비드 밀러

2
@BrianVanderbusch 정확히 동의하지 않는 부분에 대한 혼란을 인정해야합니다. 실제OP가 직면 한 문제 는 서비스를 구성 블록에 주입했기 때문에 수행 할 수 없었습니다 . 해결책은 해결에 서비스를 주입하는 것 입니다. 귀하의 답변이 서비스 구성에 대해 많은 세부 정보를 제공했지만 OP가 발생한 오류와 어떻게 관련이 있는지 알 수 없으며 OP 문제에 대한 솔루션이 정확히 동일합니다 . 결제 기능에 서비스를 주입하고 구성 기능이 아닙니다 . 우리가 여기서 동의하지 않는 부분을 자세히 설명해 주시겠습니까?
Josh David Miller 1

1
@JoshDavidMiller 앞서 설명한 방법을 사용하면 상태 활성화 전에 서비스를 구성하여 구성 단계에서 오류를 처리 / 처리하여 응용 프로그램 부트 스트랩 전에 다른 구성 값을 인스턴스화하는 방법을 변경할 수 있습니다. 예를 들어, 응용 프로그램이 올바른 기능을 컴파일하도록하기 위해 사용자의 역할을 결정합니다.
Brian Vanderbusch

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으로이 요청의 맥락에서 구체적으로 설명하여 데모를 수정했습니다.
  • 제공자 에 대한 AngularJS 개발자 안내서보기
  • factory/ service/ 에 대한 훌륭한 설명도 있습니다.provider clevertech.biz에서가 .

프로 바이더는 .service메소드에 대해 약간 더 많은 구성을 제공하여 애플리케이션 레벨 프로 바이더로서 더 나아지지만 다음 $provide과 같이 구성 에 주입하여 구성 오브젝트 자체 내에이를 캡슐화 할 수도 있습니다 .


2
고마워, 나는 그런 예를 찾고 있었다. 자세한 답변과 훌륭한 링크!
cnlevy 2016 년

1
문제 없어요! 이것은 내가 가장 좋아하는 대답입니다. 나는 현재 받아 들여진 답변이 이미 답변되었고 18 표를 얻었을 때 이것에 답변했습니다. 몇 가지 배지에 좋습니다!
Brian Vanderbusch

코드 펜 샘플이 작동하면 정말 유용합니다. 예를 들어 $ provide.service ( 'dbService', function () {에는 $ http가 삽입되어 있지 않지만 본문에 사용됩니다. 시작할 때 Angular 프로그램의 제거 파일에서 구성 데이터를로드하기가 너무 어렵습니다.
Bernard

@Alkaline이 게시물 이후로 한두 가지를 배웠습니다. 이론적으로는 정답이지만 수정해야 할 1 또는 2 가지 (1 지적한 것)가 있습니다. 의견 주셔서 감사합니다. 답변을 검토하고 업데이트하겠습니다. 현재 코드 펜을 삭제했습니다 ... 완료 할 기회가 없었습니다.
Brian Vanderbusch

5
여기에 제공 한 정보가 잘못되었다고 생각합니다. 공급자를 사용할 수 있지만 구성 단계에서는 $get통화 결과로 작업하지 않습니다 . 대신 공급자 인스턴스에 메서드를 추가하고를 this호출하면 반환 됩니다 $get. 실제로 귀하의 예에서는 서비스를 사용할 수 있습니다 ... 제공자에서는 같은 서비스를 주입 할 수 없습니다 $http. 그리고 이것은 //return the factory as a provider, that is available during the configuration phase잘못
되거나

21

짧은 대답 : 당신은 할 수 없습니다. AngularJS는 서비스가 올바르게로드되었는지 확인할 수 없으므로 구성에 서비스를 주입 할 수 없습니다.

이 질문과 답변을보십시오 : module.config 내부의 AngularJS 의존성 주입

모듈은 부트 스트랩 프로세스 동안 응용 프로그램에 적용되는 구성 및 실행 블록 모음입니다. 가장 간단한 형태로 모듈은 두 종류의 블록으로 구성됩니다.

구성 블록 -공급자 등록 및 구성 단계에서 실행됩니다. 공급자와 상수 만 구성 블록에 주입 할 수 있습니다. 이는 서비스가 완전히 구성되기 전에 실수로 서비스가 인스턴스화되는 것을 방지하기위한 것입니다.


2
실제로 이것은 가능합니다. 짧은 설명을 제공합니다.
Brian Vanderbusch

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당신이 작성한 캐시 버스터 (이 질문에 아무 곳이나 정의되지 않은) 및 dogmaCacheBusterProvider (더 사용되지 않는다). 이것에 대해 명확하게 설명 하시겠습니까?
diEcho

@ pro.mean 구성 블록에 서비스를 삽입하기 위해 내가 사용한 기술을 시연한다고 생각하지만 시간이 오래 걸렸습니다. cacheBuster구성 기능의 매개 변수로 정의됩니다. 와 관련하여 dogmaCacheBusterProviderAngular가 명명 규칙을 사용하여 오랫동안 잊어 버린 영리한 작업입니다. 이렇게하면 stackoverflow.com/a/20881705/110010에 더 가까이 갈 수 있습니다 .
kim3er

이 다른 참조에. 나는 우리가 레시피 에서 정의한 것을 공급자 에게 추가한다는 것을 알게 되었습니다 .provider(). 내가 .factory('ServiceName')또는 .service('ServiceName')레시피로 무언가를 정의하고 .config block 에서 해당 메소드 중 하나를 사용 하려면 매개 변수를 ServiceNameProvider 로 설정 하지만 응용 프로그램이 중지됩니다.
diEcho

5

$ inject service를 사용하여 구성에 서비스를 주입 할 수 있습니다

app.config (function ($ provide) {

    $ provide.decorator ( "$ exceptionHandler", 함수 ($ delegate, $ injector) {
        리턴 함수 (예외, 원인) {
            var $ rootScope = $ injector.get ( "$ rootScope");
            $ rootScope.addError ({message : "Exception", reason : exception});
            $ 대리인 (예외, 원인);
        };
    });

});

출처 : 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를 사용하여 구성에서 서비스 메소드 호출

위와 같이 $ 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.