Angular2:使用NG-ZORRO的tabs結合路由復用策略實現動態tab


1.需求,使用路由懶加載的方式實現動態tab頁,點擊左側菜單右側新建一個tab,

2.新建一個項目: $ ng new angular-tab

按照ng-zorro官網的步驟導入 ng-zorro

  • 安裝:$ npm install ng-zorro-antd --save
  •  

     

    在app.module.ts里導入
     
     
    image.png
  •  

     

     

     

    在.angular-cli.json里導入樣式
     

     

     

    image.png

3.新建兩個組件,header,sidebar

 

 

 

image.png
  • header.component.html
<div class="header"> </div> 

-header.component.css

.header{ height: 50px; width: 100%; background: lightskyblue; } 

header組件比較簡單,就是一個div設定了高度

  • sidebar.component.html
<div class="sidebar"> <ul nz-menu [nzMode]="'inline'" style="width: 240px;"> <li nz-submenu> <span title><i class="anticon anticon-appstore"></i>系統管理</span> <ul> <li nz-menu-item>頁面1</li> <li nz-menu-item>頁面2</li> <li nz-menu-item>頁面3</li> </ul> </li> </ul> </div> 
  • sidebar.component.css, 浮動一下,不然右邊內容上不來
.sidebar{ float: left; width: 240px; } 

siderbar里用到了ng-zorro組件庫里的menu組件

  • app.component.html
<app-header></app-header> <app-sidebar></app-sidebar> <div class="content"> 123 </div> 
  • app.component.css
.content{ margin-left: 240px; } 

在app.component.html里添加兩個組件

現在頁面的效果

 

 

 

image.png

4.在編寫tab之前,先添加幾個tab要用到的頁面

因為路由懶加載的方式是加載的模塊,所以文件結構是這樣的


 

 

 

image.png
  • page1.module.ts
import {NgModule} from '@angular/core'; import {Page1Component} from './page1.component'; import {CommonModule} from '@angular/common'; import {ContentComponent} from './content/content.component'; @NgModule({ imports: [ CommonModule, Page1RouteModule ], declarations: [ Page1Component, ContentComponent ] }) export class Page1Module { } 

說明一下:

  • page1.module.ts 因為路由懶加載是加載的模塊,所以這個是給路由懶加載使用的,其中聲明了兩個組件,Page1Component和ContentComponent,其中Page1Component是路由進來顯示的組件,具體看下文的page1-route.module.ts文件說明
  • page1-route.module.ts
import {NgModule} from '@angular/core'; import {RouterModule, Routes} from '@angular/router'; import {Page1Component} from './page1.component'; export const ROUTES: Routes = [ { path: '', // 當訪問 /page1的時候顯示Page1Component組件 component: Page1Component } ] @NgModule({ imports: [ RouterModule.forChild(ROUTES) ], exports: [ RouterModule ] }) export class Page1RouteModule { } 

說明一下:
可以看到Page1RouteModule里設置了路由,當訪問/page1 這個url,會加載Page1Component組件到頁面上

  • page1.component.html
<app-page1-content></app-page1-content> 
  • content.component.html
<p> page1的content組件 </p> 

以同樣的目錄結構建立page2,page3


 

 

 

image.png

添加路由

  • 在src目錄下建立app-route.module.ts
import {NgModule} from '@angular/core'; import {RouterModule, Routes} from '@angular/router'; export const ROUTES: Routes = [ { path: 'page1', loadChildren: './pages/page1/page1.module#Page1Module' }, { path: 'page2', loadChildren: './pages/page2/page2.module#Page2Module' }, { path: 'page3', loadChildren: './pages/page3/page3.module#Page3Module' } ] @NgModule({ imports: [ // 因為是根路由,所以使用forRoot RouterModule.forRoot( ROUTES ) ], exports: [ RouterModule ] }) export class AppRouterModule { } 

說明一下:前面提到的page1.module.ts在這里派上了用場,路由懶加載的方式聲明路由


 

 

 

image.png

將根路由添加到app.module.ts中
修改app.component.html

<app-header></app-header> <app-sidebar></app-sidebar> <div class="content"> <router-outlet></router-outlet> </div> 

啟動項目訪問 http://localhost:4200/page1

 

 

 

image.png

 

一、實現 RouteReuseStrategy 接口自定義一個路由復用策略

  • 在service目錄下新建SimpleReuseStrategy.ts文件
import {RouteReuseStrategy, DefaultUrlSerializer, ActivatedRouteSnapshot, DetachedRouteHandle} from '@angular/router'; /** * 路由復用策略 */ export class SimpleReuseStrategy implements RouteReuseStrategy { public static handlers: { [key: string]: DetachedRouteHandle } = {}; private static waitDelete: string; /** 表示對所有路由允許復用 如果你有路由不想利用可以在這加一些業務邏輯判斷 */ public shouldDetach(route: ActivatedRouteSnapshot): boolean { return true; } /** 當路由離開時會觸發。按path作為key存儲路由快照&組件當前實例對象 */ public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void { if (SimpleReuseStrategy.waitDelete && SimpleReuseStrategy.waitDelete === this.getRouteUrl(route)) { // 如果待刪除是當前路由則不存儲快照 SimpleReuseStrategy.waitDelete = null; return; } SimpleReuseStrategy.handlers[this.getRouteUrl(route)] = handle; } /** 若 path 在緩存中有的都認為允許還原路由 */ public shouldAttach(route: ActivatedRouteSnapshot): boolean { return !!SimpleReuseStrategy.handlers[this.getRouteUrl(route)]; } /** 從緩存中獲取快照,若無則返回nul */ public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle { if (!route.routeConfig) { return null; } return SimpleReuseStrategy.handlers[this.getRouteUrl(route)]; } /** 進入路由觸發,判斷是否同一路由 */ public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { return future.routeConfig === curr.routeConfig && JSON.stringify(future.params) === JSON.stringify(curr.params); } private getRouteUrl(route: ActivatedRouteSnapshot) { return route['_routerState'].url.replace(/\//g, '_'); } public static deleteRouteSnapshot(url: string): void { const key = url.replace(/\//g, '_'); if (SimpleReuseStrategy.handlers[key]) { delete SimpleReuseStrategy.handlers[key]; } else { SimpleReuseStrategy.waitDelete = key; } } } 

二、策略注冊到app.module模塊當中:

import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import {NgZorroAntdModule} from 'ng-zorro-antd'; import {SidebarComponent} from './layout/sidebar/sidebar.component'; import {HeaderComponent} from './layout/header/header.component'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {AppRouterModule} from './app-router.module'; import {SimpleReuseStrategy} from './service/SimpleReuseStrategy'; import {RouteReuseStrategy} from '@angular/router'; @NgModule({ declarations: [ AppComponent, SidebarComponent, HeaderComponent ], imports: [ BrowserModule, BrowserAnimationsModule, AppRouterModule, NgZorroAntdModule.forRoot() ], providers: [ { provide: RouteReuseStrategy, useClass: SimpleReuseStrategy } ], bootstrap: [AppComponent] }) export class AppModule { } 

四、新建一個tab組件並且注冊到app.module中,

 

 

 

image.png
  • tab.component.html,tab頁的具體使用參照ng-zorro官網,這里拷貝了一段官網的示例
<nz-tabset [nzType]="'card'" [nzSelectedIndex]="index"> <nz-tab *ngFor="let tab of tabs" [nzTitle]="titleTemplate"> <ng-template #titleTemplate> <div>{{ tab }}<i class="anticon anticon-close" (click)="closeTab(tab)"></i></div> </ng-template> Content of {{ tab }} </nz-tab> </nz-tabset> 
  • tab.component.ts
import {Component} from '@angular/core'; @Component({ selector: 'app-tab', templateUrl: './tab.component.html', styleUrls: ['./tab.component.css'] }) export class TabComponent { index = 0; tabs = [ 'Tab 1', 'Tab 2' ]; closeTab(tab: string): void { this.tabs.splice(this.tabs.indexOf(tab), 1); } } 
  • app.component.html
<app-header></app-header> <app-sidebar></app-sidebar> <div class="content"> <app-tab></app-tab> </div> 

現在頁面的樣子


 

 

 

image.png

五、編寫tab代碼

  • tab.component.ts
import {Component} from '@angular/core'; import {ActivatedRoute, NavigationEnd, Router} from '@angular/router'; import {Title} from '@angular/platform-browser'; import {SimpleReuseStrategy} from '../../service/SimpleReuseStrategy'; import 'rxjs/add/operator/filter'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/mergeMap'; @Component({ selector: 'app-tab', templateUrl: './tab.component.html', styleUrls: ['./tab.component.css'] }) export class TabComponent { // 路由列表 menuList = []; // 當前選擇的tab index currentIndex = -1; constructor(private router: Router, private activatedRoute: ActivatedRoute, private titleService: Title) { // 路由事件 this.router.events.filter(event => event instanceof NavigationEnd) .map(() => this.activatedRoute) .map(route => { while (route.firstChild) { route = route.firstChild; } return route; }) .filter(route => route.outlet === 'primary') .mergeMap(route => route.data) .subscribe((event) => { // 路由data的標題 const menu = {...event}; menu.url = this.router.url const url = menu.url; this.titleService.setTitle(menu.title); // 設置網頁標題 const exitMenu = this.menuList.find(info => info.url === url); if (!exitMenu) {// 如果不存在那么不添加, this.menuList.push(menu); } this.currentIndex = this.menuList.findIndex(p => p.url === url); }); } // 關閉選項標簽 closeUrl(url: string) { // 當前關閉的是第幾個路由 const index = this.menuList.findIndex(p => p.url === url); // 如果只有一個不可以關閉 if (this.menuList.length === 1) { return; } this.menuList.splice(index, 1); // 刪除復用 // delete SimpleReuseStrategy.handlers[module]; SimpleReuseStrategy.deleteRouteSnapshot(url) // 如果當前刪除的對象是當前選中的,那么需要跳轉 if (this.currentIndex === index) { // 顯示上一個選中 let menu = this.menuList[index - 1]; if (!menu) {// 如果上一個沒有下一個選中 menu = this.menuList[index]; } // 跳轉路由 this.router.navigate([menu.url]); } } /** * tab發生改變 */ nzSelectChange($event) { this.currentIndex = $event.index; const menu = this.menuList[this.currentIndex]; // 跳轉路由 this.router.navigate([menu.url]); } } 
  • tab.component.html
<nz-tabset style="margin-left: -1px;" [nzAnimated]="true" [nzSelectedIndex]="currentIndex" [nzShowPagination]="true" (nzSelectChange)="nzSelectChange($event)" [nzType]="'card'"> <nz-tab *ngFor="let menu of menuList" [nzTitle]="nzTabHeading"> <ng-template #nzTabHeading> <div> {{menu.title}} <i *ngIf="menu.isRemove" (click)="closeUrl(menu.url)" class="anticon anticon-cross" ></i> </div> </ng-template> </nz-tab> </nz-tabset> <div class="tab-content"> <!--路由的內容會被顯示在這里--> <ng-content></ng-content> </div> 
  • app.component.html
<app-header></app-header> <app-sidebar></app-sidebar> <div class="content"> <app-tab> <router-outlet></router-outlet> </app-tab> </div> 
  • app-route.module.ts
import {NgModule} from '@angular/core'; import {RouterModule, Routes} from '@angular/router'; export const ROUTES: Routes = [ { path: 'page1', loadChildren: './pages/page1/page1.module#Page1Module', data: { title:' 頁面1', isRemove: true } }, { path: 'page2', loadChildren: './pages/page2/page2.module#Page2Module', data: { title: '頁面2', isRemove: true } }, { path: 'page3', loadChildren: './pages/page3/page3.module#Page3Module', data: { title: '頁面2', isRemove: true } } ] @NgModule({ imports: [ // 因為是根路由,所以使用forRoot RouterModule.forRoot( ROUTES ) ], exports: [ RouterModule ] }) export class AppRouterModule { } 

六、現在編寫sidebar頁面,和tab聯動起來

  • sidebar.component.html
<div class="sidebar"> <ul nz-menu [nzMode]="'inline'" style="width: 240px;"> <li nz-submenu> <span title><i class="anticon anticon-appstore"></i>系統管理</span> <ul> <li nz-menu-item (click)="tabs('page1')">頁面1</li> <li nz-menu-item (click)="tabs('page2')">頁面2</li> <li nz-menu-item (click)="tabs('page3')">頁面3</li> </ul> </li> </ul> </div> 
  • sidebar.component.ts
import {Component, OnInit} from '@angular/core'; import {Router} from '@angular/router'; @Component({ selector: 'app-sidebar', templateUrl: './sidebar.component.html', styleUrls: ['./sidebar.component.css'] }) export class SidebarComponent implements OnInit { constructor(private router: Router) { } ngOnInit() { } /** * 路由方式添加tab * @param data */ tabs(data) { this.router.navigate([data]); } } 

現在頁面

 

 

 

image.png

參考文章

https://www.cnblogs.com/lovesangel/p/7853364.html
http://www.cnblogs.com/lslgg/p/7700888.html

NG-ZORRO官網

https://ng.ant.design/docs/getting-started/zh

項目地址

https://github.com/Ariesssssssss/angular-tab



作者:Lautumn
鏈接:https://www.jianshu.com/p/c8ed7db7dd0f
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM