Czytałem, że wstrzykiwanie podczas ładowania początkowego powinno mieć wszystkie elementy podrzędne, które mają tę samą instancję, ale moje komponenty główne i nagłówkowe (główna aplikacja zawiera komponent nagłówka i gniazdo routera) otrzymują osobne wystąpienie moich usług.
Mam usługę FacebookService, której używam do wykonywania połączeń z interfejsem API javascript na Facebooku oraz usługę UserService, która korzysta z usługi FacebookService. Oto mój bootstrap:
bootstrap(MainAppComponent, [ROUTER_PROVIDERS, UserService, FacebookService]);
Z mojego logowania wygląda na to, że wywołanie bootstrap zakończyło się, a następnie widzę, że FacebookService, a następnie UserService są tworzone przed uruchomieniem kodu w każdym z konstruktorów, MainAppComponent, the HeaderComponent i DefaultComponent:
angular
typescript
angular2-routing
angular2-services
Jason Goemaat
źródło
źródło
UserService
iFacebookService
doproviders
nigdzie indziej?Odpowiedzi:
Jason ma całkowitą rację! Jest to spowodowane sposobem działania wstrzykiwania zależności. Opiera się na hierarchicznych wtryskiwaczach.
W aplikacji Angular2 jest kilka wtryskiwaczy:
Kiedy Angular2 próbuje wstrzyknąć coś do konstruktora komponentu:
Jeśli więc chcesz mieć singleton dla całej aplikacji, musisz zdefiniować dostawcę na poziomie wtryskiwacza głównego lub wtryskiwacza składnika aplikacji.
Ale Angular2 spojrzy na drzewo wtryskiwaczy od dołu. Oznacza to, że zostanie użyty dostawca na najniższym poziomie, a zakres powiązanej instancji będzie na tym poziomie.
Zobacz to pytanie, aby uzyskać więcej informacji:
źródło
Aktualizacja (Angular 6 +)
Zmienił się zalecany sposób tworzenia usługi singleton . Obecnie zaleca się określenie w
@Injectable
dekoratorze w usłudze, że ma być ona udostępniana w katalogu głównym. Ma to dla mnie duży sens i nie ma już potrzeby wymieniać wszystkich usług świadczonych w Twoich modułach. Po prostu importujesz usługi, kiedy ich potrzebujesz, a one rejestrują się we właściwym miejscu. Możesz również określić moduł, aby był dostarczany tylko wtedy, gdy moduł jest importowany.Aktualizacja (Angular 2)
W przypadku NgModule sposobem na zrobienie tego teraz, myślę, jest utworzenie „CoreModule” zawierającego klasę usług i wyświetlenie usługi u dostawców modułu. Następnie importujesz moduł podstawowy do modułu głównego aplikacji, który zapewni jedno wystąpienie każdemu dziecku żądającemu tej klasy w swoich konstruktorach:
CoreModule.ts
AppModule.ts
Oryginalna odpowiedź
Jeśli podajesz dostawcę w
bootstrap()
, nie musisz umieszczać go w dekoratorze komponentów:W rzeczywistości umieszczenie Twojej klasy w grupie „dostawcy” tworzy jej nową instancję, jeśli jakikolwiek komponent nadrzędny już ją wymienia, wówczas dzieci nie muszą tego robić, a jeśli to zrobią, otrzymają nową instancję.
źródło
[providers]
w pliku modułu, a nie wbootstrap()
, prawda?ApiService
wAppModule
„sproviders
, a tym samym uniknąć koniecznościCoreModule
? (Nie jestem pewien, co sugeruje @JasonSwett.)ApiService
na górze, ale po co w ogóle zawracać sobie głowę umieszczeniem go w tablicy dostawców CoreModule, a następnie zaimportować to w app.module ... jeszcze nie kliknęło.TestService
jest to określone zarówno w modułach podstawowych, jak i aplikacyjnych, instancje nie są tworzone dla modułów, ponieważ są one dostarczane przez komponent, więc kątowość nigdy nie jest tak wysoko w drzewie wtryskiwaczy. Jeśli więc udostępniasz usługę w swoim module i nigdy jej nie używasz, instancja nie wydaje się być utworzona.Wiem, że Angular ma hierarchiczne wtryskiwacze, jak powiedział Thierry.
Ale mam tutaj inną opcję na wypadek, gdybyś znalazł przypadek użycia, w którym tak naprawdę nie chcesz wstrzyknąć go rodzicowi.
Możemy to osiągnąć, tworząc instancję usługi i zawsze ją zwracamy.
A następnie w swoim komponencie używasz niestandardowej metody dostarczania.
I powinieneś mieć usługę singleton bez uzależnienia od hierarchicznych wtryskiwaczy.
Nie mówię, że to lepszy sposób, na wypadek gdyby ktoś miał problem, w którym hierarchiczne wtryskiwacze nie są możliwe.
źródło
SHARE_SERVICE_PROVIDER
znajdować sięYOUR_SERVICE_PROVIDER
w komponencie? Zakładam również, że importowanie pliku usługi jest potrzebne jak zwykle, a konstruktor nadal będzie miał parametr typu „YourService”, prawda? Myślę, że to mi się podoba, pozwala zagwarantować singleton i nie musi upewnić się, że usługa jest świadczona w hierarchii. Pozwala również poszczególnym komponentom uzyskać własną kopię, po prostu wymieniając usługęproviders
zamiast pojedynczego dostawcy, prawda?YOUR_SERVICE_PROVIDER
. Tak, wszystkie komponenty otrzymają tę samą instancję, po prostu dodając ją do dostawców.instance
właściwość w mapę instancji z kluczem i wartościąSkładnia została zmieniona. Sprawdź ten link
Zależności to pojedyncze elementy w obrębie wtryskiwacza. W poniższym przykładzie pojedyncza instancja HeroService jest współużytkowana przez HeroesComponent i jego elementy podrzędne HeroListComponent.
Krok 1. Utwórz klasę singleton z dekoratorem @Injectable
Krok 2. Wstrzyknij w konstruktorze
Krok 3. Zarejestruj dostawcę
źródło
Injectable
klasa nie jest usługą i zawiera tylkostatic
ciągi znaków do użytku globalnego?Dodanie
@Injectable
dekoratora do usługi ORAZ zarejestrowanie go jako dostawcy w module głównym sprawi, że będzie to singleton.źródło
constructor
ten sposób:import { SomeService } from '../../services/some/some'; @Component({ selector: 'page-selector', templateUrl: 'page.html', }) export class SomePage { constructor( public someService: SomeService ) { }
wydaje mi się, że to działa dobrze dla mnie
źródło
Oto działający przykład z wersją Angular 2.3. Po prostu wywołaj konstruktora usługi w sposób stand, jak ten konstruktor (private _userService: UserService). I utworzy singleton dla aplikacji.
user.service.ts
app.component.ts
źródło
A
singleton service
to usługa, dla której istnieje tylko jedna instancja w aplikacji.Istnieją (2) sposoby zapewnienia pojedynczej usługi dla aplikacji.
korzystać z
providedIn
nieruchomości lubudostępnić moduł bezpośrednio w
AppModule
aplikacjiKorzystanie z providedIn
Począwszy od Angular 6.0, preferowanym sposobem tworzenia pojedynczej usługi jest ustawienie
providedIn
roota na@Injectable()
dekoratorze usługi . To informuje Angular, aby udostępnił usługę w katalogu głównym aplikacji.Tablica dostawców NgModule
W aplikacjach zbudowanych z wersjami Angular wcześniejszymi niż 6.0 usługi są zarejestrowanymi tablicami dostawców NgModule w następujący sposób:
Gdyby to
NgModule
był katalog głównyAppModule
, usługa UserService byłaby pojedyncza i dostępna w całej aplikacji. Chociaż możesz zobaczyć, że jest to zakodowane w ten sposób, używanieprovidedIn
właściwości@Injectable()
dekoratora w samej usłudze jest lepsze od wersji Angular 6.0, ponieważ umożliwia wstrząsanie drzewem usług.źródło
Możesz użyć
useValue
w dostawcachźródło
useValue
nie jest powiązany z singletonem. Usevalue jest po prostu przekazaniem wartości zamiast aType
(useClass
), która jest wywoływana przez DInew
lubuseFactory
gdy przekazywana jest funkcja, która zwraca wartość, gdy jest wywoływana przez DI. Angular DI automatycznie utrzymuje jedno wystąpienie na dostawcę. Podaj go tylko raz i masz singletona. Przepraszam, muszę zagłosować, bo to tylko nieprawidłowa informacja: - /Od Angular @ 6 możesz mieć
providedIn
plikInjectable
.Sprawdź dokumenty tutaj
źródło
public static
.Po prostu zadeklaruj swoją usługę jako dostawcę tylko w app.module.ts.
Wykonało to za mnie pracę.
następnie uruchom go za pomocą prywatnego parametru konstruktora:
lub ponieważ jeśli twoja usługa jest używana z html, opcja -prod zażąda:
dodaj członka dla swojej usługi i wypełnij go wystąpieniem odebranym w konstruktorze:
źródło
Jeśli chcesz, aby usługa była pojedyncza na poziomie aplikacji, powinieneś zdefiniować ją w app.module.ts
dostawcy: [MyApplicationService] (możesz zdefiniować to samo w module podrzędnym, aby uczynić go specyficznym dla tego modułu)
Jeśli chcesz zdefiniować pojedynczą usługę na poziomie komponentu, utwórz usługę, dodaj tę usługę w app.module.ts i dodaj tablicę dostawców wewnątrz określonego komponentu, jak pokazano poniżej.
@Component ({selector: 'app-root', templateUrl: './test.component.html', styleUrls: ['./test.component.scss'], dostawcy: [TestMyService]})
Angular 6 zapewnia nowy sposób dodawania usług na poziomie aplikacji. Zamiast dodawać klasę usług do tablicy dostawców [] w AppModule, możesz ustawić następującą konfigurację w @Injectable ():
@Injectable ({providedIn: 'root'}) eksportuj klasę MyService {...}
„Nowa składnia” ma jednak jedną zaletę: usługi mogą być ładowane leniwie przez Angular (za kulisami), a nadmiarowy kod można usunąć automatycznie. Może to prowadzić do lepszej wydajności i szybkości ładowania - chociaż tak naprawdę działa to tylko w przypadku większych usług i ogólnie aplikacji.
źródło
Oprócz powyższych doskonałych odpowiedzi, może brakować czegoś jeszcze, jeśli rzeczy w Twoim singletonie nadal nie zachowują się jak singleton. Natknąłem się na problem podczas wywoływania funkcji publicznej na singletonie i stwierdzenia, że używa ona niewłaściwych zmiennych. Okazuje się, że problem polegał na tym, że
this
nie ma gwarancji, że będzie on powiązany z singletonem dla jakichkolwiek funkcji publicznych w singletonie. Można to skorygować stosując się do rad tutaj , tak jak poniżej:Alternatywnie możesz po prostu zadeklarować składowe klasy jako
public static
zamiastpublic
, wtedy kontekst nie będzie miał znaczenia, ale będziesz musiał uzyskać do nich dostęp,SubscriptableService.onServiceRequested$
zamiast używać iniekcji zależności i uzyskiwać do nich dostęp za pośrednictwemthis.subscriptableService.onServiceRequested$
.źródło
Usługi dla rodziców i dzieci
Miałem problem z usługą nadrzędną i jej dzieckiem korzystającym z różnych instancji. Aby wymusić użycie jednego wystąpienia, możesz utworzyć alias elementu nadrzędnego w odniesieniu do elementu podrzędnego w dostawcach modułów aplikacji. Nadrzędny nie będzie mógł uzyskać dostępu do właściwości dziecka, ale ta sama instancja będzie używana dla obu usług. https://angular.io/guide/dependency-injection-providers#aliased-class-providers
app.module.ts
Usługi używane przez składniki spoza zakresu modułów aplikacji
Podczas tworzenia biblioteki składającej się z komponentu i usługi natknąłem się na problem polegający na utworzeniu dwóch instancji. Jeden przez mój projekt Angular i jeden przez komponent w mojej bibliotece. Poprawka:
my-outside.component.ts
my-inside.component.ts
my-inside.component.hmtl
źródło