josef.van.niekerk
josef.van.niekerk

Reputation: 12121

Binding an element to a BehaviorSubject in an Angular service

I have a small shared Angular service which looks as follows:

import {Injectable} from '@angular/core';
import {BehaviorSubject, Observable} from 'rxjs/Rx';

@Injectable()
export class SidebarService {
    private _isOpen: BehaviorSubject<boolean> = new BehaviorSubject(false);

    constructor() {
        this._isOpen.subscribe(val => {
            console.log(`isOpen: ${val}`);
        });
    }

    public get isOpen() {
        return this._isOpen.asObservable();
    }

    toggle() {
        this._isOpen.next(!this._isOpen.getValue());
    }
}

And I would like to bind the .isOpen property to an element in a view. I am currently using the following snippet to attempt binding in the view:

<p>Sidebar State: {{sidebarService?.isOpen | async}}</p>

The property is binding initially, however not responding to successive changes. What I want is for Sidebar State: to update when the sidebar toggle() method is called inside SidebarService.

Any ideas?

Upvotes: 3

Views: 6035

Answers (1)

josef.van.niekerk
josef.van.niekerk

Reputation: 12121

The issue was caused by having multiple instances of the SidebarService running.

I fixed the issue by moving the declarations for providers in the @Component decorators from two components that shared the SidebarService to app.module.ts like so:

@NgModule({
    declarations: [
        // ...
        SidebarComponent,
        // ...
    ],
    imports: [ /* ... */ ])
    ],
    // Line below was duplicated in two components,
    // causing multiple instances...
    providers: [SidebarService], // <--
    bootstrap: [AppComponent]
})

This means that my binding was observing changes on a whole different instance of an Observable all together.

I also found that the .asObservable() call is entirely unnecessary, so the resulting SidebarService now looks as follows:

import {Injectable} from '@angular/core';
import {BehaviorSubject, Observable} from 'rxjs/Rx';

@Injectable()
export class SidebarService {
    private _isOpen: BehaviorSubject<boolean>;

    constructor() {
        this._isOpen = new BehaviorSubject(true);
    }

    public get isOpen() {
        return this._isOpen;
    }

    toggle() {
        this._isOpen.next(!this._isOpen.getValue());
    }
}

Upvotes: 6

Related Questions