Cannot read property 'subscribe' of undefined angular

Sunil Garg picture Sunil Garg · Oct 4, 2018 · Viewed 7.7k times · Source

I have a service UserService that has a method getUsers() and this service is shared among different angular components.

The method calls an API and returns the data. I want to hold the result/state of last API call so I used BehaviorSubject to hold the last state so that if different components call getUsers() there should be only one API call otherwise hold state must be returned.

This is how I am implementing this

private _users$: BehaviorSubject<User[]>;

constructor(private http: HttpClient) {
    this._users$ = new BehaviorSubject([]);
}

getUsers(): BehaviorSubject<User[]> {
    if (!this._getUsersState().length) {
        this._loadUsers();
        this._users$.subscribe(() => {
            return this._users$;
        });
    } else {
        return this._users$;
    }
}

_loadUsers() {
    let _users$ = this.http.get(`${this._context}/users/`);
    _users$.subscribe((users: User[]) => {
        this._users$.next(Object.assign([], users));
    });
}

_getUsersState(): User[] {
    return this._users$.getValue();
}

And in the component

this.userService.getUsers().subscribe((users: any) => {
    console.log(users);
});

but I am getting the following error

ERROR TypeError: Cannot read property 'subscribe' of undefined

I guess get users returns automatically if the length is 0.

How can I solve this issue? And is this approach good?

Answer

Jacopo Sciampi picture Jacopo Sciampi · Oct 4, 2018

Edit your service like this :

private _users$ = new BehaviorSubject<User[]>(null);
public usersChanged = this._users$.asObservable();

constructor(private http: HttpClient) {
}

getUsers(): BehaviorSubject<User[]> {
    if (!this._getUsersState().length) {
        this._loadUsers();
        this._users$.subscribe(() => {
            return this._users$;
        });
    } else {
        return this._users$;
    }
}

_loadUsers() {
    let _users$ = this.http.get(`${this._context}/users/`);
    _users$.subscribe((users: User[]) => {
        this._users$.next(Object.assign([], users));
    });
}

_getUsersState(): User[] {
    return this._users$.getValue();
}

edit yout component logic like this:

this.userService.usersChanged
.subscribe((users: any) => {
  if(!!users){
    console.log(users);
  } else {
    console.log("No user found!");
  }
});

It's a good practise to have a variable that is an observable of your private behaviorSubject. So you just subscribe to it and whenever its state change, it'll emit.