Przejdź do treści

Angular: Krótka historia o tym, jak złamałem system

Lubię krótkie wpisy. Tylko nie umiem ich pisać. Stąd też rzadko publikuję, bo wyobrażam sobie te wielogodzinne poświęcenie, aby napisać WSPANIAŁY, a zaraz długi artykułu traktującego o zadanym temacie…

Muszę nauczyć się pisać zwięźle i z mięsem. Sam lubię czytać takie rzeczy, więc czytelnicy tego bloga zapewne również. Do rzeczy.

Baner promujący artykuł

Problem

Jest sobie taki serwis:

@Injectable()
export class UserPurchasesStateManagerService {

    private purchasesState$ = new BehaviorSubject(...);

    public setPurchasesState(state: number): void {
        this.purchasesState$.next(state);
    }

    public getPurchasesState$(): BehaviorSubject<number> {
        return this.purchasesState$;
    }
}

Niby wszystko poprawnie, ale errrr…​

Wystarczy, że po utworzeniu instancji serwisu (przypominam, że w Angularze instancje serwisów są singletonami) zrobię tak:

// Sztuczne tworzenie, oczywiście Angular zrobi to niejawnie
const stateManager = new UserPurchasesStateManagerService();

// Boom!!
stateManager.getPurchasesState$().next(null);

…i złamałem system ☹️

Solucja

Trzeba zmienić definicję (i sygnaturę) gettera na:

// ...
export class UserPurchasesStateManagerService {
    // ...

    public getPurchasesState$(): Observable<number> {
        return this.purchasesState$.asObservable();
    }
}

A teraz krótka porada:

Porada

Jeśli gdzieś chcemy zwrócić subject-a to lepiej zrobić tak:

  • ukryć do private-em
  • zrobić getter, który go zwróci ale przekształci za pomocą .asObservable() w strumień read-only inaczej oddamy władzie zmiany stanu komuś innemu.