解决方案是基本的,您应该使用 Angular 最常见的功能。我将引导您完成思考过程,然后向您展示一些示例代码。
思考过程:
问题:我们需要随时了解用户是否登录。
解决方案:我们将提供一项服务,告诉我们是否用户是否登录
问题:导航栏要依赖用户的认证状态
解决方法:我们会使用认证服务返回的状态,有条件地显示一组基于用户的身份验证状态的项目或另一组项目
代码级问题:
您的代码中存在某些问题,这将使您难以进一步开发依赖于身份验证状态的其他功能。
我已经写了两个步骤来改进你的代码,第一个只是改进你的数据流和代码质量。第二步是用更动态的数据流完成更正后的代码。
第 1 步
服务
我们将在 Authentication Service 中有一个变量,告诉我们用户是否已经登录:
private isUserLoggedIn: boolean = false;
我们需要将所有身份验证逻辑移到身份验证服务中。由于我没有this.auth.loginUser(formData) 的代码,我将自己从新的身份验证服务中调用它,但请注意,该函数的代码应该在我们的新 login 函数中。
此外,没有必要将登录的 HTTP 调用保持为可观察的,因为您只会得到一个答案,因此我们可以使用 .toPromise() 将其转换为 Promise。
调用 API 的登录函数如下所示:
private apiLogin(formData): Promise<any> {
// the logic from your auth comes in here (use the content of this.auth.loginUser(formData) here)
// let's presume that we got the response from your 'this.auth.loginUser(formData)' here as loginObservable
return new Promise((resolve, reject) => {
this.auth.loginUser(formData);
.toPromise()
.then(data => {
// Form submit action here
if (data.userdata.resMsg === 'Login failed') {
// We clear the localStorage value, since the user is not logged in
localStorage.removeItem('loggedUser');
this.isUserLoggedIn = false;
reject('Email Address and Password do not match');
} else {
// We should update the localStorage
localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
this.isUserLoggedIn = true;
resolve();
}
})
.catch(error => {
this.isUserLoggedIn = false;
reject(error);
});
})
}
我们还想通过检查 localStorage 来检查用户是否登录(以防我们希望用户在每次刷新后不必登录):
双重否定 !! 告诉我们值是真还是假,所以如果我们在 localStorage 中的 loggedUser 键上有什么东西,我们会在用户登录时接受它
// Check if the user is logged in by checking the localStorage
private isAlreadyLoggedIn(): boolean {
return !!localStorage.getItem('loggedUser');
}
我们还需要在按下登录按钮时调用的登录函数(我们通过组件从服务调用它):
public login(formData): Promise<any> {
// If the user is logged in, send a promise resolvation, otherwise, send the promise of the apiLogin
if (this.isAlreadyLoggedIn) {
return Promise.resolve();
} else {
return this.apiLogin(formData);
}
}
为了使它完整,我们将首先检查用户是否登录(我们通过在服务的构造函数中调用 isAlreadyLoggedIn() 来做到这一点。此外,我们将有一个公共函数,通过它我们可以检查是否用户已登录:
constructor() {
// On initialization, check whether the user is already logged in or not
this.isUserLoggedIn = this.isAlreadyLoggedIn()
}
public isLoggedIn(): boolean {
return this.isUserLoggedIn;
}
完整的服务如下所示:
@Injectable()
export class AuthService {
private isUserLoggedIn: boolean = false;
constructor() {
// On initialization, check whether the user is already logged in or not
this.isUserLoggedIn = this.isAlreadyLoggedIn()
}
public login(formData): Promise<any> {
// If the user is logged in, send a promise resolvation, otherwise, send the promise of the apiLogin
if (this.isAlreadyLoggedIn) {
return Promise.resolve();
} else {
return this.apiLogin(formData);
}
}
public isLoggedIn(): boolean {
return this.isUserLoggedIn;
}
// Check if the user is logged in by checking the localStorage
private isAlreadyLoggedIn(): boolean {
return !!localStorage.getItem('loggedUser');
}
// Use this function to check if the user is already logged in
// Use this function to login on the server
private apiLogin(formData): Promise<any> {
// the logic from your auth comes in here (use the content of this.auth.loginUser(formData) here)
// let's presume that we got the response from your 'this.auth.loginUser(formData)' here as loginObservable
return new Promise((resolve, reject) => {
this.auth.loginUser(formData);
.toPromise()
.then(data => {
// Form submit action here
if (data.userdata.resMsg === 'Login failed') {
// We clear the localStorage value, since the user is not logged in
localStorage.removeItem('loggedUser');
this.isUserLoggedIn = false;
reject('Email Address and Password do not match');
} else {
// We should update the localStorage
localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
this.isUserLoggedIn = true;
resolve();
}
})
.catch(error => {
this.isUserLoggedIn = false;
reject(error);
});
})
}
}
登录组件:
这将检查用户是否在初始化时已经登录,如果是,那么我们将用户重定向到个人资料页面,否则我们显示登录表单。 (HTML 与您拥有的相同,我还将在span 标记中添加一个错误)。请注意,您的 login.ts 中缺少属性,我只是做了身份验证部分,添加了与表单相关的变量以使组件完整且正常运行。
@Component({
selector: 'app-login'
})
export class LoginComponent implements OnInit {
public isLoggedIn: boolean = false;
public error: string = '';
constructor(authService: AuthService, router: Router) {}
ngOnInit() {
this.isLoggedIn = this.authService.isLoggedIn();
if (this.isLoggedIn) {
this.router.navigate(['user-profile']);
}
}
loginUser(event) {
const formData = event.value;
this.authService.login(formData)
.then(res => this.router.navigate(['user-profile']))
.catch(error => this.error = error);
}
}
导航组件:
该组件获取用户的登录状态,并相应地呈现其项目:
@Component({
selector: 'app-nav'
})
export class NavComponent implements OnInit {
public isLoggedIn: boolean = false;
constructor(authService: AuthService) {}
ngOnInit() {
this.isLoggedIn = this.authService.isLoggedIn();
}
}
导航模板:
ng-template 是我们要显示的容器,以防用户未登录。
<ul *ngIf="isLoggedIn; else notLoggedIn">
<li>Home</li>
<li>Profile</li>
<li>Log Out</li>
</ul>
<ng-template #notLoggedIn>
<ul>
<li>Home</li>
<li>Log In</li>
</ul>
</ng-template>
这种方法是基本的,使用重定向。
第 2 步
我们现在可以用更动态的方式来完成这个(虽然我个人会坚持重定向):
我们将以下变量添加到我们的服务中:
private subject = new Subject();
private observable = this.subject.asObservable();
这样做的目的是我们可以从任何组件订阅observable,并且从服务中,我们可以通过subject 将数据实时传递给可观察对象的订阅者。更多关于这些,here
现在,每当我们更新登录状态时,我们都会调用以下内容:
this.subject.next(this.isUserLoggedIn);
这样,所有订阅者都将收到此更改的通知。
我们需要一个函数来返回组件可以订阅的 observable:
public isLoggedInObservable(): Observable<boolean> {
return this.observable;
}
剩下的就是从需要实时更新身份验证状态的组件中订阅这个 observable,在我们的例子中是导航组件(在 ngOnInit 中):
this.authService.isLoggedInObservable.subscribe(isLoggedIn => this.isLoggedIn = isLoggedIn);
这是最终服务的样子:
@Injectable()
export class AuthService {
private isUserLoggedIn: boolean = false;
private subject = new Subject();
private observable = this.subject.asObservable();
constructor() {
// On initialization, check whether the user is already logged in or not
this.isUserLoggedIn = this.isAlreadyLoggedIn()
}
public login(formData): Promise<any> {
// If the user is logged in, send a promise resolvation, otherwise, send the promise of the apiLogin
if (this.isAlreadyLoggedIn) {
return Promise.resolve();
} else {
return this.apiLogin(formData);
}
}
public isLoggedIn(): boolean {
return this.isUserLoggedIn;
}
public isLoggedInObservable(): Observable<boolean> {
return this.observable;
}
// Check if the user is logged in by checking the localStorage
private isAlreadyLoggedIn(): boolean {
return !!localStorage.getItem('loggedUser');
}
// Use this function to check if the user is already logged in
// Use this function to login on the server
private apiLogin(formData): Promise<any> {
// the logic from your auth comes in here (use the content of this.auth.loginUser(formData) here)
// let's presume that we got the response from your 'this.auth.loginUser(formData)' here as loginObservable
return new Promise((resolve, reject) => {
this.auth.loginUser(formData);
.toPromise()
.then(data => {
// Form submit action here
if (data.userdata.resMsg === 'Login failed') {
// We clear the localStorage value, since the user is not logged in
localStorage.removeItem('loggedUser');
this.isUserLoggedIn = false;
this.subject.next(this.isUserLoggedIn);
reject('Email Address and Password do not match');
} else {
// We should update the localStorage
localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
this.isUserLoggedIn = true;
this.subject.next(this.isUserLoggedIn);
resolve();
}
})
.catch(error => {
this.isUserLoggedIn = false;
reject(error);
});
})
}
}
这就是最终的导航组件的样子:
@Component({
selector: 'app-nav'
})
export class NavComponent implements OnInit {
public isLoggedIn: boolean = false;
constructor(authService: AuthService) {}
ngOnInit() {
this.isLoggedIn = this.authService.isLoggedIn();
this.authService.isLoggedInObservable.subscribe(isLoggedIn => this.isLoggedIn = isLoggedIn);
}
}
我希望这可以阐明代码的外观。回顾一下,您应该在服务中处理所有登录,并公开一个您可以从任何组件获取的boolean,以便您了解身份验证的状态并根据它采取行动,并且使用 observables,您将随时获取最新状态。