Angular 中間部分 2.3 路由

路由

Angular路由包括:基礎路由,路由策略,路由參數,Auth,login,子路由

基礎&安裝

anchor tag
頁面指定內容快速訪問
在html設定<a name="about"><h1>About</h1></a>
訪問http://something/#about即可到h1tag

Angular 有如下配置路由
- Routes 這個應用支持的路由
- RouterOutlet 路由和對應的內容
- RouterLink 連接路由

導入 route 模塊 p220,p243

import {
  RouterModule,
  Routes
} from '@angular/router';

app.module.ts定義路由

const routes: Routes = [
  // 普通路由
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'home', component: HomeComponent },
  { path: 'about', component: AboutComponent },

  // 認證路由
  { path: 'login', component: LoginComponent },
  {
    path: 'protected',
    component: ProtectedComponent,
    canActivate: [ LoggedInGuard ]
  },

  // 層疊路由 nested
  {
    path: 'products',
    component: ProductsComponent,
    children: childRoutes
  }
];

path就是路徑,component就是對應的組件

redirectTo 就是重定向一個新的路徑
訪問 http://localhost:4200/#/ 會被重定向到home

同時需要在app.module.ts@NgModule({導入
RouterModule.forRoot(routes)

imports: [
    ...
    RouterModule.forRoot(routes), 
    ProductsModule
],

RouterOutlet

當改變路由的時候,最好模版的樣式不會改變,只是頁面的部分內容重新渲染。
**The router-outlet element indicates where the contents of each route component will be
rendered**

定義每一個組件的哪些內容會被渲染

在組件app.component.ts內配置

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  constructor(private router: Router) {
  };
}

在html頁面

<div class="page-header">
  <div class="container">
    <h1>Router Sample</h1>
    <div class="navLinks">
      <a [routerLink]="['/home']">Home</a>
      <a [routerLink]="['/about']">About Us</a>
      <a [routerLink]="['/contact']">Contact Us</a>
      |
      <a [routerLink]="['/products']">Products</a>
      <a [routerLink]="['/login']">Login</a>
      <a [routerLink]="['/protected']">Protected</a>
    </div>
  </div>
</div>

<div id="content">
  <div class="container">
    <router-outlet></router-outlet>
  </div>
</div>

router-outlet在導航欄的下面,
routerLink指引對應的路由

相比直接設置對應路由:
<a href="/#/home">Home</a>

好處是保持單一頁面,而不是重新加載新頁面。

put it together

html頁面的head

<head>
  ...
  <base href="/">
</head>

包含了路由情況

Angualr通過NgModule的APP_BASE_HREF來配置base而不用直接更改

在NgModule導入APP_BASE_HREF

@NgModule({
  providers: [
    { provide: LocationStrategy, useClass: HashLocationStrategy },
    { provide: APP_BASE_HREF, useValue: '/' }
  ]
})

先創建3個子組件,在創建父組件連接
p229 p252 父組件設置路由
在app.moudyle.ts導入組件(RouterModule,Routes)
並配置對應的路由

const routes: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },

@NgModule({導入

在html設置對應子組件連接,導航欄,(routerLink,router-outlet)

路由策略 Routing Strategies

默認是PathLocationStrategy,也就是html5默認路由。這個策略下,路由由普通路徑組成比如 /home

但同時,HashLocationStrategy也在被使用
但使用HTML5路由時,URL會由普通路徑結束。也就是不使用hash/anchor

假設刷新頁面時,不會請求根路徑,而是/about,但是在服務器端並沒有配置,(只配置根路徑),會返回404?
所以hTML5默認必須把所有路徑都配置。
詳細比較見

https://github.com/browserstate/history.js/wiki/Intelligent-State-Handling

HashLocationStrategy使用/#/home

路由參數 /route/:param

當每篇文章都有對應的url id時
/articles/3
/articles/4
需要配置 /route/:param

安裝 p235 p258
導入ActivatedRoute

import { ActivatedRoute } from '@angular/router';

設置路由
{ path: 'product/:id', component: ProductComponent },

把ActivatedRoute加入到constructor

export class ProductComponent {
  id: string;

  constructor(private route: ActivatedRoute) 
    route.params.subscribe(params => { this.id = params['id']; });
  }
}

Music search app

P236 P256

Router Hooks login

認證+認證後能訪問的內容
P253 P276

AuthService

寫AuthService

import { Injectable } from '@angular/core';

@Injectable()
export class AuthService {
  login(user: string, password: string): boolean {
    if (user === 'user' && password === 'password') {
      localStorage.setItem('username', user);
      return true;
    }
  \\ 當use和password匹配時,返回true,並且儲存在localStorage,
    return false;
  }

  logout(): any {
    localStorage.removeItem('username');
  }

  getUser(): any {
    return localStorage.getItem('username');
  }

  isLoggedIn(): boolean {
    return this.getUser() !== null;
  }
}
  //provide注入註冊
export const AUTH_PROVIDERS: Array<any> = [
  { provide: AuthService, useClass: AuthService }
];

在 login的組件注入

export class LoginComponent {
  message: string;

  constructor(public authService: AuthService) {
    this.message = '';
  }

  login(username: string, password: string): boolean {
    this.message = '';
    if (!this.authService.login(username, password)) {
      this.message = 'Incorrect credentials.';
      setTimeout(function() {
        this.message = '';
      }.bind(this), 2500);
    }
    return false;
  }

  logout(): boolean {
    this.authService.logout();
    return false;
  }

}

在login的html

<h1>Login</h1>
//認證錯誤信息
<div class="alert alert-danger" role="alert" *ngIf="message">
  {{ message }}
</div>

//使用*ngIf="!authService.getUser()"來認證信息輸入
<form class="form-inline" *ngIf="!authService.getUser()">
  <div class="form-group">
    <label for="username">User: (type <em>user</em>)</label>
    <input class="form-control" name="username" #username>
  </div>

  <div class="form-group">
    <label for="password">Password: (type <em>password</em>)</label>
    <input class="form-control" type="password" name="password" #password>
  </div>

  <a class="btn btn-default" (click)="login(username.value, password.value)">
    Submit
  </a>
</form>

//認證成功
<div class="well" *ngIf="authService.getUser()">
  Logged in as <b>{{ authService.getUser() }}</b>
  <a href (click)="logout()">Log out</a>
</div>

ProtectedComponent p258 p281

只讓登入用戶訪問

創建 logged-in.guard.ts
快速創建Angular內置:

ng generate guard logged-in

/* tslint:disble max-line-length */
import { Injectable } from '@angular/core';
import {
  CanActivate,
  ActivatedRouteSnapshot,
  RouterStateSnapshot
} from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { AuthService } from './auth.service';

@Injectable()
export class LoggedInGuard implements CanActivate {
  //注入AuthService
  constructor(private authService: AuthService) {}

  //CanActivate檢測authService
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
      const isLoggedIn = this.authService.isLoggedIn();
      console.log('canActivate', isLoggedIn);
      return isLoggedIn;
  }
}

使用了 CanActivate,並且注入AuthService

配置路由器 p259 p282
在app.module.ts導入AUTH_PROVIDERS和LoggedInGuard

在app.module 認證路由 見開篇路由配置
並provider p260 p283

...
  { path: 'login', component: LoginComponent },
  {
    path: 'protected',
    component: ProtectedComponent,
    canActivate: [ LoggedInGuard ]
  },
...
providers: [
...
]

Nested Routes 層疊路由 p264 p287

層疊路由是包含其他路由
主路由->子路由
適用於:網站需要根據每一個用戶單獨顯示對應的信息時。

見開篇路由配置

  {
    path: 'products',
    component: ProductsComponent,
    children: childRoutes
  }

products有子參數,
那麼在products.module.ts組件中,可以定於自己的路由

export const routes: Routes = [
{ path: '', redirectTo: 'main', pathMatch: 'full' },
{ path: 'main', component: MainComponent },
{ path: 'more-info', component: MoreInfoComponent },
]

在組件中

  goToProduct(id: string): void {
    this.router.navigate(['./', id], {relativeTo: this.route});
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章