2018年10月1日 星期一

【Ionic 4】新頁面瀏覽機制:Angular Router

新版Ionic 4在頁面瀏覽(navigation)機制方面加入了Angular Router,連帶頁面權限方面也可使用Router的CanActivate等機制進行控管;Ionic舊版的push/pop方式雖仍可使用,但預期未來應不會再支援。

(本文範例完整程式碼與執行結果可參考stackblitz.com上的專案程式碼與執行結果)

新建使用Angular Router的Ionic 4專案
為了讓新建專案使用Angular Router,Ionic CLI 4版加入新選項--type=angular。新建專案時加入此選項,便會建立Angular Routing模組檔,內有瀏覽頁面相關設定,例如新建專案:
ionic start RoutingExample blank --type=angular
則會建立模組檔: src/app/app-routing.module.ts,內有預設頁面瀏覽路徑設定:
const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'home', loadChildren: './home/home.module#HomePageModule' },
];

此處定義了預設路徑,以及'/home'路徑。此外,專案主要進入元件頁面檔app.component.html,也改採routing指令<ion-router-outlet>:
<ion-app>
  <ion-router-outlet></ion-router-outlet>
</ion-app>
<ion-router-outlet>指令在切換瀏覽頁面時,會將redirect, loadChildren, 或component指定的內容帶入。

新增頁面ionic g page與lazy loading
Ionic CLI 4版目前(4.1.2)使用ionic g page產生新頁面時,並未支援類似3版--no-module選項,所以,所有頁面都是lazy loading頁面,自成一個模組。在上述專案裡,加入兩個頁面list與detail(list將列出所有清單項目, 點選個別項目,則進入detail頁面顯示該項目),隨後執行之:
cd RoutingExample
ionic g page list
ionic g page detail
ionic serve
新增頁面,都會產生自己的模組檔(.module.ts),頁面路徑也會加入app-routing.module.ts,如下圖所示:
自動產生module檔,也更新路徑設定(更新app-routing.module.ts)
自動更新後的app-routing.module.ts,皆以loadChildren屬性設定新增頁面,於進入該頁面時,才會載入:
const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'home', loadChildren: './home/home.module#HomePageModule' },
  { path: 'list', loadChildren: './list/list.module#ListPageModule' },
  { path: 'detail', loadChildren: './detail/detail.module#DetailPageModule' },
];
至於執行期間,上述Routes與主要元件的Router Tree,如下圖所示:
Router Tree。三個元件皆為lazy loading。no-name-route則是用redirectTo所設定的預設路徑

插播:ionic g service
Ionic CLI 4版已將provider正名為service。為了建立list, detail兩個頁面的內容,此處建立一資料服務,將資料內容定義於此,同時提供讀取清單內容與個別項目內容的功能:
ionic g service services/data
src/services/data.service.ts檔內先定義資料欄位介面Language,以方便存取資料內容:
export interface Language{
  id:string,
  name:string,
  intro:string
}
接著,定義資料list為Language類型之陣列,同時提供讀取全部內容的getList()與讀取單一項目的getItem(id):
export class DataService {
  list:Language[]=[];

  constructor() {
    this.list = [
      {id:'0', name:'Java', intro:'擁有跨平台、物件導向、...'},
      {id:'1', name:'JavaScript', intro:'基於原型、函式先行的語言...'},
      {id:'2', name:'Python', intro:'屬於通用型程式語言...。'},
      {id:'3', name:'C', intro:'具有高效、靈活、功能豐富、表達...'},
      {id:'4', name:'Ruby', intro:'一種物件導向、命令式、函數...'},
    ];
  }

  getList() {
    return this.list;
  }
  getItem(id):Language{
    return this.list.find(e=>{return e.id===id});
  }
}
如此,ListPage與DetailPage便可使用此服務,讀取對應資料。

HTML檔定義連結/TS檔以navigate()切換頁面
app-routing.module.ts檔裡Routes每一個路徑的path,都可以直接用在HTML檔內,例如,設定在href屬性內,或是使用Angular Router定義的routerLink屬性:
<ion-button href="/list">前往清單</ion-button>
<a routerLink="/list">清單頁面</a>
如此,按下按鈕或點選連結,便會載入path對應的元件內容。而除了在頁面直接設定連結外,HTML檔內也可採用事件繫結(event binding),透過TS程式端呼叫Router提供的navigate(), navigateByUrl()等methods,前往其他頁面,例如HTML檔綁定click的事件處理器detail(item):
<ion-button (click)="detail(item)" slot="end">前往</ion-button>
而在TS檔定義detail(item),前往其他頁面,如:
detail(item){
    this.router.navigateByUrl(`/detail/${item.id}`);
}
或是
detail(item){
    this.router.navigate(['/detail/${item.id}']);
}

瀏覽動態url頁面與ActivatedRoute
有時候頁面的網址無法事先得知,例如清單有許多項目,而使用者未點選前,無從得知該瀏覽到哪一個項目對應的網址。此時,app-routing.module.ts內的路徑設定,便必須設定參數,例如:
const routes: Routes = [
{ path: '', redirectTo: 'list', pathMatch: 'full' }, { path: 'home', loadChildren: './home/home.module#HomePageModule' }, { path: 'list', loadChildren: './list/list.module#ListPageModule' }, { path: 'detail/:id', loadChildren: './detail/detail.module#DetailPageModule' }, ];
'/detail/:id'路徑便是動態路徑,無法事先得知;而id則是路徑的參數。不同參數值,如'/detail/0'與'detail/1',便會顯示不同內容。
搭配Routes動態路徑的設定,目的路徑元件便需要從網址中,擷取出參數值。以'detail/:id'對應的detail元件為例,該元件必須使用ActivatedRoute服務,取出網址傳送過來的參數值(detail.page.ts):
...
import { ActivatedRoute } from '@angular/router';
...

export class DetailPage implements OnInit {
  id:string;
  ...
  constructor(
    private route: ActivatedRoute,
    ...
  ) { }

  ngOnInit() {
    this.id = this.route.snapshot.paramMap.get('id');
    ...
  }
}

snapshot.paramMap.get()便是用來擷取參數的method。

完整的程式碼與執行結果,請參考Stackblitz.com上的專案程式碼與執行結果

重點摘要
  • Ionic CLI 4版加入新選項--type=angular
  • 自動生成Angular Router設定檔: src/app/app-routing.module.ts
  • Angualr Router指令<ion-router-outlet></ion-router-outlet>取代原先的<app-root>成為程式進入點
  • ionic g page產生的頁面預設為lazy loading,在Routes裡路徑會設定為loadChildren
  • 擷取動態網址參數,可利用ActivatedRoute服務的snapshot.paramMap.get()。
最後,本文使用工具的版本如下:
node: 10.9.0
npm: 6.4.0
ionic CLI: 4.1.2

沒有留言:

張貼留言