在本系列的上一篇文章 中,我们使用路由 (Route),并建立了 Login 和 Home 两个界面。 本文继续深化应用,将实现登录、导航功能并加入强制用户登录的逻辑。
完善登录页面 在上一篇文章中,我们只是通过链接的方式让用户可以从 Login 跳转到 Home 页面。在实际的项目中,登录的基本逻辑应该是让用户输入用户名、密码,然后根据验证结果,来决定页面是否跳转。真实的应用应该放在 Service 中,并且应该通过 Service 访问后台服务完成验证, 但本次练习中,为了突出重点,我们简单的将验证逻辑放在页面中。
打开 login-page.component.html 文件, 加入用户名、密码的输入框和登录按钮,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 <div class ="l_box login_form" > <div class ="l_box" > Login</div > <div class ="l_box" > <input [(ngModel )]="userId" placeholder ="Please input userId" /> </div > <div class ="l_box" > <input [(ngModel )]="pwd" placeholder ="Please input password" /> </div > <div class ="l_box" > <button (click )="login()" > Login</button > </div > </div >
其中的 CSS 类只是使内容居中,至于界面的美化,我会在以后的文章中再详细的讲解, 以下是 login-page.component.css 的内容:
1 2 3 4 5 6 7 8 .l_box { margin : 10px 10px ; } .login_form { text-align : center; margin-top : 20px ; }
然后修改 login-page.component.ts 文件,加入简单的登录逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import { Component, OnInit } from '@angular/core' ;import { Router } from '@angular/router' ;@Component ({ selector: 'app-login-page' , templateUrl: './login-page.component.html' , styleUrls: ['./login-page.component.css' ] }) export class LoginPageComponent implements OnInit { userId: string ; pwd: string ; constructor (private router: Router ) { } ngOnInit() { } login() { console .log('[login] userId = ' + this .userId + ', pwd = ' + this .pwd) if (this .userId == 'tom' && this .pwd == '123' ) { this .router.navigate(['home' ]); } else { alert('The userId or pwd is invalid!' ); } } }
在这里,使用了 Router 对象的 navigate 方法进行导航。
注意: 因为在 login 页面中使用了 ngModel 的方式对 input 进行了绑定,这需要用到 FormsModule, 应该我们需要映入这个模块。 打开 app.module.ts 文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import { BrowserModule } from '@angular/platform-browser' ;import { NgModule } from '@angular/core' ;import { HttpClientModule } from '@angular/common/http' ;import { FormsModule } from '@angular/forms' ;import { AppRoutingModule } from './app-routing.module' ;import { AppComponent } from './app.component' ;import { HeaderComponent } from './components/layout/header/header.component' ;import { CardComponent } from './components/widget/card/card.component' ;import { HomePageComponent } from './pages/home-page/home-page.component' ;import { LoginPageComponent } from './pages/login-page/login-page.component' ;@NgModule ({ declarations: [ AppComponent, HeaderComponent, CardComponent, HomePageComponent, LoginPageComponent ], imports: [ BrowserModule, AppRoutingModule, HttpClientModule, FormsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
现在执行 ng serve, 在登录页分别输入 tom, 123, 按登录按钮后,页面就跳转到 Home 页面, 如果输入的用户名和密码不正确,则会弹出一个 alert。
登录功能看起来已经完成了,但你会发现,如果用户直接输入 Home 页面的 URL, 还是可以不登录直接进入到 Home 页面的。这就有安全漏洞了,怎么解决了? Angular提供了Guard构件来处理类似的问题。 (当然,Guard的用户还有其它的场景,我会在另外的文章详细说明)
添加Guard 1 2 3 4 ? Which interfaces would you like to implement? (Press <space> to select, <a> to toggle all, <i> to invert selection) >( ) CanActivate ( ) CanActivateChild ( ) CanLoad
在课堂实验中,选择 CanActivate 即可。详细的区别,会在别的文章中说明。 空格选择,回车确认,然后系统显示:
1 2 3 ? Which interfaces would you like to implement? CanActivate CREATE src/app/sys/security.guard.spec.ts (370 bytes) CREATE src/app/sys/security.guard.ts (460 bytes)
说明已经建立好了 Gruad, 文件在 app/sys 目录下,文件名为: security.guard.ts
打开文件,可以看到生成程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { Injectable } from '@angular/core' ;import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router' ;import { Observable } from 'rxjs' ;@Injectable ({ providedIn: 'root' }) export class SecurityGuard implements CanActivate { canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise <boolean | UrlTree> | boolean | UrlTree { return true ; } }
在canActivate方法中,如果返回 True, 则路由继续。 在课堂案例中,我们需要实现一个简单的逻辑,就是如果用户登录过了,则可以进行路由,导航到新页面;如果用户还没有登录,则强制跳转到登录页面让用户登录。
添加用户验证服务 验证用户登录信息的逻辑通常应该放在后台的服务器中,但课堂练习我们进行了简化,用一个本地服务来包装用户验证方面的逻辑,以突出教学的重点。课后有兴趣的学员可以参照前面访问后台获取待办事项列表的方法来改进和完善案例。
执行 ng g 添加服务
1 ng g service services/SecuritySrv
系统显示
1 2 CREATE src/app/services/security-srv.service.spec.ts (359 bytes) CREATE src/app/services/security-srv.service.ts (140 bytes)
打开 security-srv.service.ts 文件,在 SecuritySrvService 类中简单的加入一个表示是否登录的属性: isLogin 即可, 完成后的代码:
1 2 3 4 5 6 7 8 9 10 11 12 import { Injectable } from '@angular/core' ;@Injectable ({ providedIn: 'root' }) export class SecuritySrvService { isLogin: boolean = false ; constructor ( ) { } }
打开 login-page.component.ts, 引入 SecuritySrvService, 并在登录成功时设置 isLogin 为 True, 改变后的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import { Component, OnInit } from '@angular/core' ;import { Router } from '@angular/router' ;import { SecuritySrvService } from '../../services/security-srv.service' ;@Component ({ selector: 'app-login-page' , templateUrl: './login-page.component.html' , styleUrls: ['./login-page.component.css' ] }) export class LoginPageComponent implements OnInit { userId: string ; pwd: string ; constructor (private router: Router, private securitySrv: SecuritySrvService ) { } ngOnInit() { } login() { console .log('[login] userId = ' + this .userId + ', pwd = ' + this .pwd) if (this .userId == 'tom' && this .pwd == '123' ) { this .router.navigate(['home' ]); this .securitySrv.isLogin = true ; } else { alert('The userId or pwd is invalid!' ); } } }
强制用户登录 再打开刚才建的 security.guard.ts 文件,实现用户未登录就强制跳转到登录页面的逻辑, 代码如下,注意 canActivate 方法的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import { Injectable } from '@angular/core' ; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router' ; import { Observable } from 'rxjs' ; import { SecuritySrvService } from '../services/security-srv.service' ; @Injectable({ providedIn: 'root' }) export class SecurityGuard implements CanActivate { constructor(private router: Router, private securitySrv: SecuritySrvService) {} canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { if (!this.securitySrv.isLogin) { this.router.navigate(['/' ]); } return true ; } }
最后,改变路由规则,在相关路由中加入 guard。 打开 app-routing.module.ts 文件, 修改路由规则如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import { NgModule } from '@angular/core' ;import { Routes, RouterModule } from '@angular/router' ;import { LoginPageComponent } from './pages/login-page/login-page.component' ;import { HomePageComponent } from './pages/home-page/home-page.component' ;import { SecurityGuard } from './sys/security.guard' ;const routes: Routes = [ { path:'' , component: LoginPageComponent }, { path: 'home' , component: HomePageComponent, canActivate: [SecurityGuard] } ]; @NgModule ({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
完成以上修改后,就可以执行 ng serve 命令启动程序,先尝试直接访问 home 页 (“localhost:4200/home”), 你会发现页面直接跳转到登录页面了(“/“), 登录成功以后,能够正常访问 home 页。
后续 本课堂实验手册到此就结束了。其实这个课堂应用还有很多可以扩展的地方,比如:实现调用后台登录逻辑?待办事项的增、删、改等功能,这些就留给学员(读者)去实现了。有问题可以邮件联系我。后续我将会补充一些如何在Angular中使用第三方UI库(比如: ngx-bootstrap)方面的文章。希望能帮助大家入门并解决一些实际问题。