Angular 2 Component listen to change in service
Asked Answered
A

2

31

I've a simple question about change detection.

I have a component and a (global) service with a boolean inside. How can I make the component listen to that boolean and execute a function if that boolean changes?

Thanks!

Aponeurosis answered 28/3, 2016 at 11:57 Comment(1)
See angular.io/docs/ts/latest/cookbook/…Gustave
H
41

Depending on how that boolean changes you could expose it as an Observable<boolean> on your service, and then subscribe to that stream in your component. Your service would look something like:

@Injectable()
export class MyBooleanService {
    myBool$: Observable<boolean>;

    private boolSubject: Subject<boolean>;

    constructor() {
        this.boolSubject = new Subject<boolean>();
        this.myBool$ = this.boolSubject.asObservable();
    }

    ...some code that emits new values using this.boolSubject...
}

Then in your component you would have something like this:

@Component({...})
export class MyComponent {
    currentBool: boolean;

    constructor(service: MyBooleanService) {
        service.myBool$.subscribe((newBool: boolean) => { this.currentBool = newBool; });
    }
}

Now depending on what you need to do with that bool value you may need to do some other things to get your component to update, but this is the gist of using an observable. Note, you will want to unsubscribe from the myBool$ stream at some point to prevent memory leaks and unexpected side effects.

Another option is you use the async pipe within your template instead of explicitly subscribing to the stream in the constructor. That will also ensure the subscription is disposed of automatically. Again though, that depends on what exactly you need to do with the bool values.

Herries answered 28/3, 2016 at 12:7 Comment(7)
You can get Subject from rxjsHerries
UserService.ts this._dataService.get<IUser[]>(this._userUrl) .subscribe(users => { self.user = Enumerable.from(users).firstOrDefault(e => e.Email == username && password == "1234"); if (!!self.user) { self._isLoggedInSubject.next(true); } }); Component isLoggedIn: boolean; constructor(private _userService: UserService) { this._userService.isLoggedIn.subscribe((response: boolean) => this.isLoggedIn = response); } didnt work for meProkofiev
@SamStorie Hey, it could be done with 1 line less by declaring the Subject like this: private boolSubject = new Subject<boolean>();. Good work mate! ;)Roband
Thanks. It helped me but I used the Subject only. Why are you doing this.myBool$ = this.boolSubject.asObservable();? For me the code also worked if I make boolSubject public and subscribe to it in the other component i.e. service.boolSubject.subscribe((newBool: boolean) => { this.currentBool = newBool; });. Any reason you converted the Subject to an Observable?Fumatorium
@ManuChadha It's because I didn't want to expose the Subject type to the client. That would give them the ability to push out new values, and could cause other problems. In general you want to use Subject's to push out values and strictly expose Observables to clients who need to subscribe to them.Herries
thanks. Well explained. One more related question (sort of). Is there a solution to the problem which uses only Observables? I am wondering why I would ever use an Observable over a Subject as Subject seem to be more versatile. I thought I could do Observable.create(observer=>{...}) but I suppose the complete logic of emitting values has to be in {...} block and I cannot emit values from other functions. Am I correct? Why would I use an Observable then instead of a Subject?Fumatorium
Answer sets in right direction, but forgets to implement guards against memory leaks. For that just assign the constructed service to a var of type Subscription, i.e. subscription: Subscription;, then in constructor set this.subscription = service.myBool$.subscribe(...), then in implemented ngOnDestroy() on component, implement this.subscription.unsubscribe();Merriott
P
17

The Sam's answer is completely right. I would just want to add that you could also leverage a TypeScript setter to automatically trigger the event for changes:

@Injectable()
export class MyBooleanService {
    myBool$: Observable<boolean>;

    private boolSubject: Subject<boolean>;
    private _myBool: Boolean;

    constructor() {
        this.boolSubject = new Subject<boolean>();
        this.myBool$ = this.boolSubject.asObservable();
    }

    set myBool(newValue) {
      this._myBool = newValue;
      this.boolSubject.next(newValue);
    }
}
Pederasty answered 28/3, 2016 at 12:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.