[Angular 基礎] - routing 路由(上)

[Angular 基礎] - routing 路由(上)


之前部分 Angular 筆記:

  • [Angular 基礎] - 生命周期函數

  • [Angular 基礎] - 自定義指令,深入學習 directive

  • [Angular 基礎] - service 服務


終于到 routing 了……這部分的內容比我想象的要復雜很多,果然 Angular 的學習曲線不是開玩笑的 ˉ\_(ツ)_/ˉ

基礎頁面布局

下面是一個簡單的 wireframe,在沒有實現路由時候的布局:

在這里插入圖片描述

其中:

  • 第一個模塊對應的就是主頁,一個非常簡單的歡迎信息

  • 第二個模塊對應的是服務器管理

    這里的實現是 edit 所屬的模塊與單獨展示的 server 平級

  • 第二個模塊對應的是用戶信息展示

src/app/
├── home
├── servers
│   ├── edit-server
│   └── server
└── users└── user

結構如上所示

在沒有實現路由功能的時候,可以結合前面學習的案例,采用 ngIf + services 去實現。

其主要邏輯是:

  • 使用 ngIf 去判斷當前應該渲染什么頁面

    這個就需要在 app 層添加一個變量去控制當前展示的頁面,實現一個 service 去管理對應的點擊和更新事件

  • 創建多個組件層級 services

    如一個 service 去管理當前展示的 server,一個 service 去管理當前的 user

就像之前在案例項目 第一個 Angular 項目 - 添加服務 中實現的那樣。不過這樣的實現也有一點問題,比如實現會麻煩一些,或者無法根據網址訪問對應的資源,如通過 domain/user_id 的方式訪問對應的用戶,有些驗證方式也無法通過,如有些登錄驗證的方式是通過在 URL 后拼接一些 state id 的方式進行雙向驗證,這種多為第三方驗證驗證方式。

Angular 本身自帶路由的實現

添加路由

創建一個新的 route module

這里的創建方式就是手動創建一個 TS 文件,文件名為 app-routing.module.ts,實現方式如下:

const appRoutes: Routes = [{ path: '', component: HomeComponent },{path: 'users',component: UsersComponent,},{path: 'servers',component: ServersComponent,},
];@NgModule({imports: [RouterModule.forRoot(appRoutes)],exports: [RouterModule],
})
export class AppRoutingModule {}

隨后在 app.module.ts 中導入 AppRoutingModule:

@NgModule({declarations: [// ...],imports: [BrowserModule, FormsModule, AppRoutingModule],
})
export class AppModule {}

將路由單獨拆分成一個 module 是為了代碼的可讀性,以及跟一下 SRP(Single Responsibility Principle),如果不拆分的話,直接將 appRoutes 定義在 app.module.ts 中,并且在 imports 中添加 RouterModule.forRoot(appRoutes) 也可以

這里代碼的分析也比較簡單,首先 Route 就是 Angular 定義好的類型:

在這里插入圖片描述

上面這是 Angular 提供的最簡單的配置,需要一個路徑,一個組件,這兩個是需要的最基礎的配置。children 是可選項,代表著子組件(nested component),這里后面會說。

forRoot() 會創建一個新的,包含所有提供的鹿筋和指令的 ngModule,其語法為:

static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders<RouterModule>

可以看到這是一個靜態函數,換言之,這也是一個 singleton

使用 route

渲染對應 router

這里需要更新的是 V 層,拋除一些樣式的上的內容,核心部分的代碼如下:

<ul class="nav nav-tabs"><lirole="presentation"routerLinkActive="active"[routerLinkActiveOptions]="{ exact: true }"><a routerLink="/">Home</a></li><li role="presentation" routerLinkActive="active"><a [routerLink]="['/servers']">Servers</a></li><li role="presentation" routerLinkActive="active"><a [routerLink]="'/users'">Users</a></li>
</ul><router-outlet></router-outlet>

這里總共有這么幾個需要注意的點:

  • routerLinkActive

    routerLinkActive 也是一個指令,它會動態的添加指定的類名,當前情況下這個類名就是 active,展示效果如下:

    在這里插入圖片描述

    可以看到,隨著 nav link 的變動,Angular 也會自動修改對應的類名——增添或是刪除 active

  • routerLinkActiveOptions

    這是比較經常搭配使用 routerLinkActive 的指令,比較常見的選項是 [routerLinkActiveOptions]="{ exact: true }",這樣可以保證瀏覽器的路徑和路由提供的 URL 100% 一致時,才會增加對應的 active class

    如果不加的話,所有的路徑都會 match / 這個路徑,因此就會出現兩個 active tabs 的情況:

    在這里插入圖片描述

  • routerLink

    routerLink 取代了 href,通過 href 進行定位的方式會導致整個頁面重新刷新,從而丟失掉所有的狀態——這點和 React 是一樣的

    這是配置 path 的方法,我這里一共顯示了 3 種寫法

    • routerLink="/"

      語法糖縮寫,和下一種寫法一致,具體在 [Angular 基礎] - 自定義指令,深入學習 directive 有提到過

    • [routerLink]="'/users'"

      這是在 path 比較簡單的情況下使用,直接提供一個字符串即可

    • [routerLink]="['/servers']"

      這是一個比較常見的用法,主要可以用來比較方便的接受靜態數據

      /users/user 為例,

      • [routerLink]="'/users/user'" 會生成一個靜態路徑,即永遠都是 /users/user

        如果想要生成動態路徑,那么就需要使用 + 做拼接

      • [routerLink]="['/users', user]" 會生成一個動態路徑,如 user 是一個變量名,那么 Angular 就會獲取對應的變量,并拼接出對應的路徑

        也就是說,生成的路徑名可能是 /users/user,也有可能是 /users/user1234

  • router-outlet

    這就是一個 placeholder,當 Angular 完成渲染后,它會動態加載對應的組件

    也就取代之前提到的用 ngIf 渲染的 template

編程式導航

這個情況為需要在組件內觸發一些事件后進行重定向,如在登陸后重新導航到首頁這種重定向操作

這里的案例為在 Home 頁面通過點擊事件定向到其他的頁面,V 層修改如下:

<button class="btn btn-primary" (click)="onLoadServers()">To Server Page
</button>

VM 層實現:

export class HomeComponent implements OnInit {private servers: { id: number; name: string; status: string }[] = [];constructor(private router: Router, private route: ActivatedRoute) {}onLoadServers() {this.router.navigate(['servers'], {relativeTo: this.route,queryParams: { allowEdit: '1' },fragment: 'loading',});}
}

constructor 中的內容通過 dependency injection 實現,這部分具體可以查看 [Angular 基礎] - service 服務 這篇筆記,這里不多贅述。這里的 RouterActivatedRoute 都是 Angular 提供用于導航的 service

其中:

  • Router

    是導航及歷史記錄的相關服務

    對應的 React Hook 有 useHistory/useNavigate

  • ActivatedRoute

    顧名思義,這是對當前的 active route 進行的封裝,可以通過這個 service 輕松獲取當前的 path 以及包含的相關數據

    對應的 React Hook 有 useLocation, useParams, useMatch, useLoaderData

這里的點擊事件觸發的就是重定向到 servers 這個路徑去,注意這里采用的是相對路徑,Angular 的路由可以接受絕對路徑,也可以接受相對路徑,甚至還可以使用 ../ 這樣的相對路徑。后面的參數則是定向的路由配置:

  • relativeTo: this.route

    這里指的是導航的地址所參考的路徑,如當前為 /,那么路徑拼接的就是 /servers。如果當前路徑是 /servers,那么拼接的路徑就是 /servers/servers

    使用相對路徑時,一定要使用 relativeTo,因為 Router 不知道當前路徑在哪里。當沒有接收到 relativeTo 時,Angular 會將所有的路徑默認為絕對路徑

  • queryParams: { allowEdit: '1' }

    這就是添加 query parameter 的地方

  • fragment: 'loading'

    fragment 為 #some_value,一般用來定向到 HTML 頁面中的某一個 id 上去

定向效果為:

在這里插入圖片描述

動態接受路徑數據

上面一個 section 提到了相對路徑和動態修改路徑,這里繼續實操一下,修改的是 servers component。

V 層修改如下

<div class="row"><div class="col-xs-12 col-sm-4"><div class="list-group"><a[routerLink]="['/servers', server.id]"[queryParams]="{ allowEdit: server.id === 3 ? '1' : '0' }"fragment="loading"class="list-group-item"*ngFor="let server of servers">{{ server.name }}</a></div></div>
</div>

VM 層不需要修改就此跳過,這個時候點擊路徑會發現沒有任何的變化:

在這里插入圖片描述

但是查看 HTML 元素又能發現,router-link 中是有值的。這是因為當前 Angular 的 routing 只針對 /servers 進行了處理,但是并沒有對 /servers/id 進行處理,因此這里需要修改一下 app-routing module:

const appRoutes: Routes = [// 其余不變{path: 'servers/:id',component: ServerComponent,},
];

其中 :id 代表的是一個動態變量

這時候就能成功實現重定向:

在這里插入圖片描述

這個時候的數據顯示是不完整的,如果想在在 server component 中獲取對應的 server 數據,則需要使用到 ActivatedRoute 這個 service,VM 層修改如下:

export class ServerComponent implements OnInit {server: { id: number; name: string; status: string };constructor(private serversService: ServersService,private route: ActivatedRoute,private router: Router) {}ngOnInit() {this.server = this.serversService.getServer(parseInt(this.route.snapshot.params.id));}
}

其中 serversService 只是用來獲取當前 server 數據的一個 service,具體實現這里不會提及

實現后效果如下:

在這里插入圖片描述

這里可以發現,數據已經可以正常渲染了

這里需要注意的是這個 snapshot 會獲取當前路由的狀態,其包含的數據如下:

在這里插入圖片描述

這里獲取的 id 對應的就是 path: 'servers/:id' 中的 :id,也是對 routerLink 中的 ['/servers', server.id],之前的 section 提到過,使用數組傳參數,數組中的值可以是字符串,也可以是變量,Angular 會自動拼接變量的值到路由中去。

同樣,這里也可以注意到 navigation 中的 Servers 還是處于 active 的狀態,這也是因為沒有實現 exact: true,Angular 在匹配字符串的時候,發現當前路徑與 /servers 可以匹配,因此還是會添加 active 這一類名到對應的元素上

動態更新路由數據

現在更新一下 VM 層,更新如下:

<h5>{{ server?.name }}</h5>
<p>Server status is {{ server?.status }}</p><!-- <button class="btn btn-primary" (click)="onEdit()">Edit Server</button> --><div class=""><a [routerLink]="['/servers', 2]">Click me to server 2</a>
</div>

主要是新增加了一個超鏈接,然后完成重定向到 /servers/2 的實現,效果如下:

在這里插入圖片描述

可以看到,路徑是從 http://localhost:4200/servers/1?allowEdit=0#loading 變成了 http://localhost:4200/servers/2,但是數據卻沒有任何的更新。

造成這個的原因是,對于 Angular 來說,當前的頁面沒有重新渲染——url 仍然是 /servers/:id,因此當前組件不會重新經歷一個 銷毀 --> 新建 的過程,自然 ngOnInit 并沒有重新被觸發,數據自然也不會完成對應的更新。

想要解決這個問題,就需要 subscribe ActivatedRoute 的數據變化,在每次 ActivatedRoute 的數據更新時,也需要更新組件內的數據。

這里實現如下:

export class ServerComponent implements OnInit {ngOnInit() {this.server = this.serversService.getServer(parseInt(this.route.snapshot.params.id));console.log(this.route.snapshot);this.route.params.subscribe((params: Params) => {this.server = this.serversService.getServer(parseInt(params.id));});}
}

實現后效果如下:

在這里插入圖片描述

這個實現會在每一次 this.route.params 產生變動時,更新 this.server。另外從實踐上來說,這里最好在 ngOnDestroy 里去 unsubscribe 去防止內存泄露,不過因為 ActivatedRoute 是 Angular 提供的 service,Angular 會在組件被銷毀的時候自動 unsubscribe。

如果是自己實現的 service,那就 一定 要做好對應 unsubscribe 的處理

這里涉及到了 Observable,后面會有專門的部分復習回顧一下 Observable……雖然之前也有筆記寫過 rxjs 的 Observable,大概了解過這個的用法

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/716463.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/716463.shtml
英文地址,請注明出處:http://en.pswp.cn/news/716463.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LC打怪錄 選擇排序 215.Kth Largest Element in an Array

題目鏈接&#xff1a;力扣 選擇排序知識 設第一個元素為比較元素&#xff0c;依次和后面的元素比較&#xff0c;比較完所有元素并找到最小元素&#xff0c;記錄最小元素下標&#xff0c;和第0個下表元素進行交換。在未排序區域中&#xff0c;重復上述操作&#xff0c;以此類推…

力扣每日一題 用隊列實現棧 模擬

Problem: 225. 用隊列實現棧 文章目錄 思路復雜度Code 思路 &#x1f468;?&#x1f3eb; 力扣官解 輔助隊列存棧頂元素主隊列存逆序序列 復雜度 時間復雜度: 添加時間復雜度, 示例&#xff1a; O ( n ) O(n) O(n) 空間復雜度: 添加空間復雜度, 示例&#xff1a; O ( …

js監聽網頁iframe里面元素變化其實就是監聽iframe變化

想要監聽網頁里面iframe標簽內容變化&#xff0c;需要通過監聽網頁dom元素變化&#xff0c;然后通過查詢得到iframe標簽&#xff0c;再通過iframe.contentWindow.document得到ifram內的document&#xff0c;然后再使用選擇器得到body元素&#xff0c;有了body元素&#xff0c;就…

2024年華為OD機試真題-貪吃的猴子-Python-OD統一考試(C卷)

題目描述: 一只貪吃的猴子,來到一個果園,發現許多串香蕉排成一行,每串香蕉上有若干根香蕉。每串香蕉的根數由數組numbers給出。猴子獲取香蕉,每次都只能從行的開頭或者末尾獲取,并且只能獲取N次,求猴子最多能獲取多少根香蕉。 輸入描述: 第一行為數組numbers的長度 第二…

Java和JavaScript之間的主要區別與聯系

目錄 概況 主要區別 聯系 總結 概況 Java和JavaScript&#xff0c;盡管名字相似&#xff0c;但它們在編程世界中卻扮演著截然不同的角色。Java&#xff0c;一種強類型、面向對象的編程語言&#xff0c;廣泛應用于企業級應用和安卓應用開發。它的設計理念是一次編寫&#x…

使用協程庫httpx并發請求

httpx和aiohttp都是比較常用的異步請求庫&#xff0c;當然requests多線程或requestsgevent也是不錯的選擇。 一個使用httpx進行并發請求的腳本如下&#xff1a; import functools import sys import timeimport anyio import httpxasync def fetch(client, results, index) -…

詳解 JavaScript 中的數組

詳解 JavaScript 中的數組 創建數組 注&#xff1a;在JS中的數組不要求元素的類型&#xff0c;元素類型可以一樣&#xff0c;也可以不一樣 1.使用 new 關鍵字創建 let array new Array()2.使用字面量方式創建(常用) let array1 [1,2,3,"4"]獲取數組元素 使用下…

西安-騰訊云-Python面試經驗--一面涼經

自我介紹手撕鏈表排序操作系統 a. 線程和進程區別 b. 線程安全 c. 如何保證線程安全 d. 線程崩潰&#xff0c;會不會影響所在的進程 e. 什么是守護進程&#xff0c;僵尸進程&#xff0c;孤兒進程 f. 如何產生一個守護進程 g. 如何避免僵尸進程或者孤兒進程redis a. 持久化方式有…

【STK】手把手教你利用STK進行仿真-STK軟件簡介05 STK部分第三方分析模塊介紹

1.導彈建模工具MMT 導彈建模工具MMT(Missile Modeling Tools)是STK在導彈分析領域的擴展分析應用,它是由四個獨立的應用程序組成的相互支持與關聯的系統,由第三方研究機構開發,能夠與STK基本航天分析環境進行聯合仿真分析。MMT主要用于導彈總體設計(包括彈道導彈、巡航導彈…

python進階:可迭代對象和迭代器

一、Iterable&#xff08;可迭代對象&#xff09; 1、可迭代對象&#xff1a;能夠進行迭代操作的對象。 可以理解為&#xff1a;能夠使用for循環遍歷的都是可迭代對象&#xff1b;**所有的可迭代對象&#xff0c;偶可以用內置函數iter轉換為迭代器** 2、可迭代對象包括&…

藍橋杯題練習:平地起高樓

題目要求 function convertToTree(regions, rootId "0") {// TODO: 在這里寫入具體的實現邏輯// 將平鋪的結構轉化為樹狀結構&#xff0c;并將 rootId 下的所有子節點數組返回// 如果不存在 rootId 下的子節點&#xff0c;則返回一個空數組}module.exports convert…

網絡防御保護——課堂筆記

一.內容安全 攻擊可能只是一個點&#xff0c;防御需要全方面進行 IAE引擎 DFI和DPI技術 --- 深度檢測技術 DPI ---深度包檢測技術 ---主要針對完整的數據包&#xff08;數據包分片&#xff0c;分段需要重組&#xff09;&#xff0c;之后對數據包的內容進行識別。&#xff08;應…

ifcplusplus 示例 函數中英文 對照分析以及流程圖

有需求&#xff0c;需要分析 ifc c渲染&#xff0c;分析完&#xff0c;有 230個函數&#xff0c;才能完成一個加載&#xff0c;3d加載真的是大工程&#xff01; 示例代碼流程圖 函數中英文對照表&#xff0c;方便 日后開發&#xff0c;整理思路順暢&#xff01;&#xff01;&am…

C++三級專項 digit函數

在程序中定義一函數dight(n,k),他能分離出整數n從右邊數第k個數字。 輸入 正整數n和k。 輸出 一個數字。 輸入樣例 31859 3 輸出樣例 8解析&#xff1a;遞歸&#xff0c;詳情看code. 不準直接抄&#xff01;&#xff01;&#xff01; #include <iostream> usin…

包裝類和綜合練習

包裝類 基本數據類型對應的應用類型。 jdk5以后對包裝類新增了&#xff1a;自動拆箱、自動裝箱 我們以后如何獲取包裝類對象&#xff1a; 不需要new,不需要調用方法&#xff0c;直接賦值即可 package MyApi.a09jdkdemo;public class A_01IntergerDemo1 {public static voi…

C語言——指針的進階——第1篇——(第26篇)

堅持就是勝利 文章目錄 一、字符指針1、面試題 二、指針數組三、數組指針1、數組指針的定義2、&數組名 VS 數組名3、數組指針的使用&#xff08;1&#xff09;二維數組傳參&#xff0c;形參是 二維數組 的形式&#xff08;2&#xff09;二維數組傳參&#xff0c;形參是 指針…

【RT-Thread應用筆記】英飛凌PSoC 62 + CYW43012 WiFi延遲和帶寬測試

文章目錄 一、安裝SDK二、創建項目三、編譯下載3.1 編譯代碼3.2 下載程序 四、WiFi測試4.1 掃描測試4.2 連接測試 五、延遲測試5.1 ping百度5.2 ping路由器 六、帶寬測試6.1 添加netutils軟件包6.2 iperf命令參數6.3 PC端的iperf6.4 iperf測試準備工作6.5 進行iperf帶寬測試6.6…

未來三年AI的深度發展:AIGC、視頻AI與虛擬世界構建

人工智能&#xff08;AI&#xff09;正站在科技演進的前沿&#xff0c;未來三年將見證其在多領域實現更深層次的突破。以下是對AI發展方向的深度探討以及其對各行業的深遠影響&#xff1a; 1. AIGC的演進與全面提升&#xff1a; AIGC&#xff0c;即AI通用性能力&#xff0c;將…

AI前沿-YOLOV9算法

AI前沿-YOLOV9算法 關注B站查看更多手把手教學&#xff1a; 肆十二-的個人空間-肆十二-個人主頁-嗶哩嗶哩視頻 (bilibili.com) 今天我們來一起說下最近剛出的YOLOV9算法 論文和源碼 該算法的原始論文地址為&#xff1a;https://arxiv.org/abs/2402.13616 該算法的原始代碼地…

Muduo庫編譯學習(1)

1.muduo庫簡介 muduo是由Google大佬陳碩開發&#xff0c;是一個基于非阻塞IO和事件驅動的現代C網絡庫&#xff0c;原生支持one loop per thread這種IO模型&#xff0c;該庫只支持Linux系統&#xff0c;網上大佬對其褒貶不一&#xff0c;作為小白用來學習就無可厚非了。 git倉庫…