角度学的新手,所以听起来可能是一个琐碎的问题,但是到目前为止,SO上提供的所有解决方案都无法正常工作。
我有一个简单的登录组件,在提交时,我将用户重定向到个人资料页面。我可以将用户发送到上述组件,但顶部的导航栏不会自动刷新,即我一直进行会话检查,因此当用户登录时,导航栏应自动显示Logout
按钮Login/Register
按钮。我的代码文件是这样的:
login-page.component.html
<form #loginForm="ngForm" (ngSubmit)="loginUser(loginForm)" id="loginForm" class="loginbackground">
<input ngModel #emailAddress="ngModel" type="text" autocomplete="off" placeholder="Email" id="emailAddress" name="emailAddress" />
<button type="submit" id="submit">LOGIN</button>
login-page.component.ts
@Output() refreshEvent = new EventEmitter<any>();
loginUser(event) {
// Validations. If successful, proceed
const formData = event.value;
this.auth.loginUser(formData);
.subscribe(data => {
localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
// Form submit action here
if (data.userdata.resMsg === 'Login failed') {
this.errorPopup = true;
this.errorText = 'Email Address and Password do not match';
} else {
this.refreshEvent.emit();
this.emailAvailable = true;
this.showLogin = false;
this.showRegister = false;
this.router.navigateByUrl('/404', { skipLocationChange: true }).then(() =>
this.router.navigate(['user-profile']));
}
});
});
}
问题
当我手动刷新页面时,导航栏会根据书面逻辑反映出所做的更改,效果很好。但是,我希望这种情况在用户实际登录并且不需要手动刷新页面时发生。
我尝试过的事情
ngOnInit()
刷新整个页面以重新加载
nac bar组件,但它进入了无限循环(这是
显然是黑客;但是为什么不这样有没有办法做到这一点?
答案 0 :(得分:1)
这是我解决的方法:
nav.component.html
...
<li *ngIf="!auth.isLoggedIn()">
...
</li>
<li *ngIf="auth.isLoggedIn()">
...
</li>
...
nav.component.ts
export class NavComponent implements OnInit {
constructor(public auth: AuthService) {
}
...
auth.service.ts
export class AuthService {
...
public isLoggedIn() {
return this.getId() !== null;
}
...
在最后一种方法中,“ this.getId()”可能是从localStorage获取令牌。
答案 1 :(得分:1)
此问题的解决方案是基本的,您应该使用Angular最常见的功能。我将指导您完成思考过程,然后向您展示一些示例代码。
思考过程:
问题:我们需要知道用户是否一直都在登录。
解决方案::我们将提供一项服务,告诉我们用户是否已登录
问题:导航栏应取决于用户的身份验证状态
解决方案:我们将使用身份验证服务返回的状态,根据用户的身份验证状态有条件地显示一组项目或另一组项目
代码级问题:
您的代码中存在某些问题,这将使您的生活难以继续开发依赖身份验证状态的其他功能。
我已经写了两个步骤来改进代码,这第一个步骤只是改善数据流和代码质量。第二步是使用更动态的数据流完成更正的代码。
服务
身份验证服务中将有一个变量,该变量告诉我们用户是否已经登录:
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>
这种方法将是使用重定向的基本方法。
我们现在可以采用更加动态的方式来完成此操作(尽管我个人会坚持使用重定向):
我们将在我们的服务中添加以下变量:
private subject = new Subject();
private observable = this.subject.asObservable();
这是什么,我们可以从任何组件订阅observable
,并且可以从服务中将实时数据通过subject
传递给可观察者的订户。有关这些的更多信息,here
现在,每当更新登录状态时,我们都会调用以下内容:
this.subject.next(this.isUserLoggedIn);
通过这种方式,所有订户将收到此更改的通知。
我们需要一个函数,该函数返回组件可以订阅的可观察对象:
public isLoggedInObservable(): Observable<boolean> {
return this.observable;
}
剩下的就是从需要实时更新有关身份验证状态的组件(在我们的例子中是nav组件(在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);
});
})
}
}
这就是最终的Nav组件的外观:
@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
,以便您了解身份验证的状态并基于身份验证以及可观察对象进行操作,您将始终获得最新状态。