2014-10-29 87 views
16

我旁邊的服務:「世界」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!」的警報。

什麼是正確的方法來做到這一點?

+0

使用您最喜愛的JavaScript繼承風格,然後將結果對象添加爲服務。請記住,由於服務是單身人士,您可能要添加單個實例或構造函數。 – Sacho 2014-10-29 23:09:35

回答

30

AngularJS不提供任何機制來直接實現服務的繼承,但是,對於你的情況,你可以使用$provide.decorator延長BaseService本身或像使用普通JavaScript的另一個​​的原型一樣使用它。在我的實踐中,爲了使服務具有可配置的狀態和行爲,我使用了providers。在以下所有示例中,控制檯輸出將爲世界

裝飾

如果你不需要你的模塊中的原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(); 
    }]); 

提供商

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

裝飾器不是一種繼承形式 - 它嚴格地是給定服務的裝飾,例如,在繼承中,你有兩個最終的工件:BaseService < - ChildService繼承,而裝飾後只有BaseService,在裝飾後。 – Sacho 2014-10-29 23:35:58

+2

@Sacho我知道裝飾器不是繼承的形式,但是我聲稱爲了獲得與其他服務有點不同的服務(並且其他服務不是以原始形式使用),繼承不是需要和現有服務的裝飾器可以用來代替。 – Vadim 2014-10-29 23:56:28

5

這裏是一個基於構造函數/新繼承(我通常會推薦)的示例。

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) 
+0

你能否詳細說一下你的「我一般會推薦反對」評論?這在我看來似乎很好。 – 2014-10-29 23:27:42

+1

繼承總體上很尷尬 - 與其非常多產的用途相比,它只在非常特殊的情況下才有用。由於'this'在函數調用中被綁定的方式,JavaScript繼承是一個額外的尷尬層。在javascript中基於構造函數/經典繼承增加了一個額外的混亂層(例如,注意我如何使用Object.create來擴展原型對象,考慮你需要做的工作來實現super()調用,需要。 bind()函數將被用作回調,列表繼續)。對象組合更流暢。 – Sacho 2014-10-29 23:34:33

+1

很好的例子。我相信它應該是'this.message'而不是'service.message',因爲你沒有使用服務對象。 – Greg 2014-10-30 00:41:59

12

我將修改一點點代碼:

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

}]); 

你可以看到這是如何工作的example ..末,BaseService和childService將BaseService構造函數(服務)的實例。

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

這是一個乾淨的方式去「內部」(服務聲明內的事情),我沒有想到它:) – Sacho 2014-10-30 06:57:03

2

模塊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);,你調用父類的服務(ASvcgetInstructions功能從BSvc,當你打電話BSvc.list(),你打電話給方法wh ich從ASvc覆蓋BSvc。遺產。

而且順便說一句,當我路過angular作爲參數傳遞給關閉,而不是指全球angular變量直接在它,我允許代碼minifiers和模糊處理,以做這樣的事情:

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

這是一件很好的事情,我認爲這是一個很好的做法;)

+1

混淆器參數不計算在這裏:'1.'您的代碼使用約15額外的字符。 '2。如果你不想污染窗口命名空間,你可以簡單地鏈接這些調用並保存其他字符:'angular.module('A',[])。service(...)。service(...) ;' – 2016-08-14 03:49:41

+1

@BenjaminM好的,也許混淆並不完美,但縮小效果很好。鏈接調用在我看來產生了非常難看的代碼,並且由於大部分時間我編寫的代碼將被處理成一個連接和縮小,我非常關心原始代碼的易讀性。關於閉包,即使它涉及到全局的「角度」變量,這也是我教自己做的一個習慣,將模塊封裝在閉包中。在最壞的情況下,它根本不會受到傷害。 – 2016-08-14 08:24:50