我有一个简单的基于Angular2的Web应用程序,带有身份验证。我遇到了一个问题,根据用户是否登录导航栏看起来不同,并且当用户没有登录时,某些组件也不会显示。
问题是,例如,当用户点击logout
时,导航栏会更改,但其他组件在页面刷新之前不会消失。如何在按下LogOut按钮时触发组件刷新?
//user.service.ts
logout() {
localStorage.removeItem('id_token');
this.loggedIn = false;
}
//nav.component.ts
import { Component, Inject } from 'angular2/core';
import { RouteConfig, ROUTER_DIRECTIVES } from 'angular2/router';
import { UserService } from '../user/services/user.service';
@Component({
selector: 'nav-bar',
template: `
<div class="nav">
<a [routerLink]="['LoginComponent']" *ngIf="!_userService.isLoggedIn()">Login</a>
<a [routerLink]="['SignupComponent']" *ngIf="!_userService.isLoggedIn()">Sign Up</a>
<a [routerLink]="['TodoComponent']" *ngIf="_userService.isLoggedIn()">ToDo</a>
<button (click)="_userService.logout($event)" *ngIf="_userService.isLoggedIn()">Log Out</button>
</div>
`,
styleUrls: ['client/dev/todo/styles/todo.css'],
directives: [ROUTER_DIRECTIVES],
providers: [ UserService ]
})
export class NavComponent {
constructor(@Inject(UserService) private _userService: UserService) {}
}
导航组件呈现在路由器生成的任何内容之上。
如何触发要重置的组件?
答案 0 :(得分:6)
有必要(我认为)因为Angular 2不再有two-way-binding
。
在我的LoginService中,我使用了ReplaySubject(它类似于Observable对象,但是&#39;存储&#39;最后一个值,并且&#39;在它订阅的时候重放它)来自RxJS,然后我从我想要更新的组件订阅了ReplaySubject。
登录服务示例
import {ReplaySubject, Observable} from 'rxjs/Rx';
import {Injectable} from 'angular2/core';
@Injectable()
export class LoginService {
private logged = new ReplaySubject<boolean>(1); // Resend 1 old value to new subscribers
// isLoggedIn checks with server if user is already logged in.
// Returns true if logged in, and caches the result and user data.
isLoggedIn(): ReplaySubject<boolean> {
return this.logged;
}
// logIn attempts to log in a user, returns attempt result.
// Caches login status.
logIn(user: string, password: string): Observable<boolean> {
return Observable.timer(100).map(() => { // emulate a 100ms delay on login
this.logged.next(true);
return true;
})
}
logOut() {
// need api call to logout here
this.user = null;
this.logged.next(false);
}
}
示例组件
import {Component} from 'angular2/core';
import {LoginService} from "../user/login.service";
@Component({
selector: 'an-menu',
templateUrl: 'app/user/menu.html',
})
export class MenuComponent {
loggedIn: boolean;
constructor(private _loginService: LoginService) {
this._loginService.isLoggedIn()
.subscribe(r => {
this.loggedIn = r;
});
}
onLogout() {
this._loginService.logOut();
}
}
这样您就可以在模板中使用loggedIn
变量,也可以从不同的组件订阅ReplaySubject
。
答案 1 :(得分:2)
您有几个选择。最简单的方法是触发router
事件,该事件将导致角度重新评估您的绑定。这可能不是你想要的,因为你必须将路由器指向一个完全不同的“页面”。通常在这种情况下,我使用静态登录页面,该页面始终重定向回APP,而不是将登录逻辑构建到APP本身。从长远来看,这似乎在所有时间都有效。
但是,通过查看正在执行的操作,我们可以在触发该注销事件时更新您的组件。
logout() {
localStorage.removeItem('id_token');
this.loggedIn = false;
}
正如您所看到的,这是将该Model上的loggedIn
属性设置为false。这个属性由APP(Angular)“观察”。更新后,由于导航栏模板中的以下指令
*ngIf="!_userService.isLoggedIn()
该指令对Angular说; “如果此属性为FALSE,则呈现此内容”。由于它包含在你的@Component模板中,它在渲染时由Angular渲染和评估,但是当该属性发生变化时,它将继续从Angular接收更新。
这是您需要为每个需要此逻辑的组件做的事情....
“但这很多工作和跟踪”
你在那句话中说得对。我将定义这些组件的公共集合,然后将组件作为更高级别的集合包含在内,这样您就可以轻松地在顶级组件中的单个点进行更新。然后,顶级组件将设置一个'DIV',其中这些组件在同一ngIf
指令下进行模板化。