Наследование услуги AngularJS

У меня есть следующая услуга:

angular.module('app').service('BaseService', function (alertService) {
   var service = {};
   service.message =  "Hello";
   service.perform = function () {
        alertService.add("success",service.message);
   };
   return service;
});

Теперь я хочу наследовать эту службу в некотором "ChildService" с переопределяющим сообщением "Мир!". Я ожидаю, что вызов ChildService.perform() отобразит предупреждение с "World!".

Каков правильный способ сделать это?

Ответ 1

AngularJS не предоставляет какого-либо механизма для реализации наследования сервисов напрямую, однако для вашего случая вы можете использовать $assist.decorator, чтобы расширить BaseService самостоятельно или использовать его как прототип другого ChildService, используя обычный JavaScript. В моей практике, чтобы иметь сервис с настраиваемым состоянием и поведением, я использую providers. Во всех следующих примерах консольным выходом будет World.

декоратор

Если вам не нужен оригинальный BaseService в вашем модуле, вы можете его украсить

Plunker

function AlertService() {
  this.add = function(level, message) {
    switch(level) {
      case 'success':
        console.log(message);
    }
  }
}

function BaseService(alertService) {
  this.message =  "Hello";
  this.perform = function () {
    alertService.add("success",this.message);
  };
}

angular.
  module('app',[]).
  config(['$provide', function($provide) {
    $provide.decorator('BaseService', function($delegate) {
      $delegate.message = 'World';
      return $delegate;
    });
  }]).
  service('alertService', AlertService).
  service('BaseService', ['alertService',BaseService]).
  controller('ctrl', ['BaseService', function(baseService) {
    baseService.perform();
  }]);

Прототипное наследование

Plunker

function AlertService() {
  this.add = function(level, message) {
    switch(level) {
      case 'success':
        console.log(message);
    }
  }
}

function BaseService(alertService) {
  this.message =  "Hello";
  this.perform = function () {
    alertService.add("success",this.message);
  };
}

function ChildService(BaseService) {
  angular.extend(ChildService.prototype, BaseService);
  this.message = "World";
}

angular.
  module('app',[]).
  service('alertService', AlertService).
  service('BaseService', ['alertService',BaseService]).
  service('ChildService', ['BaseService',ChildService]).
  controller('ctrl', ['ChildService', function(ChildService) {
    ChildService.perform();
  }]); 

Provider

Plunker

function AlertService() {
  this.add = function(level, message) {
    switch(level) {
      case 'success':
        console.log(message);
    }
  }
}

function BaseService() {
  var message =  "Hello";

  this.setMessage = function(msg) {
    message = msg;
  }

  function Service(alertService) {
    this.perform = function () {
      alertService.add("success", message);
    };
  }

  function Factory(alertService) {
    return new Service(alertService);
  }

  this.$get = ['AlertService', Factory];
}

angular.
  module('app',[]).
  provider('BaseService', BaseService).
  config(['BaseServiceProvider', function(baseServiceProvider) {
    baseServiceProvider.setMessage('World');
  }]).
  service('AlertService', AlertService).
  controller('ctrl', ['BaseService', function(baseService) {
    baseService.perform();
  }]);

Ответ 2

Я немного изменил бы ваш код:

app.factory('BaseService', function () {
   //var service = {}; 
   function service(){
       this.message = "hello";
   }; 
   service.prototype.perform = function () {
        console.log('perfom', this.message);
   };
   return new service();
});

(Я просто изменяю alertService для console.log();..)

затем реализуем наследование следующим образом:

app.factory('childBaseService',['BaseService', function(BaseService){
    var childBaseService = function(){
            BaseService.constructor.call(this)
            this.message = 'world!';
    };

    childBaseService.prototype = Object.create(BaseService.constructor.prototype);
    childBaseService.prototype.constructor = childBaseService;

    return new childBaseService();

}]);

Вы можете увидеть пример того, как это работает.. в конце, BaseService и childService будут экземплярами конструктора BaseService (службы).

console.log(BaseService instanceof BaseService.constructor); //true
console.log(childBaseService instanceof BaseService.constructor); //true

Ответ 3

Вот пример, основанный на Constructor/new inheritance (который я обычно рекомендую против).

BaseService.$inject = ['alertService']
function BaseService(alertService) {
    this.message = 'hello'
    this.alertService = alertService
}

BaseService.prototype.perform = function perform() {
    this.alertService.add("success",this.message);
}


ChildService.$inject = ['alertService']
function ChildService(alertService) {
    this.message = 'hello world'
    this.alertService = alertService
}

ChildService.prototype = Object.create(BaseService.prototype)

И тогда вы просто включите их в качестве сервисов:

angular.module('app')
    .service('BaseService', BaseService)
    .service('ChildService', ChildService)

Ответ 4

Модуль A с сервисом ASvc:

(function(angular) {
  var app = angular.module('A', []);

  app.service('ASvc', ['$http', function($http) {
     var ASvc = {
       list: function() {
         return $http({
           method: 'GET',
           url: '/A'
         });
       },

       getInstructions: function(id) {
         return $http({
           method: 'GET',
           url: '/instructions/' + id
         });
       }
     };
     return ASvc;
  }]);
})(angular);

Модуль B с сервисом BSvc, который наследует от ASvc:

(function(angular) {
  var app = angular.module('B', ['A']);

  app.service('BSvc', ['$http', 'ASvc', function($http, ASvc) {
     var BSvc = {
       list: function() {
         return $http({
           method: 'GET',
           url: '/B'
         });
       }
     };

     BSvc.__proto__ = ASvc; // here you're settting the inheritance
     return BSvc;
  }]);
})(angular);

Теперь, когда вы вызываете BSvc.getInstructions(30775);, вы вызываете функцию родительской службы (ASvc) getInstructions из BSvc, а когда вы вызываете BSvc.list(), вы вызываете метод, который был переопределен от ASvc в BSvc. Наследование.

И BTW, когда я передаю angular в качестве аргумента для закрытия, вместо того, чтобы ссылаться на глобальную переменную angular непосредственно изнутри, я разрешаю код minifiers и obfuscators делать такие вещи, как это

(function(j){var c=j.module('A',[]);})(angular); // and so on

Хорошо иметь в виду, и я считаю, что это хорошая практика;)