[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.mzph.cn/news/716463.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣每日一题 用队列实现栈 模拟

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;就…

Java和JavaScript之间的主要区别与联系

目录 概况 主要区别 联系 总结 概况 Java和JavaScript&#xff0c;尽管名字相似&#xff0c;但它们在编程世界中却扮演着截然不同的角色。Java&#xff0c;一种强类型、面向对象的编程语言&#xff0c;广泛应用于企业级应用和安卓应用开发。它的设计理念是一次编写&#x…

详解 JavaScript 中的数组

详解 JavaScript 中的数组 创建数组 注&#xff1a;在JS中的数组不要求元素的类型&#xff0c;元素类型可以一样&#xff0c;也可以不一样 1.使用 new 关键字创建 let array new Array()2.使用字面量方式创建(常用) let array1 [1,2,3,"4"]获取数组元素 使用下…

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语言——指针的进阶——第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…

Muduo库编译学习(1)

1.muduo库简介 muduo是由Google大佬陈硕开发&#xff0c;是一个基于非阻塞IO和事件驱动的现代C网络库&#xff0c;原生支持one loop per thread这种IO模型&#xff0c;该库只支持Linux系统&#xff0c;网上大佬对其褒贬不一&#xff0c;作为小白用来学习就无可厚非了。 git仓库…

b站小土堆pytorch学习记录——P14 torchvision中的数据集使用

文章目录 一、前置知识如何查看torchvision的数据集 二、代码&#xff08;附注释&#xff09;及运行结果 一、前置知识 如何查看torchvision的数据集 &#xff08;1&#xff09;打开官网 https://pytorch.org/ pytorch官网 &#xff08;2&#xff09;打开torchvision 在Do…

Unity游戏输入系统(新版+旧版)

使用新版还是旧版 旧版 using System.Collections; using System.Collections.Generic; using UnityEngine;public class c5 : MonoBehaviour {void Start(){}void Update(){// 注意要在游戏中 点鼠标键盘进行测试// 鼠标// 0左键 1右键 2滚轮if (Input.GetMouseButtonDown(0)…

【javaSE-语法】lambda表达式

【javaSE-语法】lambda表达式 1. 先回忆一下&#xff1a;1.1 接口不能直接通过关键字new进行实例化1.2 函数式接口1.3 匿名内部类1.31 匿名内部类在代码中长啥样&#xff1f;1.32 构造一个新的对象与构造一个扩展了某类的匿名内部类的对象&#xff0c;两者有什么区别&#xff1…

midjourney提示词语法

更高级的提示可以包括一个或多个图像URL、多个文本短语和一个或更多个参数 Image Prompts 可以将图像URL添加到提示中&#xff0c;以影响最终结果的样式和内容。图像URL总是位于提示的前面。 https://docs.midjourney.com/image-prompts Text Prompt 要生成的图像的文本描述。…

YOLOv6、YOLOv7、YOLOv8网络结构图(清晰版)

承接上一篇博客&#xff1a;YOLOv3、YOLOv4、YOLOv5、YOLOx的网络结构图(清晰版)_yolox网络结构图-CSDN博客 1. YOLOv6网络结构图 2. YOLOv7网络结构图 3. YOLOv8网络结构图

搭建 LNMP 架构

一 理论知识 &#xff08;一&#xff09;架构图 &#xff08;二&#xff09;CGI 由来 最早的Web服务器只能简单她响应浏览器发来的HTTP请求&#xff0c;并将存储在服务器上的HTML文件返回给浏览器&#xff0c;也就是静态html文件&#xff0c;但是后期随着网站功能增多网站开…

c++阶梯之模板初阶

1. 泛型编程 void Swap(int& x, int& y) {int tmp x;x y;y tmp; }void Swap(double& x, double& y) {double tmp x;x y;y tmp; }void Swap(char& x, char& y) {char tmp x;x y;y tmp; } int main() {int a 10, b 20;double c 1.1, d 2.2…

《Spring Security 简易速速上手小册》第7章 REST API 与微服务安全(2024 最新版)

文章目录 7.1 保护 REST API7.1.1 基础知识详解7.1.2 重点案例&#xff1a;使用 JWT 进行身份验证和授权案例 Demo 7.1.3 拓展案例 1&#xff1a;API 密钥认证案例 Demo测试API密钥认证 7.1.4 拓展案例 2&#xff1a;使用 OAuth2 保护 API案例 Demo测试 OAuth2 保护的 API 7.2 …

linux安装matlab获取许可证

1.点击许可证 2. 3. 4. 4.主机ID 打开linux输入 /sbin/ifconfigether后边的就是 6.计算机登录名 打开linux输入 whoami7. 8. 9.