How to use Ionic Data Storage with BehaviorSubject and Observable

≡放荡痞女 提交于 2020-06-17 15:26:26

问题


I'm trying to create app with Angular 9 / Ionic 5

I'm using Ionic Data Storage

So, my auth.service.ts looks like:

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

import { Storage } from '@ionic/storage'

import { BehaviorSubject, Observable, from } from 'rxjs'

@Injectable({
    providedIn: 'root'
})

export class AuthService {

    private currentTokenSubject: BehaviorSubject<string>
    public currentToken: Observable<string>

    constructor(
        private http: HttpClient,
        private storage: Storage,
    ) {
        this.getToken()
            .then(res => {
                this.currentTokenSubject = new BehaviorSubject(res)
                this.currentToken = this.currentTokenSubject.asObservable()
            }
        )
    }

    async getToken() {
        return await this.storage.get('accessToken')
    }

    public get currentTokenValue(): string {
        return this.currentTokenSubject.value;
    }

    login(username: string, password: string) {
        const headers = new HttpHeaders({
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'Authorization': 'Basic ' + btoa(username + ':' + unescape(encodeURIComponent(password)))
        })

        return this.http.post<Token>(`${environment.apiUrl}/auth/signin`, { }, { headers })
            .pipe(map((res: Token) => {
                let token = res.token
                // store user details and jwt token in local storage to keep user logged in between page refreshes
                this.storage.set('accessToken', token);
                return token;
            }));
    }

    logout() {
        // remove user from local storage to log user out
        this.storage.remove('accessToken');
        this.currentTokenSubject.next(null);
    }
}

and jwt.interceptor.ts looks like:

import { Injectable } from '@angular/core'
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http'
import { Observable } from 'rxjs'

import { AuthService } from '@app/_services'

@Injectable()
export class JwtInterceptor implements HttpInterceptor {
    constructor(
        private authService: AuthService
    ) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // add authorization header with jwt token if available
        const currentToken = this.authService.currentTokenValue;

        if (currentToken) {
            request = request.clone({
                setHeaders: {
                    Authorization: `Bearer ${currentToken}`
                }
            });
        }

        return next.handle(request);
    }
}

So, when I try to call service, I get error, because Ionic Storage returns Observable:

Error: Uncaught (in promise): TypeError: Cannot read property 'value' of undefined
TypeError: Cannot read property 'value' of undefined
at AuthService.get currentTokenValue [as currentTokenValue] (auth.service.ts:39)

Question is: What is the proper way to get value from Ionic Storage and use it?


回答1:


The issue you are trying to access the BehaviorSubject's value getter before it is assigned any values. It is best to avoid the value getter and subscribe to the observable to keep things asynchronous. Try the following

auth.service.ts

export class AuthService {
  private currentTokenSubject = new BehaviorSubject<string>(null); // <-- assign default value here

  constructor(
    private http: HttpClient,
    private storage: Storage,
  ) {
    this.getToken().then(
      res => {
        this.currentTokenSubject.next(res);               // <-- push token to the observable
      }
    );
  }

  async getToken() {
    return await this.storage.get('accessToken');
  }

  public get currentTokenValue(): Observable < string > {
    return this.currentTokenSubject.asObservable();        // <-- return observable here
  }

  login(username: string, password: string) {
    const headers = new HttpHeaders({
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Authorization': 'Basic ' + btoa(username + ':' + unescape(encodeURIComponent(password)))
    })

    return this.http.post<Token>(`${environment.apiUrl}/auth/signin`, {}, { headers }).pipe(
      map((res: Token) => {
        let token = res.token;
        // store user details and jwt token in local storage to keep user logged in between page refreshes
        this.storage.set('accessToken', token);
        this.currentTokenSubject.next(token);              // <-- also push new token here?
        return token;
      }));
  }

  logout() {
    // remove user from local storage to log user out
    this.storage.remove('accessToken');
    this.currentTokenSubject.next(null);
  }
}

Now you need to subscribe to the function currentTokenValue() to retreive the token.

Some component/service

export class SomeComponent implements OnInit {
  token: string;

  ngOnInit() {
    this.authService.currentTokenValue().subscribe(
      token => { 
        if(token) {       // <-- check if token valid because it can also be null
          this.token = token;
        }
      }
    );
  }
}


来源:https://stackoverflow.com/questions/62187212/how-to-use-ionic-data-storage-with-behaviorsubject-and-observable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!