Jack N @ GitHub

Full stack engineer, focus on: Angular/React, node.js/.Net

0%

环境:

  • Angular CLI: 11.0.6
  • Angular: 11.0.7
  • Node: 12.18.3
  • npm : 6.14.6
  • IDE: Visual Studio Code

1. 摘要

在我们的实际的业务开发过程中,我们经常会遇到如下需求:

  1. 需要限制某些 URL 的可访问性,例如,对于系统管理界面,只有那些拥有管理员权限的用户才能打开。
  2. 当用户处于编辑界面时,在没有保存就离开时,需要提示用户是否放弃修改。

针对以上场景,Angualr使用路由守卫(Route Guards)来实现。

2. 路由守卫(Route Guards)

2.1. 创建路由守卫

Angular CLI提供了命令行工具,可以快速创建路由守卫框架文件:ng generate guard auth。 执行后,Angular CLI会问我们需要实现哪些接口,我们直接勾选即可:

1
2
3
4
5
? Which interfaces would you like to implement? (Press <space> to select, <a> to toggle all, <i> to invert selection)
>(*) CanActivate
( ) CanActivateChild
( ) CanDeactivate
( ) CanLoad

说明:

  1. CanActivate: 控制路由是否可以激活
  2. CanActivateChild: 控制子路由是否可以激活
  3. CanDeactivate: 控制路由是否可以退出
  4. CanLoad: 控制模块(module)是否可以被加载

比较经常使用的是1、3,分别控制进入和退出。 按照上面配置,AngularCLI自动生成如下代码,return true; 替换为我们实际的代码即可。return false; 表示不允许跳转,或者取消离开当前页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';

@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate, CanDeactivate<unknown> {
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return true;
}
}

在canActivate方法中,我们还可以使用跳转。如页面判断是否已经登录,如果没有登录,跳转到Login页面:

1
2
this.router.navigate(['/login']);
return false;

2.2. 控制路由是否可以激活

控制路由是否可以激活,需要定义在定义路由的地方,增加canActivate属性。如果需要,还可以增加data属性, 比如告诉我们的AuthGuard进入当前路由需要验证哪些权限。data属性是可选的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const routes: Routes = [
{
path: "page1",
component: Page1Component,
data: { permissions: ['YourPage1Permission'] }, // 传入参数给AuthGuard,可选
canActivate: [AuthGuard]
},
{
path: "page2",
component: Page2omponent,
data: { permissions: ['YourPage2Permission'] }, // 传入参数给AuthGuard,可选
canActivate: [AuthGuard]
}
]

2.3. 控制路由是否退出(离开)

和控制路由是否可以激活类似,在路由定义出增加 canDeactivate,并制定相应的Guard守卫即可。这里不再举例

3. 总结

  1. 通过路由守卫(Route Guards)实现控制URL的进入和离开;
  2. Angular CLI可以辅助我们创建guard文件;



—————- END —————-






======================

环境:

  • Angular CLI: 11.0.6
  • Angular: 11.0.7
  • Node: 12.18.3
  • npm : 6.14.6
  • IDE: Visual Studio Code

1. 摘要

简单来说地址栏中,不同的地址(URL)对应不同的页面,这就是路由。同时,点击浏览器的前进和后退按钮,浏览器就会在你的浏览历史中向前或向后导航,这也是基于路由。

在 Angular 里面,Router 是一个独立的模块,定义在 @angular/router 模块中,

  1. Router 可以配合 NgModule 进行模块的延迟加载(懒加载)、预加载操作(参考《Angular入门到精通系列教程(11)- 模块(NgModule),延迟加载模块》);
  2. Router 会管理组件的生命周期,它会负责创建、销毁组件。

对于一个新的基于AngularCLI的项目,初始化时可以通过选项,将AppRoutingModule默认加入到app.component.ts中。

2. 路由(Router)基本用法

2.1. 准备

我们首先创建2个页面,用于说明路由的使用:

1
2
ng g c page1
ng g c page2

使用上面AnuglarCLI命令,创建Page1Component, Page2Component 2个组件。

2.2. 注册路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//src\app\app-routing.module.ts
const routes: Routes = [
{
path: 'page1',
component: Page1Component
},
{
path: 'page2',
component: Page2Component
},
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}

可以看到,简单的路由注册,只需要path和component2个属性,分别定义路由的相对路径,以及这个路由的响应组件。

2.3. html中的用法

1
2
<a routerLink="page1">Page1</a> |
<a routerLink="page2">Page2</a>

在html模板中,直接使用routerLink属性,标识为angular的路由。执行代码,可以看到 Page1和Page2 两个超链接,点击可以看到地址栏地址改为http://localhost:4200/page2或http://localhost:4200/page1, 页面内容在page1和page2中切换

2.4. ts 代码中的用法

有时候,需要根据ts中的业务逻辑,进行跳转。ts中,需要注入Router实例,如

1
constructor(private router: Router) {}

跳转代码:

1
2
3
4
5
// 跳转到 /page1
this.router.navigate(['/page1']);

// 跳转到 /page1/123
this.router.navigate(['/page1', 123]);

3. 接收参数

3.1. 路径中的参数

一般来说,我们把参数作为url中的一段,如/users/1, 代表查询id是1的用户,路由定义为”/users/id” 这种风格。

针对我们的简单页面,比如我们的page1页面可以传id参数,那么我们需要修改我们的routing为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const routes: Routes = [
{
path: 'page1/:id', //接收id参数
component: Page1Component,
},
{
// 实现可选参数的小技巧。 这个routing处理没有参数的url
path: 'page1',
redirectTo: 'page1/', // 跳转到'page1/:id'
},
{
path: 'page2',
component: Page2Component,
},
];

ts代码读取参数时, 首先需要注入ActivatedRoute,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
constructor(private activatedRoute: ActivatedRoute) {}

ngOnInit(): void {
this.activatedRoute.paramMap.subscribe((params) => {
console.log('Parameter id: ', params.get('id'));

// 地址 http://localhost:4200/page1/33
// 控制台输出:Query Parameter name: 33

// 地址 http://localhost:4200/page1/
// 控制台输出:Query Parameter name: (实际结果为undefined)
});
}

3.2. 参数(QueryParameter)中的参数

参数还有另外一种写法,如http://localhost:4200/?name=cat, 即URL地址后,加一个问号’?’, 之后再加参数名和参数值(’name=cat’)。这种称为查询参数(QueryParameter)。

取这查询参数时,和之前的路由参数类似,只是paramMap改为queryParamMap,代码如下:

1
2
3
4
5
6
7
8
9
this.activatedRoute.queryParamMap.subscribe((params) => {
console.log('Query Parameter name: ', params.get('name'));

// 地址 http://localhost:4200/page1?name=cat
// 控制台输出:Query Parameter name: cat

// 地址 http://localhost:4200/page1/
// 控制台输出:Query Parameter name: (实际结果为undefined)
});

4. URL路径显示格式

不同于传统的纯静态(html)站点,angular中的url不是对应一个真实的文件(页面),因为anuglar接管的路由(Routing)处理,来决定显示那个Component给终端用户。为了针对不同的场景,angular的URL路径显示格式有2中:

  1. http://localhost:4200/page1/123
  2. http://localhost:4200/#/page1/123

默认是第一种,不加#的。如果需要,可以在app-routing.ts中,加入useHash: true, 如:

1
2
3
4
5
// app-routing.ts
@NgModule({
imports: [RouterModule.forRoot(routes, { useHash: true })],
exports: [RouterModule],
})

5. 部署中遇到的问题

同样,因为anuglar接管的路由(Routing)处理,所以部署时,部署到iis, nginx等等的服务器,都会有不同的技巧(要求),详细参考:
https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-configure-your-server-to-work-with-html5mode

6. 总结

  1. angular默认不支持可选路由(e.g. /user/:id?),但是我们可以定义2个路由,指向同一个Component来实现这个,达到代码复用;(或者使用redirectTo)
  2. 可以使用useHash参数,实现augular路径前加一个#;
  3. 读取参数时,都需要subscribe订阅一下,不能直接读取。
  4. 打包后部署问题,查看官方wifi (https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-configure-your-server-to-work-with-html5mode)



—————- END —————-






======================

环境:

  • Angular CLI: 11.0.6
  • Angular: 11.0.7
  • Node: 12.18.3
  • npm : 6.14.6
  • IDE: Visual Studio Code

1. 摘要

指令(Directive)在Angular 1.0时代(当时叫AngularJS)是很流行的,现在用到的偏少。可以简单理解为,指令(Directive)用于扩展已有Element(DOM)。

2. 组件与指令之间的关系

如果去看Angular源码,可以看到下面定义

1
2
3
4
5
6
/**
* Supplies configuration metadata for an Angular component.
*
* @publicApi
*/
export declare interface Component extends Directive {

是的,Component派生于Directive,也就是说,Component属于Directive。

2.1. 指令的种类

  1. Component 是 Directive 的子接口,是一种特殊的指令,Component 可以带有 HTML 模板,Directive 不能有模板。
  2. 属性型指令:用来修改 DOM 元素的外观和行为,但是不会改变DOM 结构,Angular 内置指令里面典型的属性型指令有 ngClass、ngStyle,如果打算封装自己的组件库,属性型指令是必备的内容。
  3. 结构型指令:可以修改 DOM 结构,内置的常用结构型指令有 *ngFor*ngIf*ngSwitch。由于结构型指令会修改 DOM 结构,因而同一个 HTML 标签上面不能同时使用多个结构型指令。如果要在同一个 HTML 元素上面使用多个结构性指令,可以考虑加一层空的元素来嵌套,比如在外面套一层空的(div) 。

3. Angular 中指令的用途

Angualr中用指令来增强DOM的功能,包括 HTML 原生DOM和我们自己自定义的组件(Component)。举例来说,可以扩展一个Button,实现避免点击后,服务器端未响应前的二次点击;高亮某些收入内容等等。

4. 指令举例

4.1. 指令功能

实现一个指令,鼠标移动到上面时, 背景显示为黄色,鼠标移开,恢复正常。

4.2. Anuglar CLI生成基本文件

1
ng generate directive MyHighlight

Anuglar CLI自动生成html、css、ut等文件。
##

4.3. Directive指令核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { Directive, ElementRef } from '@angular/core';

@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(el: ElementRef) {
el.nativeElement.style.backgroundColor = 'yellow';
}

@HostListener('mouseenter') onMouseEnter() {
this.highlight('yellow');
}

@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}

private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}

4.4. 使用该指令

1
2
<p my-highlight>Highlight me!</p>

my-highlight 即我们的元素扩展属性(指令、directive)。

5. 总结

  • 指令(Directive)用于扩展DOM 元素或组件的功能。
  • Angular中的 *ngFor*ngIf*ngSwitch 都是Angular内置的指令。



—————- END —————-






======================

环境:

  • Angular CLI: 11.0.6
  • Angular: 11.0.7
  • Node: 12.18.3
  • npm : 6.14.6
  • IDE: Visual Studio Code

1. 概述

组件是 Angular 应用的主要构造块。每个组件包括如下部分:

  • 一个 HTML 模板,用于声明页面要渲染的内容
  • 一个用于定义行为的 Typescript 类
  • 一个 CSS 选择器,用于定义组件在模板中的使用方式
  • (可选)要应用在模板上的 CSS 样式

Component可以是一个页面,也可以是一个组件(控件)。总是,是一个页面元素。

任何一个Component都是NgModule的一部分,这样它就可以被其他应用和其他Component所调用。为了定义Component是NgModule的一个成员之一,开发者应该在NgModule的declarations属性中,将自己开发的Component列出。

另外,通过Component修饰符(也就是@Component)开发者可以配置元数据,这样通过各式各样的Life-Cycle hooks,Components就可以控制他们的运行环境。

2. 创建Component

基于AngularCLI,可以很方便的创建Component。在要创建Component的目录下,执行如下命令

1
ng generate component <component-name> 

e.g. ng generate component MyComponent
AngularCLI会自动生成一个文件夹和4个文件:

  • 一个以该组件命名的文件夹(e.g my-component)
  • 一个组件文件 < component-name >.component.ts(e.g my-component.component.ts)
  • 一个模板文件 < component-name >.component.html(e.g my-component.component.html)
  • 一个 CSS 文件, < component-name >.component.css(e.g my-component.component.css)
  • 测试文件 < component-name >.component.spec.ts(e.g my-component.component.spec.ts)

对于Component,所有文件名都会自动增加Component后缀,所以不建议< component-name > 中带有‘component’这个单词。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html',
styleUrls: ['./my-component.component.css']
})
export class MyComponentComponent implements OnInit {

constructor() { }

ngOnInit(): void {
}

}

以上是核心的ts文件,指定了selector(CSS 选择器),template(html)文件,css文件。html/css文件如果需要可以多个component公用。尤其是css,可以看到参数是Array,所以可以制定多个css。

2.1. 组件模板

组件模板,即HTML部分,可以是一个html文件,也可以是一段html描述,是等价的。Angular 组件需要一个用 template 或 templateUrl 定义的模板。但你不能在组件中同时拥有这两个语句。

  1. html 文件方式

    1
    2
    3
    4
    @Component({
    selector: 'app-component-overview',
    templateUrl: './component-overview.component.html',
    })
  2. html代码方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Component({
    selector: 'app-component-overview',
    template: '<h1>Hello World!</h1>',
    })
    ```

    # 3. 视图封装模式
    在 Angular 中,组件的 CSS 样式被封装进了自己的视图中,而不希望影响到应用程序的其它部分。这部分也是可以通过配置去进行控制的。
    ```ts
    @Component({
    selector: 'app-my-component',
    templateUrl: './my-component.component.html',
    encapsulation: ViewEncapsulation.None,
    styleUrls: ['./my-component.component.css']
    })

    可以看到,增加了一个encapsulation属性 (视图封装模式)。通过在组件的元数据上设置视图封装模式,你可以分别控制每个组件的封装模式。 可选的封装模式一共有如下几种:

  3. Emulated 模式(默认值)通过预处理(并改名)CSS 代码来模拟 Shadow DOM 的行为,以达到把 CSS 样式局限在组件视图中的目的。 更多信息,见附录 1。(说明:只进不出,全局样式能进来,组件样式出不去)

  4. ShadowDom 模式使用浏览器原生的 Shadow DOM 实现来为组件的宿主元素附加一个 Shadow DOM。组件的视图被附加到这个 Shadow DOM 中,组件的样式也被包含在这个 Shadow DOM 中。(说明:不进不出,没有样式能进来,组件样式出不去。)

  5. None 意味着 Angular 不使用视图封装。 Angular 会把 CSS 添加到全局样式中。而不会应用上前面讨论过的那些作用域规则、隔离和保护等。 从本质上来说,这跟把组件的样式直接放进 HTML 是一样的。

3.1. 特殊的选择器 :host

使用 :host 伪类选择器,用来选择组件宿主元素中的元素(相对于组件模板内部的元素)。 :host 选择是是把宿主元素作为目标的唯一方式。除此之外,你将没办法指定它, 因为宿主不是组件自身模板的一部分,而是父组件模板的一部分。

e.g.

1
2
:host {
}

3.2. inline-styles

默认AngularCLI生成的component,css在一个单独文件中。当然,同html模板类似,如果需要,你也可以制定css样式写在ts中, 不单独放到一个文件中。命令:ng generate component MyComponent --inline-style

生成component如下

1
2
3
4
5
@Component({
selector: 'app-my-component',
template: '<h1>Hello World!</h1>',
styles: ['h1 { font-weight: normal; }']
})

4. 总结

  • Angular CLI辅助创建一个component所需的多个文件
  • 建议html/css/ts分开
  • 在期望目录下执行Angular CLI命令,可以生成到制定目录
  • ng generate component XXX 可以简写为 ng g c



—————- END —————-






======================

环境:

  • Angular CLI: 11.0.6
  • Angular: 11.0.7
  • Node: 12.18.3
  • npm : 6.14.6
  • IDE: Visual Studio Code

1. 摘要

模块(NgModule)是Angular的核心概念之一。模块(NgModule)用于组织业务代码,按照自己的业务场景,把组件、服务、路由打包到模块里面。

模块(NgModule)的主要作用:

  1. NgModule 组织业务代码,开发者可以利用 NgModule 把关系比较紧密的组件组织到一起。
  2. NgModule 用来控制组件、指令、管道等的可见性,处于同一个NgModule 里面的组件默认互相可见,而对于外部的组件来说,只能看到NgModule 导出(exports)的内容。这样就实现了封装,只暴露希望暴露的组件、服务给调用者。
  3. NgModule 是 @angular/cli 打包的最小单位。打包的时候,@angular/cli 会检查所有 @NgModule 和路由配置,如果你配置了异步模块,cli 会自动把模块切分成独立的 chunk(块)。在 Angular 里面,打包和切分的动作是 @angular/cli 自动处理的,不需要你干预。当然,如果需要,你也可以修改angular的编译配置,基于Webpack 配一个环境出来,自定义打包规则。
  4. NgModule 是 Router 进行异步加载的最小单位,Router 能加载的最小单位是模块,而不是组件。当然,也可以一个模块里面只放一个组件是。

2. NgModule举例、说明

前文说过,Angular中任何的Component、service,都必须属于一个NgModule。所以,使用AngularCLI生成的框架程序,自动生成的组件,也是属于一个Component的,并且标记为启动模块。
这样,angular站点启动后,会以这个模块为入口,根据配置加载各个模块。

下面以默认生成的启动模块为例,进行解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@NgModule({
declarations: [
AppComponent,
MyComponentComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

  • declarations,用来放组件、指令、管道的声明;
  • imports,用来导入外部模块。比如当前模块需要调用其他模块的组件,需要加入到这里。比如上面例子,导入了Browser和Routing 2个模块。
  • providers,需要使用的第三方或者其他模块的Service 都放在这里;
  • bootstrap,定义启动组件。 一个可以启动的Angular项目(如果只是一个Library除外),需要定义一个启动组件。
  • exports, 声明的组件、指令和管道可以在导入了本模块的模块下任何组件的模板中使用。 导出的这些可声明对象就是该模块的公共 API。换句话说,其他模块想用本模块中定义的内容,需要在这里声明。
  • entryComponents, 如果其他模块想要动态加载本模块中的组件到视图中, 那么,这些组件需要写入entryComponents。

3. Angular CLI生成模块

AngularCLI是一个很好很强大的工具集,可以帮助我们生成很多基础代码、文件, 包括创建一个模块,并且可以制定参数。

1
ng generate module <name> [options]

详情参考https://angular.io/cli/generate#module。

几个简单的例子:

  1. 创建feature1模块: ng generate module feature1
  2. 创建feature2模块, 并且带路由:ng generate module feature2 --routing
  3. 创建一个延迟加载的feature3模块(延迟加载模块,参考下面章节): ng generate module feature3 --route feature3 --module app.module

说明: ng generate module xxx 可以简写为 ng g m xxx

4. 延迟加载模块

延迟加载使得应用程序在启动时不被载入,而是结合路由配置,在需要时才动态加载相应模块。这样 Angular 就不需要在第一个界面从服务器下载所有的文件,直到请求它,才下载相应的模块。这对提供性能和减少首屏的初始下载文件尺寸有巨大的帮助,而且它可以很容易设置。

举例来说,上文创建了3个模块,主程序模块以及feature1、feature2会被打成一个包(js),feature3会被单独打一个包(js),当用户访问/feature3/* 的地址后,才会加载feature3这个包(js),否则永远不会请求、加载feature3的模块,从而提高性能(首页加载时间)。当一个项目大到一定程度时,最好考虑把某些模块设置为延迟加载模块。

延迟加载的路由定义(angular CLI会自动生成):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
{
path: 'feature3',
loadChildren: () =>
import('./feature3/feature3.module').then((m) => m.Feature3Module),
},
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}


最后复习一下生成延迟加载模块的命令ng generate module feature3 --route feature3 --module app.module,或者简写为 ng g m feature3 --route feature3 --module app.module

5. 总结

  1. Angular项目,就是模块(NgModule)的一个集合,任组件、服务等必须包含在一个模块中。
  2. 延迟加载模块用于提高首页加载性能。
  3. Angular CLI命令,生成模块。



—————- END —————-






======================

环境:

  • Angular CLI: 11.0.6
  • Angular: 11.0.7
  • Node: 12.18.3
  • npm : 6.14.6
  • IDE: Visual Studio Code

1. 摘要

当 Angular 实例化组件类并渲染组件视图及其子视图时,组件实例的生命周期就开始了。生命周期一直伴随着变更检测,Angular 会检查数据绑定属性何时发生变化,并按需更新视图和组件实例。当 Angular 销毁组件实例并从 DOM 中移除它渲染的模板时,生命周期就结束了。当 Angular 在执行过程中创建、更新和销毁实例时,指令就有了类似的生命周期。

你的应用可以使用生命周期钩子方法来触发组件或指令生命周期中的关键事件,以初始化新实例,需要时启动变更检测,在变更检测过程中响应更新,并在删除实例之前进行清理。

2. 生命周期及顺序

  1. ngOnChanges(): 当 Angular 设置或重新设置数据绑定的输入属性时响应。
  2. ngOnInit(): 在 Angular 第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。
  3. ngDoCheck(): 每次执行变更检测时的 ngOnChanges() 和 首次执行变更检测时的 ngOnInit() 后调用。
  4. ngAfterContentInit(): 当 Angular 把外部内容投影进组件视图或指令所在的视图之后调用。
  5. ngAfterContentChecked(): ngAfterContentInit() 和每次 ngDoCheck() 之后调用
  6. ngAfterViewInit(): 当 Angular 初始化完组件视图及其子视图或包含该指令的视图之后调用。
  7. ngAfterViewChecked(): ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用。
  8. ngOnDestroy(): 每当 Angular 每次销毁指令/组件之前调用,清理释放资源。

3. 响应生命周期事件

我们以通过实现一个或多个 Angular中定义的生命周期钩子接口来响应组件或指令生命周期中的事件。每个接口都有唯一的一个钩子方法,它们的名字是由接口名再加上 ng 前缀构成的。例如:

1
2
3
4
5
6
7
8
9
@Component()
export class DemoComponent implements OnInit {
constructor() { }

// implement OnInit's `ngOnInit` method
ngOnInit() {
// do something here
}
}

说明:
1) 通过生命周期钩子接口来响应生命周期中的事件,需要在类名之后,声明实现(implements) 具体的钩子接口。然后代码中定义个钩子函数才能被执行。如 ngOnInit() 对应 接口OnInit
2) 可以实现多个钩子接口,例如 export class DemoComponent implements OnInit, OnDestroy {

4. 主要生命周期事件

4.1. 初始化事件 ngOnInit()

使用 ngOnInit() 方法执行以下初始化任务:

  1. 逻辑稍复杂,不适合放到构造函数中的逻辑
  2. 初始化中的数据访问逻辑
  3. 处理需要根据父组件传入参数(@Input)进行初始化的逻辑

4.2. 实例销毁 ngOnDestroy()

把清理逻辑放进 ngOnDestroy() 中,这个逻辑就必然会在 Angular 销毁该指令之前运行。下列逻辑可言放到ngOnDestroy():

  • 取消订阅可观察对象和 DOM 事件。
  • 停止 interval 计时器。
  • 反注册该指令在全局或应用服务中注册过的所有回调。
  • 释放其他占有的资源。

5. 总结

  1. 使用生命周期事件钩子函数,别忘了类名后面implements 相应的接口,否则不生效;
  2. 初始化代码,区分哪些放构造函数,哪些放 ngOnInit();
  3. 可以精简的钩子事件方法来避免性能问题;
  4. ngOnChanges()发生的非常频繁,加入复杂逻辑会影响性能;



—————- END —————-






======================

环境:

  • Angular CLI: 11.0.6
  • Angular: 11.0.7
  • Node: 12.18.3
  • npm : 6.14.6
  • IDE: Visual Studio Code

1. 摘要

组件之间传递数据,最主要的就是父子组件之间传递数据, 例如:

1
2
3
<parent-component>
<child-component></child-component>
</parent-component>

父组件传入数据给子组件,同时,子组件数据发生变化是,希望能够通知父组件。

Angular 中,@Input() 和 @Output() 为子组件提供了一种与其父组件通信的方法。 @Input() 允许父组件更新子组件中的数据。相反,@Output() 允许子组件向父组件发送数据。

2. 父传子 @Input()

2.1. 子组件定义@Input()

子组件中的 @Input() 装饰器表示该属性可以从其父组件中获取值。

例如:

1
2
3
export class ChildComponent {
@Input() message: string;
}
  1. 增加@Input() 装饰器的变量,除了数据可以从父组件传入后,其他逻辑和普通变量一致;
  2. 子组件的html代码中,既可使用message这个变量, 例如:
    1
    2
    3
    <p>
    Parent says: {{message}}
    </p>

    2.2. 父组件传递变量给子组件

    当父组件调用子组件时,可以把父组件的变量(如messageToChild) 传递给子组件
    1
    <child-component [message]="messageToChild"></child-component>

子组件中,可以更改message这个传入的变量,但是其作用域只在子组件中,父组件拿不到更改后的结果。(如何传给父组件,请接着看)

3. 子传父 @Output()

Angular通过事件(Event)来实现子组件通知父组件数据的改变,父组件需要订阅该事件。

3.1. 子组件定义@Output

子组件定义@Output

1
2
3
4
5
6
7
8
9
10
11
export class ChildComponent {

// EventEmitter ,这意味着它是一个事件
// new EventEmitter<string>() -
// 使用 Angular 来创建一个新的事件发射器,它发出的数据是 string 类型的。
@Output() newItemEvent = new EventEmitter<string>();

addNewItem(value: string) {
this.newItemEvent.emit(value);
}
}

子组件当数据发生变化时,调用这个addNewItem方法既可。例如,html中

1
2
<label>Add an item: <input #newItem></label>
<button (click)="addNewItem(newItem.value)">Add to parent's list</button>

3.2. 父组件订阅事件

  1. 父组件的ts代码中,增加一个处理上面事件的方法,例如
    1
    2
    3
    addItem(newItem: string) {
    // logic here
    }
  2. 父组件的html中,订阅该事件。
1
<child-component (newItemEvent)="addItem($event)"></child-component>

事件绑定 (newItemEvent)='addItem($event)' 会把子组件中的 newItemEvent 事件连接到父组件的 addItem() 方法。

4. 总结

  1. 使用@Input() 和 @Output() 可以很方便的实现父子组件之间的数据传递、共享。
  2. 可以同时使用 @Input() 和 @Output()



—————- END —————-






======================

环境:

  • Angular CLI: 11.0.6
  • Angular: 11.0.7
  • Node: 12.18.3
  • npm : 6.14.6
  • IDE: Visual Studio Code

1. 摘要

近些年Web技术飞速发展,新的类库、框架如雨后春笋般不断涌现,而每个类库也不断的更新、升级,甚至是不再兼容的升级。笔者之前维护的一个6年项目,是用jQueryMobile开发的,估计入门web开发比较短的同学都没有听说过吧。另外,就拿Angular来说,从2.0开始,使用ts开发,不再兼容1.0 版本。从2.0开始,命名为Angular,1.0的叫angularJS, 2个版本彻底切割。而这也是angular流失很多用户的其中一个原因。

针对这些问题,Angular专门提供了版本升级的指导方案,非常详细、准确(Angular每半年发布一个主版本,这个也是必须的)。

2. https://update.angular.io/

https://update.angular.io/ 是官方的angular升级指导页面,你可以选择你的项目的版本,以及要升级到的版本;同时可以选择项目使用的技术,比如是否使用了Angular Material (这也是之前推荐这个UI框架的原因–升级方便),是否和AngualrJS同时使用等等。之后,自动提示升级步骤。

升级主要通过 ng update xxx 来实现, ng update 命令不同于npm命令,npm update相当于 npm+更改配置(代码),也就是说,再更新完node-modules之后,ng update会自动更新配置文件,甚至是代码中的基本的import的代码,实现自动升级、更新。

举例,10.2升级到11.1,如果没有涉及到复杂的anuglar技术,大致的升级步骤是:

  • Run ng update @angular/core @angular/cli which should bring you to version 11 of Angular.
  • Run ng update @angular/material.
  • Angular now requires TypeScript 4.0. ng update will migrate you automatically.
  • Support for IE9, IE10, and IE mobile has been removed. This was announced * in the v10 update.

3. 总结

  • Angular虽然更新、升级比较频繁(从某种意义上也是优点),但是升级部分确实做得最完善的
  • UI框架,@angular/material可以实现和Angular的同步升级,其他第三方框架,都会有演示,时间不等
  • 个人不推荐使用最新的版本,不做小白鼠。可以使用之前发布的一个版本,比如现在是v11,那么我们就使用v10。更稳定,同时网上资料也多。
  • 及时更新angular版本,免得技术债欠的越来越多而导致无法升级。
  • 这个升级指导也是相对的,如果你对Angular有些特殊的用法,或者没有安装官方推荐方式使用,升级也是比较麻烦的。
  • 升级时,如果跨多版本,建议一个版本一个版本的升级,每升级一次,执行UT或者把站点跑起来试试,以防出错。



—————- END —————-






======================

环境:

  • Angular CLI: 11.0.6
  • Angular: 11.0.7
  • Node: 12.18.3
  • npm : 6.14.6
  • IDE: VSCode

1. 第三方UI库的选择

Angular开发,除非你只有简单一两个页面,否则引入第三方UI库就是必不可少的。而具体用哪个库,就需要考虑多方面的东西了。比如:

  1. 流行程度
  2. 版本更新情况 (是否能跟进anuglar更新,bug是否及时修复)
  3. UI风格,是否适用于项目
  4. 入手难易程度
  5. 文档完善程度

市面上有很多Angular可用的类库,可用根据项目、企业情况去选择。简单列一下可选的UI库。

  1. Angular Material: angular 官方UI库。https://material.angular.io/
  2. clarity: 国外的一套比较流行的框架, https://clarity.design/。
  3. NG-ZORRO: 阿里Ant Design的Angular版本。 https://ng.ant.design/docs/introduce/en
  4. Element Angular:国内非常流行的基于Vue的Element,同样有Angular版本。 https://element-angular.faas.ele.me/
  5. Kendo UI。很多很强大,只是要收费。https://www.telerik.com/kendo-angular-ui

2. Angular Material

2.1. 优缺点

2.1.1. 优点

  • 官方UI组件
  • 最标准的Material实现
  • 紧跟Angular核心组件的更新进度
  • 官方支持与Angular的同步升级

2.1.2. 缺点

  • 组件不像其他框架那么多,但是基本够用
  • 风格不是特别像国内的框架

2.2. 引入到项目

  1. 根目录下执行下面命令:

    1
    ng add @angular/material
  2. import 到页面所属的module,或者是app.module.ts中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // import MatSliderModule
    import { MatSliderModule } from '@angular/material/slider';


    @NgModule ({....
    imports: [...,
    MatSliderModule,
    …]
    })

    3. 总结

  3. 第三方组件各有优缺点

  4. 根据公司、项目的需求去选择

  5. Angular Material升级更容易,官方支持。

@[TOC]

了解了一些Angular的基本概念后,如果想进一步学习Angular,接下来让我们来搭建本地开发环境,并从一个入门项目了解Angular的基本用法。

环境:

  • Angular CLI: 11.0.6
  • Angular: 11.0.7
  • Node: 12.18.3
  • npm : 6.14.6
  • IDE: VSCode

1. 本地开发环境搭建

本地开发环境搭建只需要node.js, 和Angular CLI。

1.1. node.js

官网(https://nodejs.org/)下载最新的LTS(Long Time Support)版本的node.js,安装。

说明:

  1. LTS(Long Time Support)版本, 官方会支持更长时间,比如打补丁,改bug等。相对更稳定、靠谱。
  2. node.js 安装后,同时会安装npm

检查本地node.js, npm环境

1
2
3
4
# node.js 版本
node -v
# npm 版本
npm -v

1.2. Angular CLI

angular-cli又称 Angular脚手架,是angular开发团队自行维护的一个开发工具,用于快速生成项目或者组件的框架以提高效率。可以方便的生成angular app、component、service 等等, 并且可以通过参数,按照自己的需求去创建。可以说是angular开发必不可少的利器。(参考:https://cli.angular.io/)

npm安装最新版本@angular/cli

1
npm install -g @angular/cli

检查本地angular环境

1
ng v

说明:

  1. 该命令如果在非angular项目下执行,返回全局的Angular CLI环境版本
  2. 在angular项目下执行, 返回当前angular项目使用的angular,angular CLI,以及核心angular组件的版本。
  3. 全局Angular CLI版本有何能与项目的Angular CLI版本不一致,不冲突。项目中,使用项目制定的CLI版本。

2. 开发工具 - Visual Studio Code

推荐使用,Visual Studio Code (VSCode),微软开发的,可以说是现今为止最好的免费的Angular开发工具。并且有很多非常好用的插件。

推荐插件:

  • Angular Language Service: Angular语法自动提示, Angular开发必备。 This extension provides a rich editing experience for Angular templates, both inline and external templates.
    This extension is brought to you by members of the Angular team. It is fantastic at helping write solid code in the html templates.
  • Prettier - 代码自动格式化插件。VS Code plugin for prettier/prettier, which formats code consistently. Prettier is an opinionated code formatter. It enforces a consistent style by parsing your code and re-printing it with its own rules that take the maximum line length into account, wrapping code when necessary.
  • Code Spell Checker - 语法检查插件. 注释可以写中文,变量名不行吧,如果拼写不对不好看吧。所以推荐把这个语法检查插件装上。
  • GitLens - GIT 辅助插件。If you use git, this is quite helpful. GitLens supercharges the Git capabilities built into Visual Studio Code. It helps you to visualize code authorship at a glance via Git blame annotations and code lens, seamlessly navigate and explore Git repositories, gain valuable insights via powerful comparison commands, and so much more.
  • Markdown All in One: 如果用Markdown写东西,这个东西一定要有,增加了对MD文件的很多支持,比如生成目录(TOC), 目录编号等。

第一个Anuglar项目

创建第一个anuglar项目

使用Anuglar CLI可以很轻松的创建angular项目。使用的Angular版本与当前环境的Anuglar CLI一致。

1
2
3
4
5
6
# 创建angular项目,项目名 ·my-ngular-app·
ng new my-ngular-app
# 进入项目目录
cd my-ngular-app
# 启动angualar项目
ng serve

说明

  1. ng 是angular CLI的简称
  2. ng serve: 启动angular项目。

Angular CLI常用命令

  1. ng serve: 启动angular项目。默认情况下,angular CLI检测代码改动,如果文件改动,自动编译更改部分代码,然后重新加载(reload)页面。
  2. ng build: 编译代码,默认输出到根目录下的dist目录。
  3. ng test: 执行单元测试(Unit Test)

在线实战项目

Angular官方提供了2个新手入门项目,并且都是基于StackBlitz(针对 Web 开发者的在线 IDE),可以不使用本地环境,直接基于Web学习和练习Angular。

新手项目:Basic Angular app

入门项目:Tour of Heroes