Judson Terrell
Judson Terrell

Reputation: 4306

Angular 2 Observables cannot assign to boolean

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

Answers (1)

Lars Gyrup Brink Nielsen
Lars Gyrup Brink Nielsen

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

Related Questions