我正在使用Ionic 4,我想显示当前登录用户的信息,但是没有工作,我的意思是它在任何组件上都可以工作,除了应用程序组件,我使用了一个名为userData的变量,这是一个BehaviorSubject。你知道我的代码有什么问题吗?
auth.service.ts
import { Platform } from '@ionic/angular';
import { Injectable } from '@angular/core';
import { Storage } from '@ionic/storage';
import { BehaviorSubject, Observable, from, of, throwError } from 'rxjs';
import { take, map,tap, switchMap, catchError } from 'rxjs/operators';
import { JwtHelperService } from "@auth0/angular-jwt";
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { Tokens } from '../models/tokens';
import { environment } from 'src/environments/environment';
const helper = new JwtHelperService();
//Agregar token access y refresh
const accessToken = 'access';
const refreshToken = 'refresh';
@Injectable({
providedIn: 'root'
})
export class AuthService {
public user: Observable<any>;
private userData = new BehaviorSubject(null);
private access: string = null;
private refresh: string = null;
constructor(private storage: Storage, private http: HttpClient, private plt: Platform, private router: Router) {
this.loadStoredTokens();
}
getTokenExpirationDate(userData): Date {
if (userData['exp'] === undefined) return null;
const date = new Date(0);
date.setUTCSeconds(userData['exp']);
return date;
}
isAcessTokenExpired(): boolean {
let userData = this.userData.getValue();
console.log(userData);
if(!userData) return true;
const date = this.getTokenExpirationDate(userData);
if(date === undefined) return false;
return !(date.valueOf() > new Date().valueOf());
}
loadStoredTokens() {
let platformObs = from(this.plt.ready());
this.user = platformObs.pipe(
switchMap(() => {
return from(this.storage.get(refreshToken));
}),
map(refreshToken => {
if (refreshToken) {
this.refresh = refreshToken;
} else {
this.refresh = null;
}
}),
switchMap(() => {
return from(this.storage.get(accessToken));
}),
map(accessToken => {
if (accessToken) {
this.access = accessToken;
let decoded = helper.decodeToken(accessToken);
this.userData.next(decoded);
return true;
} else {
this.access = null;
return null;
}
})
);
}
login(credentials: {username: string, password: string }) {
return this.http
.post(`${environment.apiUrl}/token/obtain/`,credentials)
.pipe(
take(1),
map(res => {
// Extract the JWT
return res;
}),
switchMap(token => {
let decoded = helper.decodeToken(token['access']);
this.userData.next(decoded);
from(this.storage.set(refreshToken, token['refresh']))
let storageObs = from(this.storage.set(accessToken, token['access']));
return storageObs;
})
);
}
getUser() {
return this.userData.getValue();
}
logout() {
this.storage.remove(accessToken).then(() => {
this.storage.remove(refreshToken).then(() => {
this.router.navigateByUrl('/');
this.userData.next(null);
});
});
}
getAccessToken() {
return this.access;
}
getRefreshToken() {
return this.refresh;
}
setAccessToken(token) {
this.storage.set(accessToken, token);
}
refreshToken() {
return this.http.post<any>(`${environment.apiUrl}/token/refresh/`, {
'refresh': this.getRefreshToken()
}).pipe(tap((tokens: Tokens) => {
this.setAccessToken(tokens.access);
this.access = tokens.access;
}));
}
}
app.component.ts (我使用getUser()函数来获取当前用户信息)
import { Component, OnInit, ViewChildren, QueryList, OnChanges } from '@angular/core';
import { Platform, ModalController, ActionSheetController, PopoverController, IonRouterOutlet, MenuController } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';
import { Router } from '@angular/router';
import { faHome, faInfo, faFileContract } from '@fortawesome/free-solid-svg-icons';
import { ToastController } from '@ionic/angular';
import { AuthService } from './auth/services/auth.service';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss']
})
export class AppComponent implements OnInit {
public selectedIndex = 0;
user = null;
public appPages = [
{
title: 'Home',
url: '/home',
icon: faHome
},
{
title: 'About',
url: '/about',
icon: faInfo
},
{
title: 'Legal',
url: '/legal',
icon: faFileContract
}
];
constructor(
private platform: Platform,
private splashScreen: SplashScreen,
private statusBar: StatusBar,
public modalCtrl: ModalController,
private menu: MenuController,
private actionSheetCtrl: ActionSheetController,
private popoverCtrl: PopoverController,
private router: Router,
public toastController: ToastController,
private auth: AuthService
) {
this.initializeApp();
}
initializeApp() {
this.platform.ready().then(() => {
this.statusBar.backgroundColorByHexString('#D4AF37');
this.splashScreen.hide();
});
}
ngOnInit() {
this.user = this.auth.getUser();
const path = window.location.pathname.split('home/')[1];
if (path !== undefined) {
this.selectedIndex = this.appPages.findIndex(page => page.title.toLowerCase() === path.toLowerCase());
}
}
}
app.component.html (这里我想显示用户信息,除了这个组件,其他组件的代码都是一样的)
<ion-app>
<ion-split-pane contentId="main-content">
<ion-menu contentId="main-content" type="overlay">
<ion-content>
<ion-list id="inbox-list">
<ion-list-header>Menu</ion-list-header>
<ion-note *ngIf="user">{{ user.email }} </ion-note>
<ion-menu-toggle auto-hide="false" *ngFor="let p of appPages; let i = index">
<ion-item (click)="selectedIndex = i" routerDirection="root" [routerLink]="[p.url]" lines="none" detail="false" [class.selected]="selectedIndex == i">
<fa-icon (keyup)="onKeyFaIcon($event)" slot="start" [icon]="p.icon"></fa-icon>
<ion-label>{{ p.title }}</ion-label>
</ion-item>
</ion-menu-toggle>
</ion-list>
</ion-content>
</ion-menu>
<ion-router-outlet id="main-content"></ion-router-outlet>
</ion-split-pane>
</ion-app>
它出现在 userData
变量在函数中被赋予一个新的值 loadStoredTokens()
和 login()
. 我没看到 login()
在任何地方调用的函数和 loadStoreTokens()
函数被错误地同步调用。它处理数据是异步的,数据流也应该是异步的。
服务
export class AuthService {
constructor(private storage: Storage, private http: HttpClient, private plt: Platform, private router: Router) {
this.loadStoredTokens().subscribe(
accessToken => {
let decoded = helper.decodeToken(accessToken);
this.userData.next(decoded);
},
);
}
loadStoredTokens() {
const result = new Subject<any>();
let platformObs = from(this.plt.ready());
this.user = platformObs.pipe(
switchMap(() => {
return from(this.storage.get(refreshToken));
}),
map(refreshToken => {
if (refreshToken) {
this.refresh = refreshToken;
} else {
this.refresh = null;
}
}),
switchMap(() => {
return from(this.storage.get(accessToken));
}),
map(accessToken => {
if (accessToken) {
this.access = accessToken;
result.next(accessToken);
} else {
this.access = null;
result.next(null);
}
})
);
return result.asObservable();
}
此外,如果你使用的是 getValue()
的功能 BehaviorSubject
并没有使用任何多播观察器。相反,你可以跳过 getValue()
的服务中,并在组件中订阅它。
服务中的
getUser() {
return this.userData.asObservable();
}
组成部分
ngOnInit() {
this.auth.getUser().subscribe(
user => { this.user = user }
);
}
使用安全导航仪 ?.
的模板中,检查是否有 user
变量在试图访问它的属性之前已经被定义。因为最初的值是 null
的默认值指定的。BehaviorSubject
.
<ion-note *ngIf="user">{{ user?.email }} </ion-note>
或者,你可以跳过组件控制器中的订阅,而使用 async
模板中的管道。
组成部分
ngOnInit() {
this.user = this.auth.getUser();
}
模板
<ion-note *ngIf="user">{{ (user | async)?.email }} </ion-note>
因为BehaviorSubject是异步的,你需要订阅它。
ngOnInit() {
this.auth.getUser().subscribe(user => this.user = user);
}
同时要记住,它不是即时的,所有的依赖性都在 this.user
也应该是异步的。
例如在模板中,你可以检查
<ng-container *ngIf="user">
{{ user }} now we can use it
</ng-container>
请使用async管道来实现。由于app加载时用户没有数据。请按照下面的代码,可能会对你有所帮助。
app.component.html
<ion-app>
<ion-split-pane contentId="main-content">
<ion-menu contentId="main-content" type="overlay">
<ion-content>
<ion-list id="inbox-list">
<ion-list-header>Menu</ion-list-header>
<ion-note *ngIf="user">{{ auth?.userData?.email | async }} </ion-note>
</ion-list>
</ion-content>
</ion-menu>
<ion-router-outlet id="main-content"></ion-router-outlet>
在userData对象中保留邮件属性。并保持AuthService作用域为public,同时在用户数据对象中注入。
app.component.ts constructor.