我的AuthService中有一个属性,可以告诉您是否登录。
export class AuthService {
token: string;
isLogIn = new Subject<boolean>();
当我正确登录时,另一方面,当我单击注销时,将isLogIn设置为true,将该属性设置为false。我的AppComponent中有这个逻辑(第一个组件已加载)。
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User is signed in.
this.authService.user = new User(user.displayName, user.email, user.photoURL, user.uid);
console.log(user);
this.isLoading = false;
this.authService.isLogIn.next(true);
} else {
// No user is signed in.
console.error('no user is login');
this.authService.isLogIn.next(false);
}
});
在我的HeaderComponent中,我有:
export class HeaderComponent implements OnInit {
isActivated = false;
constructor(private authService: AuthService) { }
ngOnInit() {
this.authService.isLogIn.asObservable().subscribe(
(data: boolean) => {
this.isActivated = data;
}
);
// this.isActivated = this.authService.isLogin();
}
}
模板:
<ng-template [ngIf]="!isActivated">
<a class="nav-item nav-link text-white anchor-hover"
routerLink="/login"
routerLinkActive="active font-weight-bold">
<i class="fa fa-sign-in pr-2" aria-hidden="true"></i>Ingresar
</a>
</ng-template>
<ng-template [ngIf]="isActivated">
<!--DROPDOWN PROFILE-->
<div class="btn-group" role="group" *ngIf="authService.user">
<button id="btnGroupDrop1" type="button" class="btn btn-light dropdown-toggle pointer" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
{{authService.user.displayName}}
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="btnGroupDrop1">
<a class="dropdown-item" href="#">Dropdown link</a>
<a class="dropdown-item" href="#">Dropdown link</a>
</div>
</div>
<a class="nav-item nav-link text-white anchor-hover"
routerLink="/dashboard"
routerLinkActive="active font-weight-bold">
<i class="fa fa-sign-in pr-2" aria-hidden="true"></i>Dashboard
</a>
</ng-template>
我在这里要解决的问题是,当用户登录时,我向他显示了一个仪表板按钮并隐藏了登录按钮,但这仅在刷新页面或更改当前路线时才会显示。我不想更改路线或刷新页面以查看不同的UI组件,具体取决于您是否登录。
现在,我让您查看整个文件:
AppRouting模块:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {HomeComponent} from './shared/home/home.component';
import {LoginComponent} from './public/login/login.component';
import {DashboardComponent} from './auth/dashboard/dashboard.component';
const routes: Routes = [
{path: '', component: HomeComponent},
{path: 'login', component: LoginComponent},
{path: 'dashboard', component: DashboardComponent}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule{}
AppComponent.ts
import {Component, OnInit} from '@angular/core';
import * as firebase from 'firebase';
import {AuthService} from './public/services/auth.service';
import {User} from "./auth/models/user.model";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
isLoading = true;
constructor(private authService: AuthService) {}
ngOnInit() {
// this.authService.isLogIn.next(localStorage.length > 0);
firebase.auth().onIdTokenChanged(
(data) => {
console.log('TOKEN HAS CHANGED', data);
if(data) {
data.getIdToken().then(
(tk) => {
this.authService.token = tk;
}
);
} else {
console.log('You don\'t have a token yet, please login...');
// TODO this means that the user is new or have logout.
}
}
);
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User is signed in.
this.authService.user = new User(user.displayName, user.email, user.photoURL, user.uid);
console.log(user);
this.isLoading = false;
this.authService.isLogIn.next(true);
} else {
// No user is signed in.
console.error('no user is login');
this.authService.isLogIn.next(false);
}
});
// check localstorage, so we can see if there is a logged user
if (this.authService.getLocalUserInfo()) {
this.authService.isLogIn.next(true);
} else {
this.authService.isLogIn.next(false);
}
}
}
AppComponent模板:
<div>
<app-header></app-header>
</div>
<div>
<router-outlet></router-outlet>
</div>
AppHeader.ts
import { Component, OnInit } from '@angular/core';
import {AuthService} from '../../public/services/auth.service';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
isActivated = false;
constructor(private authService: AuthService) { }
ngOnInit() {
this.authService.isLogIn.asObservable().subscribe(
(data: boolean) => {
this.isActivated = data;
}
);
// this.isActivated = this.authService.isLogin();
}
}
AppHeader模板
<nav class="navbar navbar-expand-lg navbar-dark bg-custom-nav px-5">
<a class="navbar-brand" routerLink="/">MovieApp</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="container-fluid">
<div class="collapse navbar-collapse d-lg-flex justify-content-lg-between row" id="navbarNavAltMarkup">
<div class="navbar-nav px-sm-4 px-lg-0">
<a class="nav-item nav-link text-white"
routerLink="/"
routerLinkActive="active font-weight-bold"
[routerLinkActiveOptions]="{exact: true}">Inicio</a>
</div>
<div class="col-lg-8">
<app-search-bar></app-search-bar>
</div>
<div class="row">
<ng-template [ngIf]="!isActivated">
<a class="nav-item nav-link text-white anchor-hover"
routerLink="/login"
routerLinkActive="active font-weight-bold">
<i class="fa fa-sign-in pr-2" aria-hidden="true"></i>Ingresar
</a>
</ng-template>
<ng-template [ngIf]="isActivated">
<!--DROPDOWN PROFILE-->
<div class="btn-group" role="group" *ngIf="authService.user">
<button id="btnGroupDrop1" type="button" class="btn btn-light dropdown-toggle pointer" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
{{authService.user.displayName}}
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="btnGroupDrop1">
<a class="dropdown-item" href="#">Dropdown link</a>
<a class="dropdown-item" href="#">Dropdown link</a>
</div>
</div>
<a class="nav-item nav-link text-white anchor-hover"
routerLink="/dashboard"
routerLinkActive="active font-weight-bold">
<i class="fa fa-sign-in pr-2" aria-hidden="true"></i>Dashboard
</a>
</ng-template>
</div>
</div>
</div>
</nav>
AuthService.ts
import * as firebase from 'firebase';
import {Router} from '@angular/router';
import { Injectable } from "@angular/core";
import {Subject} from "rxjs/Subject";
import {User} from "../../auth/models/user.model";
@Injectable()
export class AuthService {
token: string;
isLogIn = new Subject<boolean>();
user: User;
constructor(private router: Router){}
signinWithFacebook() {
const provider = new firebase.auth.FacebookAuthProvider();
provider.addScope('user_location');
return firebase.auth().signInWithPopup(provider)
.then(
(res) => {
console.log(res);
this.getTokenId();
localStorage.setItem('userInfo', JSON.stringify(firebase.auth().currentUser.providerData[0]));
const userInfo = firebase.auth().currentUser.providerData[0];
this.user = new User(userInfo.displayName, userInfo.email, userInfo.photoURL, userInfo.uid);
this.isLogIn.next(true);
this.router.navigate(['/dashboard']);
}
);
}
getTokenId() {
firebase.auth().currentUser.getIdToken()
.then(
(tk) => {
return this.token = tk;
}
);
}
logout() {
return firebase.auth().signOut();
// handle in component
}
getLocalUserInfo(): boolean {
if(localStorage.getItem('userInfo')) {
const transformStoredUser = JSON.parse(localStorage.getItem('userInfo'));
this.user = new User(transformStoredUser.displayName, transformStoredUser.email, transformStoredUser.photoURL, transformStoredUser.uid);
return true;
} else {
return false;
}
}
isLogin():boolean {
if (localStorage.getItem('userInfo')) {
return true;
}
return false;
}
}
您可以在以下位置找到完整的项目:https://github.com/lucasdavidferrero/movieApp
还有一件事,我使用了异步管道,但是模板没有反映出变化。在github项目中,您会发现我在模板中使用了async,但是仍然存在相同的错误。我希望有人可以帮助我。
最佳答案
我将尝试帮助/回答,请注意,有很多代码可供筛选,因此我可能错过了一些显而易见的内容。无论如何,希望可以整理一下代码,并为您指出正确的方向,您可能想研究AsyncPipe。
我想首先将isLogIn = new Subject<boolean>();
更改为private _isLogIn = new Subject<boolean>();
,以首先修改您的AuthService以获得一些更好的做法。我们不想将Subject与其他将对其进行修改的组件共享,因此在我们的AuthService中的每个地方,您只需要添加下划线...最后,导出要使用的Subject,我们可以添加如下的get函数:
get isLogIn(): Observable<boolean> {
return this._isLogIn.asObservable();
}
现在,我们不需要将
asObservable()
链接到我们使用过的任何地方。另外,我们希望将监视用户状态更改的位置从AppComponent移到AuthService。然后,您可以在AppModule的构造函数中实例化该服务,以便您的应用程序启动后,您的服务将开始监视auth状态的更改。
您还可能会遇到一个问题,即身份验证状态的解析可能不在区域内,因此虽然这不是必需的,但您可以/应该从核心导入
NgZone
并将其传递到AuthService constructor(private router: Router, private _zone: NgZone) { }
的构造函数中因此,当您更新auth状态的值时,可以将其包装在此函数中:this._zone.run(() => {
// your code here
}
现在,如果我要写你的
header.component.ts
,我会做这样的事情:export class HeaderComponent implements OnInit {
constructor(private authService: AuthService) { }
ngOnInit() {}
get isActivated(): Observable<boolean> {
return this.authService.isLogIn;
}
}
所以在这里,我只是将可观察对象直接通过get函数传递给我们的模板。因此,在模板中,我将像这样实现AsyncPipe:
<div *ngIf="!(isActivated | async) else user">
<a class="nav-item nav-link text-white anchor-hover" routerLink="/login"
routerLinkActive="active font-weight-bold">
<i class="fa fa-sign-in pr-2" aria-hidden="true"></i>Ingresar
</a>
</div>
<ng-template #user>
<!--DROPDOWN PROFILE-->
<div class="btn-group" role="group" *ngIf="authService.user">
<button id="btnGroupDrop1" type="button" class="btn btn-light dropdown-toggle pointer" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
{{authService.user.displayName}}
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="btnGroupDrop1">
<a class="dropdown-item" href="#">Dropdown link</a>
<a class="dropdown-item" href="#">Dropdown link</a>
</div>
</div>
<a class="nav-item nav-link text-white anchor-hover"
routerLink="/dashboard"
routerLinkActive="active font-weight-bold">
<i class="fa fa-sign-in pr-2" aria-hidden="true"></i>Dashboard
</a>
</ng-template>
AsyncPipe将自动订阅和取消订阅您的Observable,并处理刷新/区域更新。
现在,我做了一些概述/基本更改,以确保代码的行为符合预期。我邀请您尝试一下,看看您的
*ngIf
是否更“整洁”地工作。但是,您可能还想在Angular中查找其他内容... => https://stackoverflow.com/a/43006589/5076023