2014-10-29 15 views
16

mam kolejną usługę: „Świat”angularjs serwis dziedziczenie

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

Teraz chcę dziedziczą tę usługę w jakimś „ChildService” z nadrzędnym wiadomości dalej. Oczekuję, że wywołanie funkcji ChildService.perform() spowoduje wyświetlenie alertu z komunikatem "World!".

Co to jest właściwy sposób?

+0

Użyj ulubionego smaku dziedziczenia javascript, a następnie dodaj obiekty wynikowe jako usługi. Należy pamiętać, że ponieważ usługi są pojedyncze, należy dodać pojedynczą instancję lub konstruktora. – Sacho

Odpowiedz

30

angularjs nie przewiduje żadnego mechanizmu wdrożenia dziedziczenie usług bezpośrednio, jednak w Twoim przypadku można użyć $provide.decorator przedłużyć BaseService się lub używaj go jak prototyp innego ChildService używając zwykłego JavaScript. W mojej praktyce, aby mieć usługę z konfigurowalnym stanem i zachowaniem, używam providers. We wszystkich poniższych przykładach wyjście konsoli będzie Świat.

Dekorator

Jeśli nie potrzebujesz oryginalnego BaseService w module, można ozdobić go

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

prototypowa Dziedziczenie

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

Dekoratory nie są formą dziedziczenia - jest to jedynie dekoracja danej usługi, np. w dziedziczeniu masz dwa ostatnie artefakty: BaseService <- Dziedziczenie ChildService, natomiast z dekoracją masz tylko BaseService, po tym jak została udekorowana. – Sacho

+2

@Sacho Jestem świadomy, że dekoratorzy nie są dziedziczeniem, jednak twierdzę, że aby uzyskać usługę, która jest nieco inna niż w przypadku innej usługi (a inna usługa nie jest używana w jej oryginalnej formie) dziedziczenie nie jest w razie potrzeby można użyć dekoratora istniejącej usługi. – Vadim

5

Oto przykład oparty na konstruktorze/nowym dziedziczeniu (którego generalnie nie polecam).

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) 

A potem po prostu włączyć te jak usługi:

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

Czy możesz rozwinąć swój komentarz "Ogólnie polecam przeciwko"? Wydaje mi się, że będzie dobrze. –

+1

Dziedziczenie jest ogólnie rzecz biorąc niezręczne - jest korzystne tylko w bardzo szczególnych przypadkach, w porównaniu do bardzo intensywnego użytkowania. Dziedziczenie JavaScriptu jest dodatkową warstwą powodującą kłopotliwość na wierzchu, ze względu na sposób, w jaki 'ten' jest związany z wywołaniami funkcji. Dziedziczenie oparte na konstruktorze/klasycznym w javascriptu dodaje dodatkową warstwę dezorientacji (np. Zwróć uwagę, w jaki sposób muszę użyć Object.create, aby rozszerzyć prototypowane obiekty, rozważ pracę, którą musiałbyś wykonać, aby zaimplementować wywołania super(), potrzebę. funkcje bind(), które byłyby używane jako callbacki, lista jest kontynuowana). Kompozycja obiektu działa płynniej. – Sacho

+1

ładny przykład. Wierzę, że powinno to być 'this.message' zamiast' service.message', ponieważ nie korzystałeś z obiektu usługi. – Greg

12

chciałbym zmodyfikować trochę swój kod:

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

(ja po prostu zmienić swoje alertService dla console.log(); ..)

następnie wdrożyć dziedziczenia tak:

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

}]); 

Można zobaczyć example jak to działa .. w końcu BaseService i childService byłyby przypadki konstruktora BaseService (serwis).

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

To jest fajny sposób, aby przejść "w domu" (robi rzeczy w deklaracjach usług), nie myślałem o tym :) – Sacho

2

Moduł A z usług 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); 

Moduł B z usług BSvc która dziedziczy z 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); 

Teraz, gdy dzwonisz BSvc.getInstructions(30775);, jesteś nazywając rodziców usługa (ASvc) getInstructions funkcja z BSvc, a kiedy zadzwonisz pod numer BSvc.list(), nazywasz metodę wh Zostały one zmienione z ASvc w BSvc. Dziedzictwo.

A tak przy okazji, kiedy jestem przechodzącej angular jako argument do zamknięcia, a nie odnosząc się do globalnego angular zmiennej bezpośrednio z nim, jestem pozwalając minifiers kod i obfuscators robić rzeczy tak:

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

Warto mieć to na uwadze i uważam, że jest to dobra praktyka;)

+1

Argument obfuratora nie liczy się tutaj: '1.' Twój kod wykorzystuje około ~ 15 dodatkowe znaki. '2.'jeśli nie chcesz zanieczyszczać przestrzeni nazw okna, możesz po prostu połączyć te połączenia i zapisać dodatkowe znaki:' angular.module ('A', []). service (...). service (...) ; ' –

+1

@BenjaminM OK może zaciemnienie nie jest idealne, ale zminimalizowanie działa bardzo dobrze. Połączenia łańcuchowe generują bardzo brzydki kod na mojej opinii, a ponieważ przez większość czasu napisany przeze mnie kod zostanie przetworzony w konkatenację i zminimalizowanie, bardzo zależy mi na czytelności oryginalnego kodu. A jeśli chodzi o zamknięcie, nawet jeśli odnosi się do globalnej zmiennej "kątowej", to nawyk, do którego się nauczyłem, obejmuje moduły w zamknięciach. W najgorszym przypadku po prostu nie zaszkodzi. –