【发布时间】:2020-12-25 19:48:05
【问题描述】:
我正在开发一个带有 JWT 令牌身份验证的角度网站,一旦用户通过身份验证,用户数据就会存储在所有其他组件订阅的行为主体中。到目前为止,我有 10 个组件都运行良好,因为我在 HTML 部分中使用了可观察的数据。
但是在这个允许用户更改其他用户角色的当前组件中,我想让所有用户的角色低于当前用户的角色。所以我使用以当前用户角色作为参数的 API 调用来获取所需用户的列表。当我使用 routerLink 来到这个组件时,这完全正常,但是当我刷新或直接来到这个 url 时,它会引发以下错误:
您可以看到来自 API 调用的 Console.log(result) 在错误(用户信息对象)之后被调用,因此可能在来自 API 调用的数据发布到 behaviorSubject 之前调用了组件的 ngOnInit。
主应用组件: (在每次页面加载时调用 getCurrentUser 以检查本地存储中的用户凭据和令牌)
import { Component, OnInit } from '@angular/core';
import { AppService } from './Services/app.service';
import { error } from '@angular/compiler/src/util';
import { ThrowStmt } from '@angular/compiler';
import { BehaviorSubject } from 'rxjs';
import { AuthService } from './Services/auth.service';
import { LoggedInUser } from './Models/app.context';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'webapp';
currentUser : LoggedInUser;
loginError : string;
isAuthenticated : boolean
constructor(private _appService : AppService,
private _authService : AuthService) {
this.isAuthenticated = false;
this._authService.isAuthenticated$.subscribe(value => this.isAuthenticated = value)
this._authService.currentUserSubject$.subscribe(user => this.currentUser = user )
}
ngOnInit() {
this._authService.GetCurrentUser()
}
}
AuthService:
@Injectable({
providedIn: 'root'
})
export class AuthService {
public currentUserSubject$ = new BehaviorSubject<any>(null);
public isAuthenticated$ = new BehaviorSubject<boolean>(false);
currentUser = this.currentUserSubject$.asObservable();
constructor(private apiService: HttpService,
private router: Router) { }
GetCurrentUser() {
if(!localStorage.getItem(ACCESS_TOKEN)) {
return;
}
return this.apiService.getData(environment.API_BASE_URI + "/api/v1/users/me").subscribe(
result => {
debugger;
console.log(result)
this.currentUserSubject$.next(result)
this.isAuthenticated$.next(true)
},
error => (this.HandleLoginError())
);
}
IsUserLoggedIn() : boolean {
if(localStorage.getItem(ACCESS_TOKEN)) {
return true
}
return false;
}
Login(loginDetails) {
return this.apiService.postData(environment.API_BASE_URI + "/auth/login",loginDetails)
}
SignUp(signupDetails) {
return this.apiService.postData(environment.API_BASE_URI + "/auth/signup",signupDetails)
}
HandleLogoutEvent() {
localStorage.removeItem(ACCESS_TOKEN)
this.currentUserSubject$.next(null);
this.isAuthenticated$.next(false);
this.router.navigate(['account/login'])
}
HandleLoginError(){
this.currentUserSubject$.next(null)
this.isAuthenticated$.next(false);
}
HandleBackClick(){
}
}
提供访问组件
@Component({
selector: 'app-provide-access',
templateUrl: './provide-access.component.html',
styleUrls: ['./provide-access.component.css']
})
export class ProvideAccessComponent implements OnInit {
currentUser : User;
UserAccessForm : FormGroup
error = '';
selectedUser : string;
selectedRole : number;
usersList : Array<User>
rolesList : Array<Role>
constructor(private formBuilder : FormBuilder,
private _authService : AuthService,
private _appService : AppService) {
this.UserAccessForm = this.formBuilder.group({
selectedUser: '',
selectedRole: ''
});
}
ngOnInit(): void {
this._authService.currentUser.subscribe(user => this.currentUser = user);
this._appService.getAllUsersForAccess(this.currentUser.roleId).subscribe(
result => {
this.usersList = result;
},
error => {
console.log(error);
});
}
AccessRoleSubmit(accessRoleForm) {
//check if newpass and conf pass are same
console.log(accessRoleForm);
//update password api call
}
}
provide-access-component.html
<div class="col-md-9">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-md-12">
<h4>Give Access</h4>
<hr>
</div>
</div>
<div class="row">
<div class="col-md-12">
<form [formGroup]="UserAccessForm" (ngSubmit)="AccessRoleSubmit(UserAccessForm.value)" >
<div class="form-group row">
<label for="name" class="col-4 col-form-label">User</label>
<div class="col-8">
<select class="form-control" formControlName="selectedUser" id="userList">
<option *ngFor='let user of usersList' [value]="user.email">
{{user.email}}
</option>
</select>
</div>
</div>
<div class="form-group row">
<label for="email" class="col-4 col-form-label">Role</label>
<div class="col-8">
<select class="form-control" formControlName="selectedRole" id="emailList">
<option *ngFor='let role of rolesList' [value]="role?.id">
{{role?.role}}
</option>
</select>
</div>
</div>
<p class="h5-error">{{ error == '' ? '' : '*' + error }}</p>
<div class="form-group row">
<div class="offset-4 col-8">
<button name="submit" type="submit" class="btn btn-primary">Change Password</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
当我点击访问并来到这个组件时,列表被加载:
但是当我刷新或直接访问此 URL 时,我看到错误并且 UI 不加载项目:
仅供参考 - 我没有直接在组件之间传递任何数据,仅通过 authservice.. 路由有一个保护,不允许未登录的用户访问受限制的 url,它工作得很好
{path: 'admin/provide-access', component: ProvideAccessComponent, canActivate: [LoggedInGuard]}
我通常不会在这里发布任何内容,直到我在互联网上搜索了很多之后完全卡住了。如果需要,我可以提供更多代码。
谢谢
更新 1:
我尝试了 Micheal 给出的解决方案,因为我的 currentUser observable 是 type ,它会抛出错误,但即使在将其更改为 User 并运行它之后,我还是遇到了与以前相同的问题,但错误堆栈不同
我调试了它,它仍然首先执行 switchmap 行,然后在 getCurrentUser() 中执行订阅结果
【问题讨论】:
-
@Nikhil Kolte查看我的更新 -
@RafiHenig 刚试过,Overload 1 of 2, '(predicate: (value: User, index: number, source: Observable
) => value is User, thisArg?: any) : OperatorFunction ',给出了以下错误。类型“用户”不可分配给类型“布尔”。 Overload 2 of 2, '(predicate: (value: User, index: number, source: Observable ) => boolean, thisArg?: any): OperatorFunction ',给出以下错误。类型“用户”不可分配给类型“布尔”。在 find(user => user) 上抛出此错误,当我在 switchmap 之后移动它时,此错误消失 -
但是还是同样的问题,不确定它是否重要,find() 是否需要在 switchmap() 之前??
-
@sNikhil Kolte查看我的更新
标签: angular typescript asynchronous rxjs