Reputation: 4306
I am trying to use Observables in angular 2 to watch for a simple change to a boolean called loggedIn which can be true or false. I am getting this error in TS.
Type 'boolean' is not assignable to type 'Subject'. Type 'boolean' is not assignable to type 'Observable'.
Can someone provide an alternative or tell me what I am doing wrong?
import { Injectable } from '@angular/core';
import { Http, Headers } from '@angular/http';
import {Subject} from "../../node_modules/rxjs/src/Subject";
import {Observable} from "../../node_modules/rxjs/src/Observable";
@Injectable()
export class UserService {
user: Subject<any>;
user$: Observable<any>;
//loggedIn: Subject<any>;
//loggedIn$: Observable<any>;
loggedIn: boolean;
constructor(private http: Http) {
this.user = new Subject();
this.user$ = this.user.asObservable();
this.loggedIn = false;
//this.loggedIn = new Subject();
//this.loggedIn$ = this.user.asObservable();
}
createAccount(user) {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http
.post(
'/api/accounts',
JSON.stringify(user),
{ headers }
)
.map(res => res.json())
.map((res) => {
if (res['success']) {
localStorage.setItem('auth_token', res['auth_token']);
//this.loggedIn$ = true;
//this.loggedIn.next(true);
}
return res['success'];
});
}
login(user) {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http
.post(
'/api/authenticate',
JSON.stringify(user),
{ headers }
)
.map(res => res.json())
.map((res) => {
console.log('Login Result:', res.user);
if (res["success"]) {
localStorage.setItem('jwt', res.token);
//set user service info...
//this.user.next(res.user[0]);
//this.loggedIn.next(true);
}
return res;
});
}
updateAccount(user) {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('x-access-token', localStorage.getItem('jwt'));
console.log('PAYLOAD FOR UPDATE USER: ' , user);
return this.http
.put(
'/api/accounts/' + user._id,
JSON.stringify(user),
{ headers }
)
.map(res => res.json())
.map((res) => {
if (res['success']) {
localStorage.setItem('auth_token', res['auth_token']);
//this.loggedIn$ = true;
//this.loggedIn.next(true);
}
return res['success'];
});
}
logout() {
localStorage.removeItem('auth_token');
//this.loggedIn$ = false;
//this.loggedIn.next(false);
}
}
Please see the lines that are commented out. I need to subscribe to the Observable which will return true or false at any time.
Upvotes: 3
Views: 6080
Reputation: 4105
Use a subject to emit values. Use private loggedIn: ReplaySubject<boolean> new ReplaySubject(1);
if you want to emit the state on subscription but do not want an initial state. If you want an initial state, use private loggedIn: BehaviorSubject<boolean> = new BehaviorSubject(false);
.
Do not present subjects as public properties - only expose observables, i.e. loggedIn$: Observable<boolean> = this.loggedIn.asObservable();
. Consumers of the service should only be able to alter the state through public methods in a controlled way.
These are some of the parts of your service that are concerned with the logged in state.
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, ReplaySubject } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class UserService {
private readonly loggedIn: ReplaySubject<boolean> = new ReplaySubject(1);
readonly loggedIn$: Observable<boolean> = this.loggedIn.asObservable();
constructor(private readonly http: HttpClient) {}
login(user): Promise<any> {
return this.http
.post(/* (...) */)
.map((res) => {
if (res["success"]) {
this.loggedIn.next(true);
}
});
}
logout(): void {
this.loggedIn.next(false);
}
}
So we expose the state as an Observable<boolean>
in the public loggedIn$
property which is only the Observable
interface of the private loggedIn
subject property. The state is changed by emitting a value to loggedIn
which is observed by subscribers of loggedIn$
.
Upvotes: 1