问题
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