Angular - 如何统一auth observable?
Angular - How to unify auth observable?
我有一个 Angular 服务,它使用 AngularFire 的 auth observable 来监听用户的状态变化。当用户登录时,应用程序必须从 MongoDB 获取用户文档。这些数据需要被组件使用,所以我需要有另一个可观察的。问题是,我不太确定如何让它工作。
这是我的身份验证服务的片段。
import { Injectable } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import { Router } from '@angular/router';
import firebase from 'firebase/app';
import { environment } from '../../environments/environment'
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { User } from '../interfaces/User.model'
@Injectable({
providedIn: 'root'
})
export class AuthService {
public redirectRoute = ''
public loginError = '';
public _mongoUser: Observable<User | null> = of(null);
public mongoUser: User | null = null;
constructor(public auth: AngularFireAuth, private router: Router, private http: HttpClient,) {
this.auth.user.subscribe(async (user) => {
console.log('auth changed', user)
if (user) {
let headers = {
headers: new HttpHeaders()
.set('idToken', await user.getIdToken())
}
this._mongoUser = this.http.post<User>(
`${environment.apiUrl}/users/email/${user.email}`,
{ personal: { email: user.email } },
headers
)
this._mongoUser.subscribe(val => {
console.log('val', val)
this.mongoUser = val
})
} else {
}
})
}
}
主要问题是,我应该如何初始化_mongoUser?我认为使用...然后 httpClient 方法无法按我希望的方式工作。
我想在其他组件中像这样使用_mongoUser 或mongoUser。但是,我上面的代码不起作用。
constructor() {
this.authService._mongoUser.subscribe(val => {
if (val) {
this.editForm.patchValue({ 'username': val.username })
}
})
}
通过重新分配给 this._mongoUser
,您将丢弃在重新分配之前进行的所有订阅。
为避免重新分配,您可以使用 Subject
或 BehaviorSubject
。在这种情况下,我认为 BehaviorSubject
会更合适。它跟踪最近发出的项目和
这是您的示例的一个版本,已修改为使用 BehaviorSubject
。
import { Injectable } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import { Router } from '@angular/router';
import firebase from 'firebase/app';
import { environment } from '../../environments/environment'
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of, BehaviorSubject } from 'rxjs';
import { tap, filter, mergeMap } from 'rxjs/operators';
import { User } from '../interfaces/User.model'
@Injectable({
providedIn: 'root'
})
export class AuthService {
public redirectRoute = ''
public loginError = '';
// readonly so we never reassign this field
public readonly mongoUser: BehaviorSubject<User | null> = new BehaviorSubject(null);
constructor(public auth: AngularFireAuth, private router: Router, private http: HttpClient,) {
this.auth.user.pipe(
tap((user) => console.log('auth changed', user)),
filter((user) => !!user),
mergeMap(async (user) => ({ user, idToken: await user.getIdToken()})),
mergeMap(({user, idToken}) => {
let headers = {
headers: new HttpHeaders()
.set('idToken', idToken)
}
return this.http.post<User>(
`${environment.apiUrl}/users/email/${user.email}`,
{ personal: { email: user.email } },
headers
)
}),
).subscribe({
next: (userFromApi) => this.mongoUser.next(userFromApi)
});
}
}
重新分配对象
在 Javascript 中,如果您有一个对象的引用并且它被重新分配,您不会获得新的引用。
let exampleObject = {Hello: "World"};
const ref = exampleObject;
exampleObject = {New: "Value"};
console.log("ref: ", ref); // ref: {"Hello":"World"}
console.log("eo : ", exampleObject); // eo : {"New":"Value"}
所以您不想重新分配 public _mongoUser
因为其他服务可能有过时的引用。
您可以改用 behaviorSubject:
@Injectable({
providedIn: 'root'
})
export class AuthService {
public redirectRoute = ''
public loginError = '';
public _mongoUser = new BehaviorSubject<User | null>(null);
public mongoUser: User | null = null;
constructor(public auth: AngularFireAuth, private router: Router, private http: HttpClient,) {
this.auth.user.pipe(
map(user => user.getIdToken().pipe(
map(idToken => ({user, idToken}))
))
).subscribe(({user, idToken}) => {
console.log('auth changed', user)
if (user) {
let headers = {
headers: new HttpHeaders()
.set('idToken', idToken)
}
this.http.post<User>(
`${environment.apiUrl}/users/email/${user.email}`,
{ personal: { email: user.email } },
headers
).subscribe(this._mongoUser.next.bind(this._mongoUser));
this._mongoUser.subscribe(val => {
console.log('val', val)
this.mongoUser = val
})
} else {
}
})
}
}
一些清理工作
以上是应该开始的最小更改,但有几个地方您可以使用 RxJS 进一步简化您的代码:
@Injectable({
providedIn: 'root'
})
export class AuthService {
public redirectRoute = ''
public loginError = '';
public _mongoUser = new BehaviorSubject<User | null>(null);
public mongoUser: User | null = null;
constructor(public auth: AngularFireAuth, private router: Router, private http: HttpClient,) {
this._mongoUser.subscribe(val => {
console.log('val', val)
this.mongoUser = val
});
this.auth.user.pipe(
filter(user => user != null),
map(user => user.getIdToken().pipe(
map(idToken => ({
user,
headers: {
headers: new HttpHeaders().set('idToken', idToken)
}
}))
)),
concatMap(({user, headers}) => this.http.post<User>(
`${environment.apiUrl}/users/email/${user.email}`,
{ personal: { email: user.email } },
headers
))
).subscribe(
this._mongoUser.next.bind(this._mongoUser)
);
}
}
我有一个 Angular 服务,它使用 AngularFire 的 auth observable 来监听用户的状态变化。当用户登录时,应用程序必须从 MongoDB 获取用户文档。这些数据需要被组件使用,所以我需要有另一个可观察的。问题是,我不太确定如何让它工作。
这是我的身份验证服务的片段。
import { Injectable } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import { Router } from '@angular/router';
import firebase from 'firebase/app';
import { environment } from '../../environments/environment'
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { User } from '../interfaces/User.model'
@Injectable({
providedIn: 'root'
})
export class AuthService {
public redirectRoute = ''
public loginError = '';
public _mongoUser: Observable<User | null> = of(null);
public mongoUser: User | null = null;
constructor(public auth: AngularFireAuth, private router: Router, private http: HttpClient,) {
this.auth.user.subscribe(async (user) => {
console.log('auth changed', user)
if (user) {
let headers = {
headers: new HttpHeaders()
.set('idToken', await user.getIdToken())
}
this._mongoUser = this.http.post<User>(
`${environment.apiUrl}/users/email/${user.email}`,
{ personal: { email: user.email } },
headers
)
this._mongoUser.subscribe(val => {
console.log('val', val)
this.mongoUser = val
})
} else {
}
})
}
}
主要问题是,我应该如何初始化_mongoUser?我认为使用...然后 httpClient 方法无法按我希望的方式工作。
我想在其他组件中像这样使用_mongoUser 或mongoUser。但是,我上面的代码不起作用。
constructor() {
this.authService._mongoUser.subscribe(val => {
if (val) {
this.editForm.patchValue({ 'username': val.username })
}
})
}
通过重新分配给 this._mongoUser
,您将丢弃在重新分配之前进行的所有订阅。
为避免重新分配,您可以使用 Subject
或 BehaviorSubject
。在这种情况下,我认为 BehaviorSubject
会更合适。它跟踪最近发出的项目和
这是您的示例的一个版本,已修改为使用 BehaviorSubject
。
import { Injectable } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import { Router } from '@angular/router';
import firebase from 'firebase/app';
import { environment } from '../../environments/environment'
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of, BehaviorSubject } from 'rxjs';
import { tap, filter, mergeMap } from 'rxjs/operators';
import { User } from '../interfaces/User.model'
@Injectable({
providedIn: 'root'
})
export class AuthService {
public redirectRoute = ''
public loginError = '';
// readonly so we never reassign this field
public readonly mongoUser: BehaviorSubject<User | null> = new BehaviorSubject(null);
constructor(public auth: AngularFireAuth, private router: Router, private http: HttpClient,) {
this.auth.user.pipe(
tap((user) => console.log('auth changed', user)),
filter((user) => !!user),
mergeMap(async (user) => ({ user, idToken: await user.getIdToken()})),
mergeMap(({user, idToken}) => {
let headers = {
headers: new HttpHeaders()
.set('idToken', idToken)
}
return this.http.post<User>(
`${environment.apiUrl}/users/email/${user.email}`,
{ personal: { email: user.email } },
headers
)
}),
).subscribe({
next: (userFromApi) => this.mongoUser.next(userFromApi)
});
}
}
重新分配对象
在 Javascript 中,如果您有一个对象的引用并且它被重新分配,您不会获得新的引用。
let exampleObject = {Hello: "World"};
const ref = exampleObject;
exampleObject = {New: "Value"};
console.log("ref: ", ref); // ref: {"Hello":"World"}
console.log("eo : ", exampleObject); // eo : {"New":"Value"}
所以您不想重新分配 public _mongoUser
因为其他服务可能有过时的引用。
您可以改用 behaviorSubject:
@Injectable({
providedIn: 'root'
})
export class AuthService {
public redirectRoute = ''
public loginError = '';
public _mongoUser = new BehaviorSubject<User | null>(null);
public mongoUser: User | null = null;
constructor(public auth: AngularFireAuth, private router: Router, private http: HttpClient,) {
this.auth.user.pipe(
map(user => user.getIdToken().pipe(
map(idToken => ({user, idToken}))
))
).subscribe(({user, idToken}) => {
console.log('auth changed', user)
if (user) {
let headers = {
headers: new HttpHeaders()
.set('idToken', idToken)
}
this.http.post<User>(
`${environment.apiUrl}/users/email/${user.email}`,
{ personal: { email: user.email } },
headers
).subscribe(this._mongoUser.next.bind(this._mongoUser));
this._mongoUser.subscribe(val => {
console.log('val', val)
this.mongoUser = val
})
} else {
}
})
}
}
一些清理工作
以上是应该开始的最小更改,但有几个地方您可以使用 RxJS 进一步简化您的代码:
@Injectable({
providedIn: 'root'
})
export class AuthService {
public redirectRoute = ''
public loginError = '';
public _mongoUser = new BehaviorSubject<User | null>(null);
public mongoUser: User | null = null;
constructor(public auth: AngularFireAuth, private router: Router, private http: HttpClient,) {
this._mongoUser.subscribe(val => {
console.log('val', val)
this.mongoUser = val
});
this.auth.user.pipe(
filter(user => user != null),
map(user => user.getIdToken().pipe(
map(idToken => ({
user,
headers: {
headers: new HttpHeaders().set('idToken', idToken)
}
}))
)),
concatMap(({user, headers}) => this.http.post<User>(
`${environment.apiUrl}/users/email/${user.email}`,
{ personal: { email: user.email } },
headers
))
).subscribe(
this._mongoUser.next.bind(this._mongoUser)
);
}
}