Angular 2:验证时在导航栏中显示图标
Angular 2: Displaying icons in navbar on authentication
我的组件结构大致如下所示。我的应用程序组件有一个导航栏和路由器出口。导航栏有徽标、一些通用链接和一些仅在用户登录和身份验证时显示的特定链接。 router插座根据路由加载home组件或wall组件url。主页组件包含登录组件,其中包含常用的用户 ID、密码和提交按钮。提交并成功登录后,登录组件会发出一个事件。现在,如何在主组件(父组件)中捕获该事件?
如果我直接在应用程序下使用主页选择器,我可以捕获事件,将其冒泡到应用程序,然后使导航栏中的隐藏链接可见。
我不知道如何捕获 home 组件中登录组件发出的事件,因为它已加载到路由器输出中。
<!-- app.html -->
<div>
<nav>
<!-- Product logo -->
<!-- Some generic links -->
<!-- some hidden icons to be shown on authentication -->
</nav>
<router-outlet></router-outlet>
</div>
<!-- home.html -->
<div>
<login></login>
</div>
<!-- login.html -->
<div>
<!-- user name and password -->
<!-- submit button - the associated ts file raises an event on successful login -->
</div>
<!-- wall.html -->
<div>
<!-- Content to be displayed on authentication -->
</div>
谢谢,
希尔帕
我想通了这个问题,虽然我仍然没有解决方案。
结果是在地图上调用的方法无法访问 class 级别的对象。如果我在身份验证时调用发出事件,它工作得很好。
我尝试将事件发射器对象传递给映射方法,但这也无济于事。如何将对象传递到映射的方法范围?
我服务中的代码如下所示:
authenticate(authRequest: login): Observable<user> {
let url: string = this.apiUrl + AppSettings.LOGIN_SERVICE;
let headers = new Headers({
'Content-Type': AppSettings.CONTENT_TYPE_HEADER
});
let options = new RequestOptions({ headers: headers });
return this._http.post(url, authRequest, options) // ...using post request
.map(this.authenticated)
.catch(this.handleError);
//.catch(this.handleError);
}
private authenticated(res: Response) {
let body = res.json();
if (body.StatusCode === 200) {
localStorage.setItem('auth_token', res.headers.get("access-token"));
//This is the event declared at the class level.
//The following line of code is the one giving the error - cannot execute emit on undefined
this.authEvent.emit(true);
return body.Data || {};
}
else {
return {};
}
}
抱歉耽搁了,我是这样做的:
auth.service.ts
export class AuthService {
authChanged: EventEmitter<any> = new EventEmitter();
postLogin(f: ILoginForm) {
return this.http.post('/login', f)
.map(res => res.json())
.subscribe(data => this._checkLoginResponse(data));
}
/**
* Check Login Result
* @param data
*/
private _checkLoginResponse(data: any) {
// If Successful Login
if (data.data && data.meta && data.meta.token) {
// Set User & Token
localStorage.setItem('inctoken', data.meta.token);
localStorage.setItem('incuser', JSON.stringify(data.data));
// Emit Auth & User Events
this.authChanged.emit(this.getUser());
// Show OK Login Flash Message
this.flash.success('You have been logged in successfully.');
// Navigate Home
this.injector.get(Router).navigate(['/']);
}
}
/**
* Logout of Interface
* @returns boolean
*/
logout(withMsg = false): boolean {
// # Remove Token & User from LS
localStorage.removeItem('inctoken');
localStorage.removeItem('incuser');
// Emit Auth Events
this.authChanged.emit(false);
// Show Flash Message
if (withMsg)
this.flash.info('You have been logged out.', 'OK.');
// Redirect to Login Page
this.injector.get(Router).navigate(['/login']);
return true;
}
}
然后任何组件都可以注入 AuthService 并知道 authedUser 何时更改、注销等。以我的 Navbar 为例:
navbar.component.ts
export class NavbarComponent {
/**
* Authed User
*/
authedUser: IUser;
constructor(private authService: AuthService) {
this.authService.authChanged
.subscribe((user?: IUser) => {
// user will be false if logged out
// or user object if logged in.
this.authedUser = user;
});
}
}
然后在我的导航栏模板中,我可以选择根据身份验证状态显示内容:
<nav class="navbar">
<!-- Truncated -->
<ul class="menu-item"
*ngIf="authedUser">
<li><img [src]="authedUser.avatar"></li>
<!-- Whatever -->
</ul>
<ul class="menu-item"
*ngIf="!authedUser">
<li><a [routerLink]="['/login']">Login</a></li>
<!-- Whatever -->
</ul>
</nav>
显然,为了简洁起见,其中很多 class 都被截断了。
如果您对令牌如何在 header 中发送感到好奇,这里是我创建的 HttpClient class 的简单示例(为简洁起见被截断):
http.service.ts
export class HttpClient {
constructor(private http: Http) {
// ...
}
/**
* Get
* @param url
* @returns {Observable<any>}
*/
get(url): Observable<any> {
// Create New Headers
let headers = new Headers();
// Set Authorization Header
this._createAuthorizationHeader(headers);
// Create Observable to Return to Calling Service.
// We Dont Just Return the HTTP Observable Because
// We Need to Subscribe Here to Catch The Errors That
// May Be Thrown, Otherwise, Every Service Would Need
// To Call The Handle Errors Method
return Observable.create((observer) => {
// Fire Http GET Request, Subscribe & Catch Errors
return this.http.get(this.baseUrl + url, {
headers: headers
}).subscribe(
data => observer.next(data), // Emit Data Returned
err => this.handleError(err), // Catch Errors
() => observer.complete() // Emit Completed
);
});
}
/**
* Create Authorization Header
* @param {Headers} headers
*/
private _createAuthorizationHeader(headers: Headers) {
// If We Have A Token, Append It. The
// API Server Will Determine Its Validity
if (localStorage.getItem('inctoken')) {
headers.append('Authorization', 'Bearer: ' + localStorage.getItem('inctoken'));
}
}
}
然后在我的其他组件中,我可以注入我的 HttpClient class 并使用它自动将令牌放入 headers,即
some.component.ts
export class SomeComponent {
constructor(private http: HttpClient) {
// ...
}
private _getSomeData() {
return this.get('/someurl')
.map(res => res.json();
}
}
偶然发现这个解决方案 --> http://plnkr.co/edit/KfcdDi?p=info
//alert.service.ts
import { Injectable } from '@angular/core';
import { Router, NavigationStart } from '@angular/router';
import { Observable } from 'rxjs';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class AlertService {
private subject = new Subject<any>();
private keepAfterNavigationChange = false;
constructor(private router: Router) {
// clear alert message on route change
router.events.subscribe(event => {
if (event instanceof NavigationStart) {
if (this.keepAfterNavigationChange) {
// only keep for a single location change
this.keepAfterNavigationChange = false;
} else {
// clear alert
this.subject.next();
}
}
});
}
success(message: string, keepAfterNavigationChange = false) {
this.keepAfterNavigationChange = keepAfterNavigationChange;
this.subject.next({ type: 'success', text: message });
}
error(message: string, keepAfterNavigationChange = false) {
this.keepAfterNavigationChange = keepAfterNavigationChange;
this.subject.next({ type: 'error', text: message });
}
getMessage(): Observable<any> {
return this.subject.asObservable();
}
}
//login.component
private loggedIn(user1: user) {
this.currentUser = user1;
this._alertService.alert("login", true);
}
//app.component
ngOnInit(): void {
this.authenticated = this._authService.isLoggedIn();
this._alertService.getMessage().subscribe(data => this.setData(data));
}
private setData(data: any) {
if (!this.authenticated) {
if (data && (data.type === 'login') && data.success === true) {
this.authenticated = true;
}
else {
this.authenticated = false;
}
}
}
<!-- app.html -->
<nav class="navbar navbar-color">
<div class="container-fluid" id="nav_center">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed nav-expand-button"
data-toggle="collapse" data-target="#navbar-collapse1" aria-expanded="false"
*ngIf="authenticated">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<button type="button" class="nav-features nav-expand-button"
(click)="isCollapsed = !isCollapsed" *ngIf="authenticated">
<span class="sr-only">Navigate features</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Outili</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="navbar-collapse1" *ngIf="authenticated">
<!--*ngIf="showNotification">-->
<ul class="nav navbar-nav navbar-right">
<li class="navbar-icons">
<a href="#" class="navbar-a">
<span class="glyphicon glyphicon-inbox navbar-icons"></span>
</a>
</li>
<li class="dropdown navbar-icons">
<a href="#" class="dropdown-toggle navbar-a" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<span class="glyphicon glyphicon-user navbar-icons"></span>
<span class="caret navbar-icons"></span>
</a>
<ul class="dropdown-menu">
<li><a href="#">Profile</a></li>
<li><a href="#">Settings</a></li>
<li role="separator" class="divider"></li>
<li (click)="logout()"><button type="button" class="btn">Logout</button></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
这里的警报服务正是我要找的。
我的组件结构大致如下所示。我的应用程序组件有一个导航栏和路由器出口。导航栏有徽标、一些通用链接和一些仅在用户登录和身份验证时显示的特定链接。 router插座根据路由加载home组件或wall组件url。主页组件包含登录组件,其中包含常用的用户 ID、密码和提交按钮。提交并成功登录后,登录组件会发出一个事件。现在,如何在主组件(父组件)中捕获该事件?
如果我直接在应用程序下使用主页选择器,我可以捕获事件,将其冒泡到应用程序,然后使导航栏中的隐藏链接可见。
我不知道如何捕获 home 组件中登录组件发出的事件,因为它已加载到路由器输出中。
<!-- app.html -->
<div>
<nav>
<!-- Product logo -->
<!-- Some generic links -->
<!-- some hidden icons to be shown on authentication -->
</nav>
<router-outlet></router-outlet>
</div>
<!-- home.html -->
<div>
<login></login>
</div>
<!-- login.html -->
<div>
<!-- user name and password -->
<!-- submit button - the associated ts file raises an event on successful login -->
</div>
<!-- wall.html -->
<div>
<!-- Content to be displayed on authentication -->
</div>
谢谢, 希尔帕
我想通了这个问题,虽然我仍然没有解决方案。 结果是在地图上调用的方法无法访问 class 级别的对象。如果我在身份验证时调用发出事件,它工作得很好。 我尝试将事件发射器对象传递给映射方法,但这也无济于事。如何将对象传递到映射的方法范围?
我服务中的代码如下所示:
authenticate(authRequest: login): Observable<user> {
let url: string = this.apiUrl + AppSettings.LOGIN_SERVICE;
let headers = new Headers({
'Content-Type': AppSettings.CONTENT_TYPE_HEADER
});
let options = new RequestOptions({ headers: headers });
return this._http.post(url, authRequest, options) // ...using post request
.map(this.authenticated)
.catch(this.handleError);
//.catch(this.handleError);
}
private authenticated(res: Response) {
let body = res.json();
if (body.StatusCode === 200) {
localStorage.setItem('auth_token', res.headers.get("access-token"));
//This is the event declared at the class level.
//The following line of code is the one giving the error - cannot execute emit on undefined
this.authEvent.emit(true);
return body.Data || {};
}
else {
return {};
}
}
抱歉耽搁了,我是这样做的:
auth.service.ts
export class AuthService {
authChanged: EventEmitter<any> = new EventEmitter();
postLogin(f: ILoginForm) {
return this.http.post('/login', f)
.map(res => res.json())
.subscribe(data => this._checkLoginResponse(data));
}
/**
* Check Login Result
* @param data
*/
private _checkLoginResponse(data: any) {
// If Successful Login
if (data.data && data.meta && data.meta.token) {
// Set User & Token
localStorage.setItem('inctoken', data.meta.token);
localStorage.setItem('incuser', JSON.stringify(data.data));
// Emit Auth & User Events
this.authChanged.emit(this.getUser());
// Show OK Login Flash Message
this.flash.success('You have been logged in successfully.');
// Navigate Home
this.injector.get(Router).navigate(['/']);
}
}
/**
* Logout of Interface
* @returns boolean
*/
logout(withMsg = false): boolean {
// # Remove Token & User from LS
localStorage.removeItem('inctoken');
localStorage.removeItem('incuser');
// Emit Auth Events
this.authChanged.emit(false);
// Show Flash Message
if (withMsg)
this.flash.info('You have been logged out.', 'OK.');
// Redirect to Login Page
this.injector.get(Router).navigate(['/login']);
return true;
}
}
然后任何组件都可以注入 AuthService 并知道 authedUser 何时更改、注销等。以我的 Navbar 为例:
navbar.component.ts
export class NavbarComponent {
/**
* Authed User
*/
authedUser: IUser;
constructor(private authService: AuthService) {
this.authService.authChanged
.subscribe((user?: IUser) => {
// user will be false if logged out
// or user object if logged in.
this.authedUser = user;
});
}
}
然后在我的导航栏模板中,我可以选择根据身份验证状态显示内容:
<nav class="navbar">
<!-- Truncated -->
<ul class="menu-item"
*ngIf="authedUser">
<li><img [src]="authedUser.avatar"></li>
<!-- Whatever -->
</ul>
<ul class="menu-item"
*ngIf="!authedUser">
<li><a [routerLink]="['/login']">Login</a></li>
<!-- Whatever -->
</ul>
</nav>
显然,为了简洁起见,其中很多 class 都被截断了。
如果您对令牌如何在 header 中发送感到好奇,这里是我创建的 HttpClient class 的简单示例(为简洁起见被截断):
http.service.ts
export class HttpClient {
constructor(private http: Http) {
// ...
}
/**
* Get
* @param url
* @returns {Observable<any>}
*/
get(url): Observable<any> {
// Create New Headers
let headers = new Headers();
// Set Authorization Header
this._createAuthorizationHeader(headers);
// Create Observable to Return to Calling Service.
// We Dont Just Return the HTTP Observable Because
// We Need to Subscribe Here to Catch The Errors That
// May Be Thrown, Otherwise, Every Service Would Need
// To Call The Handle Errors Method
return Observable.create((observer) => {
// Fire Http GET Request, Subscribe & Catch Errors
return this.http.get(this.baseUrl + url, {
headers: headers
}).subscribe(
data => observer.next(data), // Emit Data Returned
err => this.handleError(err), // Catch Errors
() => observer.complete() // Emit Completed
);
});
}
/**
* Create Authorization Header
* @param {Headers} headers
*/
private _createAuthorizationHeader(headers: Headers) {
// If We Have A Token, Append It. The
// API Server Will Determine Its Validity
if (localStorage.getItem('inctoken')) {
headers.append('Authorization', 'Bearer: ' + localStorage.getItem('inctoken'));
}
}
}
然后在我的其他组件中,我可以注入我的 HttpClient class 并使用它自动将令牌放入 headers,即
some.component.ts
export class SomeComponent {
constructor(private http: HttpClient) {
// ...
}
private _getSomeData() {
return this.get('/someurl')
.map(res => res.json();
}
}
偶然发现这个解决方案 --> http://plnkr.co/edit/KfcdDi?p=info
//alert.service.ts
import { Injectable } from '@angular/core';
import { Router, NavigationStart } from '@angular/router';
import { Observable } from 'rxjs';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class AlertService {
private subject = new Subject<any>();
private keepAfterNavigationChange = false;
constructor(private router: Router) {
// clear alert message on route change
router.events.subscribe(event => {
if (event instanceof NavigationStart) {
if (this.keepAfterNavigationChange) {
// only keep for a single location change
this.keepAfterNavigationChange = false;
} else {
// clear alert
this.subject.next();
}
}
});
}
success(message: string, keepAfterNavigationChange = false) {
this.keepAfterNavigationChange = keepAfterNavigationChange;
this.subject.next({ type: 'success', text: message });
}
error(message: string, keepAfterNavigationChange = false) {
this.keepAfterNavigationChange = keepAfterNavigationChange;
this.subject.next({ type: 'error', text: message });
}
getMessage(): Observable<any> {
return this.subject.asObservable();
}
}
//login.component
private loggedIn(user1: user) {
this.currentUser = user1;
this._alertService.alert("login", true);
}
//app.component
ngOnInit(): void {
this.authenticated = this._authService.isLoggedIn();
this._alertService.getMessage().subscribe(data => this.setData(data));
}
private setData(data: any) {
if (!this.authenticated) {
if (data && (data.type === 'login') && data.success === true) {
this.authenticated = true;
}
else {
this.authenticated = false;
}
}
}
<!-- app.html -->
<nav class="navbar navbar-color">
<div class="container-fluid" id="nav_center">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed nav-expand-button"
data-toggle="collapse" data-target="#navbar-collapse1" aria-expanded="false"
*ngIf="authenticated">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<button type="button" class="nav-features nav-expand-button"
(click)="isCollapsed = !isCollapsed" *ngIf="authenticated">
<span class="sr-only">Navigate features</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Outili</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="navbar-collapse1" *ngIf="authenticated">
<!--*ngIf="showNotification">-->
<ul class="nav navbar-nav navbar-right">
<li class="navbar-icons">
<a href="#" class="navbar-a">
<span class="glyphicon glyphicon-inbox navbar-icons"></span>
</a>
</li>
<li class="dropdown navbar-icons">
<a href="#" class="dropdown-toggle navbar-a" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<span class="glyphicon glyphicon-user navbar-icons"></span>
<span class="caret navbar-icons"></span>
</a>
<ul class="dropdown-menu">
<li><a href="#">Profile</a></li>
<li><a href="#">Settings</a></li>
<li role="separator" class="divider"></li>
<li (click)="logout()"><button type="button" class="btn">Logout</button></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
这里的警报服务正是我要找的。