我已经使用jwt令牌化实现了身份验证,并且登录后我想在导航栏上显示用户名,但是它没有显示任何内容,并且在控制台上显示错误“ ERROR TypeError:无法读取未定义的属性'username'”,但是在刷新后页面上显示用户名,并在控制台上仍然给出相同的错误。
这是我的代码
NavbarComponent.ts
import { Component, OnDestroy } from '@angular/core';
import { AuthService } from '../../services/auth.service';
import { Router } from '@angular/router';
import { User } from '../../shared/user';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { AdminAuthGuard } from '../../guards/admin.auth.guard';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.css']
})
export class NavbarComponent {
user$: any;
constructor(private authService: AuthService, private router: Router, private adminGuard: AdminAuthGuard) {
this.authService.getProfile().subscribe((user) => {
console.log('fsdf' + user.user.email);
this.user$ = user.user;
});
}
onLogoutClick() {
// console.log('this.user' + this.user$.email);
this.authService.logout();
this.router.navigate(['/']);
}
}
我要在其中打印用户名的html代码。
<nav class="navbar navbar-expand-md navbar-light bg-light fixed-top">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" routerLink="/home">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" *ngIf="!authService.loggedIn()" [routerLinkActive]="['active']" [routerLinkActiveOptions]="{exact:true}" routerLink="/login">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" *ngIf="!authService.loggedIn()" [routerLinkActive]="['active']" [routerLinkActiveOptions]="{exact:true}" routerLink="/register">Register</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="/shopping-cart">Shopping Cart</a>
</li>
<li *ngIf="authService.loggedIn()" ngbDropdown class="nav-item dropdown">
<a ngbDropdownToggle class="nav-link dropdown-toggle" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{ user$.username }}</a>
<div ngbDropdownMenu class="dropdown-menu " aria-labelledby="dropdown01">
<a class="dropdown-item" routerLink="/my/orders">My Orders</a>
<a class="dropdown-item" routerLink="/profile">Profile</a>
<a class="dropdown-item" routerLink="/admin/orders">Manage Orders</a>
<a class="dropdown-item" routerLink="/admin/products">Manage Products</a>
<button class="dropdown-item" (click)="onLogoutClick()">Logout</button>
</div>
</li>
</ul>
</div>
</nav>
身份验证服务代码。关注getProfile()方法
import { Injectable } from '@angular/core';
import 'rxjs/add/operator/map';
import { Http, Headers, RequestOptions } from '@angular/http';
// import { map } from "rxjs/operators";
// import { map } from 'rxjs/operators';
import { switchMap } from 'rxjs/operators';
import { tokenNotExpired } from 'angular2-jwt';
import { User } from '../shared/user';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/observable/of';
@Injectable()
export class AuthService {
private subject = new Subject<any>();
domain = 'http://localhost:3000';
authToken;
user;
options;
constructor(private http: Http) { }
registerUser(user) {
return this.http.post(this.domain + '/authentication/register', user).map(res => res.json());
}
createAuthenticationHeaders() {
this.loadToken();
this.options = new RequestOptions({
headers : new Headers({
'Content-Type': 'application/json',
'authorization': this.authToken
})
});
}
loadToken() {
this.authToken = localStorage.getItem('token');
}
checkUsername(username) {
return this.http.get(this.domain + '/authentication/checkUsername/' + username).map(res => res.json());
}
checkEmail(email) {
return this.http.get(this.domain + '/authentication/checkEmail/' + email).map(res => res.json());
}
login(user) {
return this.http.post(this.domain + '/authentication/login', user).map(res => res.json());
}
logout() {
this.authToken = null;
this.user = null;
localStorage.clear();
}
storeUserData(token, user) {
localStorage.setItem('token', token);
localStorage.setItem('user', JSON.stringify(user));
this.authToken = token;
this.user = user;
}
getProfile() {
this.createAuthenticationHeaders();
return this.http.get(this.domain + '/authentication/profile', this.options).map(res => res.json());
}
loggedIn() {
return tokenNotExpired();
}
}
这是http路由的API,可从用Node.js编写的mongodb数据库中获取用户。专注于get /)的'/ profile'方法,它返回一个Observable。
const User = require('../models/user'); // Import User Model Schema
const jwt = require('jsonwebtoken');
const config = require('../config/database');
module.exports = (router) => {
/* ==============
Register Route
============== */
router.post('/register', (req, res) => {
// Check if email was provided
if (!req.body.email) {
res.json({ success: false, message: 'You must provide an e-mail' }); // Return error
} else {
// Check if username was provided
if (!req.body.username) {
res.json({ success: false, message: 'You must provide a username' }); // Return error
} else {
// Check if password was provided
if (!req.body.password) {
res.json({ success: false, message: 'You must provide a password' }); // Return error
} else {
// Create new user object and apply user input
let user = new User({
email: req.body.email.toLowerCase(),
username: req.body.username.toLowerCase(),
password: req.body.password
});
// Save user to database
user.save((err) => {
// Check if error occured
if (err) {
// Check if error is an error indicating duplicate account
if (err.code === 11000) {
res.json({ success: false, message: 'Username or e-mail already exists' }); // Return error
} else {
// Check if error is a validation rror
if (err.errors) {
// Check if validation error is in the email field
if (err.errors.email) {
res.json({ success: false, message: err.errors.email.message }); // Return error
} else {
// Check if validation error is in the username field
if (err.errors.username) {
res.json({ success: false, message: err.errors.username.message }); // Return error
} else {
// Check if validation error is in the password field
if (err.errors.password) {
res.json({ success: false, message: err.errors.password.message }); // Return error
} else {
res.json({ success: false, message: err }); // Return any other error not already covered
}
}
}
} else {
res.json({ success: false, message: 'Could not save user. Error: ', err }); // Return error if not related to validation
}
}
} else {
res.json({ success: true, message: 'Acount registered!' }); // Return success
}
});
}
}
}
});
router.get('/checkEmail/:email', (req, res) => {
if (!req.params.email) {
res.json({ success: false, message: 'email not provided'});
} else {
User.findOne({ email: req.params.email}, (err, user) => {
if (err) {
res.json({ success: false, message: err});
} else {
if (user) {
res.json({ success: false, message: 'email taken'});
} else {
res.json({ success: true, message: 'email available'});
}
}
});
}
});
router.get('/checkUsername/:username', (req, res) => {
if (!req.params.username) {
res.json({ success: false, message: 'username not provided'});
} else {
User.findOne({ username: req.params.username}, (err, user) => {
if (err) {
res.json({ success: false, message: err});
} else {
if (user) {
res.json({ success: false, message: 'username taken'});
} else {
res.json({ success: true, message: 'username available'});
}
}
});
}
});
router.post('/login', (req, res) => {
if (!req.body.username) {
res.json({ success: false, message: 'No username was provided'});
} else {
if (!req.body.password) {
res.json({ success: false, message: 'No password was provided'});
} else {
User.findOne({ username: req.body.username.toLowerCase() }, (err, user) => {
if (err) {
res.json({ success: false, message: err});
} else {
if (!user) {
res.json({ success: false, message: 'No user exist'});
} else {
const validPassword = user.comparePassword(req.body.password);
if (!validPassword) {
res.json({ success: false, message: 'password invalid'});
} else {
const token = jwt.sign({userId: user._id}, config.secret, {expiresIn: '24h'});
res.json({ success: true, message: 'Success!', token: token, user: {username: user.username}});
}
}
}
});
}
}
});
// MIDDLEWARE TO INTERCEPT HEADERS
// THIS MIDDLEWARE DECRYPTS THE TOKEN
router.use((req, res, next) => {
const token = req.headers['authorization']; // whenever a request coming from angular2 with headers attached it is going to search fot this header
if (!token) {
res.json({ success: false, message: 'No token provided'});
} else {
jwt.verify(token, config.secret, (err, decoded) => {
if (err) {
res.json({ success: false, message: 'invalid token' + err});
} else {
req.decoded = decoded;
next();
}
});
}
})
// ANY ROUTES COMING AFTER THIS MIDDLEWARE WILL PASS THROUGH THE SAME
// BELOW METHOD TAKES THE DECRYPTED TOKEN FIND THE USER
router.get('/profile', (req, res) => {
User.findOne({ _id: req.decoded.userId }).select('username email isAdmin').exec((err, user) => {
if (err) {
res.json({ success: false, message: err});
} else {
if (!user) {
res.json({ success: false, message: 'user not found'});
} else {
res.json({ success: true, user: user });
}
}
});
});
return router; // Return router object to main index.js
}
答案 0 :(得分:2)
请尝试使用 safe navigation operator
或* ngIf,因为您是向API提出请求并异步获取数据。尝试如下,
<a ngbDropdownToggle class="nav-link dropdown-toggle" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{ user$?.username }}</a>
答案 1 :(得分:0)
我非常确定您会遇到这种情况,因为当用户名属性不存在时,您正在尝试访问 user $ .username 。为了避免该错误,您可以使用定义的类型然后进行初始化,例如。
public $user: UserInterface = {
username: ''
}
或者是否不想使用定义的类型,只需使用ngIf向html块添加额外的验证,例如。
<a *ngIf="user$.username" ngbDropdownToggle class="nav-link dropdown-toggle" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{ user$.username }}</a>
btw,作为一种良好的编码习惯,请将公共方法声明用于在代码和视图之间共享的方法,自动构建将感谢您。