前言 Angular相比于原生js有什么优势? 什么是单页面应用(SPA)呢?
优势:
项目架构很清晰
代码的复用性强
代码的可测试强,可以添加e2e测试,单元测试
单页面应用相比较之前请求多个html页面的优势
用户体验好,一次性加载所有的js和css,内容的改变不需要重新加载整个页面
前后端分离
Angular开发环境搭建 在原生js里一般通过<aaaa>
标签引入一些第三方库,随着框架的的普及,之前引入的方式略low,工作中也很少用到这种方式,在学习Angular时候,我们就采用脚手架形式的安装。这就需要我们安装Node.js
,用里边的npm
来进行安装。
node安装 http://nodejs.cn/
可以通过 node - v 以及 npm - v 的形式来检查是否安装成功
脚手架安装 node安装成功之后,可以使用npm命令安装脚手架
npm install - g @angular/ cli
npm install - g @angular/ cli@xx. xx. x
创建一个新项目并执行 ng new project_name
cd project_name
ng serve
浏览器输入http: / / localhost: 4200 便可看到相关页面
[图片上传失败...(image-b13261-1637659554980)]
输入命令之后,按照接下来的提示,进行选择,第一个是否选择在严格模式下创建这个项目;第二是否在创建项目的时候帮你把router文件一并创建出来;第三是用那种css类型进行编码。可以按照上图的选择情况进行创建。
项目目录介绍 [图片上传失败...(image-6ff86f-1637659554980)]
组件介绍 组件的概念 [图片上传失败...(image-537545-1637659554980)] 介绍组件之前,我们先来介绍一下组件
和模块
之前的关系,框架都是推崇组件化开发的,页面上所有的元素都可以看做一个组件,具体页面中组件划分的粒度是多大,还是按照自己的个人开发习惯来进行。在angular中我们知道了app模块
是我们的根模块,他管理了整个单页面应用中涉及到其他模块
和组件
。我们来熟悉一下app.module.ts这个根模块文件。
[图片上传失败...(image-702fde-1637659554980)]
@NgModule是 angular中的装饰器,是需要从'@angular/core'导入进来 作用: 帮助开发者组织业务代码,开发者可以利用 NgModule 把关系比较紧密的组件组织到一起,他不仅可以控制组件,还可以控制指令,管道等(本期我们只要先学会组件,指令和管道后期会讲到)
declarations :用来放组件、指令、管道
的声明, 组件、指令、管道都必须属于一个模块,而且只能属于一个
模块。
imports :用来导入外部模块
而非组件。
exports : 我们这个模块需要导出的一些组件,指令,模块
等; 如果别的模块导入了我们这个模块,那么别的模块就可以直接使用我们在这里导出的组件,指令模块等.
providers :需要使用的Service
都放在这里。指定应用程序的根级别需要使用的service
bootstrap :定义启动组件。你可能注意到了这个配置项是一个数组,也就是说可以指定做个组件作为启动点。一般这个不需要改动。
由上述可见: angular中单页面是有一个根模块的,这个模块中包含了很多组件,也就是说多个组件组成了模块。
hello world以及组件讲解 首先我们先将app.component.html中的内容全部清空,为我们接下来的组件学习做准备。
1. 在app文件夹下,新建一个组件文件,文件名为hello-world.component.ts
[图片上传失败...(image-b51833-1637659554980)] 从上图片中,我们可以得知组件的组成有以下三部分组成:
类 (类名的命名是根据组件的文件名来决定的,首字母大写的驼峰命名方式)
装饰器@component , 需要从'@angular/core'中导入,其作用是把某个类标记为Angular组件,并为他配置一些元数据,目前这里只涉及到3个元数据,其中selector
也称作是选择器,我们可以把他理解成我们自定义组件的名字,一般他的命名也是app-组件文件
的名字,更多的元数据应用可以参照官网。
HTML模板 ,就是template。
2. 上边已经提到到,组件必须在模块中声明才可以正常使用,所以我在根模块声明一下
[图片上传失败...(image-e5e65a-1637659554980)]3. 我们将定义好的组件在app的html中中引用一下
[图片上传失败...(image-2029ac-1637659554980)]
这个时候页面上已经可以完美的展现出 hello world了,这样一个简单的组件也就封装好了。
组件中的插值表达式应用 假如有个用户在上述的组件中不希望展示”hello world“,怎么样动态灵活的显示在上述组件中呢?
其实在angular中我们可以使用插值
的方式,将一些变量或者表达式
嵌入到文本中,默认是用{{}}来作为定界符
import { Component } from '@angular/core' ;
@Component ( {
selector: 'app-hello-world' ,
template: '<h1> {{title}} </h1>' ,
styles: [ 'h1 { color: red; }' ]
} )
export class HelloWorldComponent {
public title = '学习Angular第一课' ;
}
此时打开浏览器页面,就可以看到页面上展示” 学习Angular第一课 “
组件中内置指令的介绍 首先我们来看一下什么是指令?其实指令是为 Angular 应用程序中的元素
添加额外行为的类
。 这里主要介绍一下属性型指令
和结构型指令
。属性型指令:
NgClass
: 动态添加和删除一组css类
import { Component } from '@angular/core' ;
@Component ( {
selector: 'app-hello-world' ,
template: `
<div [ngClass]="ngClassHasColor ? 'ngclass-color' : ''">练习Ngclass的使用</div>
` ,
styles: [
'h1 { color: red; }' ,
'.ngclass-color {color: green}'
]
} )
export class HelloWorldComponent {
public ngClassHasColor = true ;
}
NgStyle
: 动态添加和删除一组内联样式
1. import { Component } from '@angular/core' ;
@Component ( {
selector: 'app-hello-world' ,
template: `
<div [ngStyle]="{'color': 'black', 'font-size': '18px'}">练习Ngstyle的使用</div>
` ,
styles: [ ]
} )
export class HelloWorldComponent { }
2. import { Component } from '@angular/core' ;
@Component ( {
selector: 'app-hello-world' ,
template: `
<div [ngStyle]=ngStyleObj>练习Ngstyle的使用</div>
` ,
styles: [ ]
} )
export class HelloWorldComponent {
public ngStyleObj = {
'color' : 'black' ,
'font-size' : '18px'
} ;
}
NgModel
: 将数据的双向绑定添加到HTML表单元素上结构型指令:
NgFor
: 重复循环渲染某一个节点
import { Component } from '@angular/core' ;
@Component ( {
selector: 'app-hello-world' ,
template: `
<ul>
<li *ngFor="let subject of studySubjects">{{subject}}</li>
</ul>
` ,
styles: [ ]
} )
export class HelloWorldComponent {
public studySubjects = [
'环境搭建' ,
'项目目录介绍' ,
'组件介绍' ,
'ng-zorro组件库的引入介绍'
] ;
}
< ul>
< li * ngFor= "let subject of studySubjects; let i=index" > { { i+ 1 } } - { { subject } } < / li>
< / ul>
循环结果如下所示: [图片上传失败...(image-91ad8a-1637659554980)]
NgIf
: 从模板中创建或销毁子视图,通俗说是控制组件或者元素的显示和隐藏
import { Component } from '@angular/core' ;
@Component ( {
selector: 'app-hello-world' ,
template: `
<ul *ngIf="userRole === 'backend'">
<li *ngFor="let subject of studySubjects; let i=index">{{ i+1 }} - {{ subject }}</li>
</ul>
` ,
styles: [ ]
} )
export class HelloWorldComponent {
public userRole= 'backend' ;
}
NgSwitch
: 类似于js中的switch的逻辑,也是按照满足固定的条件显示某些模板/视图
NgSwitch是一组属性(三个) 分别是: NgSwitch
; NgSwitchCase
; ngSwitchDefault
import { Component } from '@angular/core' ;
@Component ( {
selector: 'app-hello-world' ,
template: `
<div [ngSwitch]="age">
<span *ngSwitchCase="'12'">小学毕业</span>
<span *ngSwitchCase="'18'">高中毕业</span>
<span *ngSwitchCase="'22'">大学毕业</span>
<span *ngSwitchDefault>未知</span>
</div>
` ,
styles: [ ]
} )
export class HelloWorldComponent {
public age = 18 ;
}
组件中属性的绑定 在上述内容中,我们发现很多用[]
包裹起来的值,比如之前提到的[ngClass]
,[ngStyle]
,其实这些被中括号包裹起来的,我们就称作是属性绑定
。他的作用就是动态设置 HTML 元素或指令
的属性值
。接下来我们介绍一下在开发中常见的属性值。
基本属性绑定 import { Component } from '@angular/core' ;
@Component ( {
selector: 'app-hello-world' ,
template: `
<img [src]="itemImageUrl">
<button [disabled]="isDisabledBtn">Disabled Button</button>
<span [innerHtml]="spanText"></span>
` ,
styles: [ ]
} )
export class HelloWorldComponent {
public itemImageUrl = '../../assets/bg.png' ;
public isDisabledBtn = false ;
public spanText= '通过属性绑定来展示span标签的内容' ;
}
绑定到class的Attribute(平时开发推荐使用,且用的频率很高) 此方式可以动态的添加和删除单个或者多个类,与上述的[ngClass]很类似,但是用法更加的简单
import { Component } from '@angular/core' ;
@Component ( {
selector: 'app-hello-world' ,
template: `
<div [class.textcolor]="isSingleClass">尝试绑定单个class</div>
<div [class]="multipleClass">尝试绑定多个class</div>
` ,
styles: [
'.bgColor{background-color: yellow}' ,
'.textcolor{color: red}'
]
} )
export class HelloWorldComponent {
public isSingleClass: boolean;
public multipleClass = {
bgColor: true ,
textcolor: false ,
} ;
}
组件中管道的使用 管道用来对字符串、货币金额、日期和其他显示数据
进行转换和格式化
。它是一些简单的函数,angular中提供了一下内置的管道方法,同时我们也可以自定义管道。
import { Component } from '@angular/core' ;
@Component ( {
selector: 'app-hello-world' ,
template: `
<p>小明的生日是{{ birthday | date }}</p>
<p>小明的生日是{{ birthday | date:"MM/dd/yy" }}</p>
` ,
styles: [ ]
} )
export class HelloWorldComponent {
public birthday = new Date ( 1988 , 3 , 15 ) ;
}
小明的生日是Apr 15 , 1988
小明的生日是04 / 15 / 88
该组件的 birthday
值通过管道操作符(|)流向 date
函数。 date: 后边的值是传入到这个管道的参数
,如果有多个参数可以用多个:
来分割传入
组件的事件绑定-方法和参数 组件的事件绑定确实比原生js的事件绑定简便很多,我们先来介绍一下angular中是怎么样实现事件绑定的吧~
按钮的事件绑定
import { Component } from '@angular/core' ;
@Component ( {
selector: 'app-hello-world' ,
template: `
<button (click)="addCount()">当前数值加1</button>
<span>当前值是:{{curNumber}}</span>
` ,
styles: [ ]
} )
export class HelloWorldComponent {
public curNumber = 1 ;
public addCount ( ) : void {
this . curNumber++ ;
}
}
事件绑定中的$event对象
在编写响应事件时,是可以接受一个$event参数的,这个参数就是关于响应事件的一些内容.
< button ( click) = "addCount($event)" > 当前数值加1 < / button>
public addCount ( event) : void {
console. log ( event) ;
this . curNumber++ ;
}
事件的传参
< button ( click) = "addCount($event, 5)" > 当前数值加1 < / button>
public addCount ( event, paramsNumber: number) : void {
this . curNumber = this . curNumber + paramsNumber;
}
一个按钮同时调用两个方法
< button ( click) = "addCount($event, 5);consoleCount()" > 当前数值加1 < / button>
public addCount ( event: any, paramsNumber: number) : void {
this . curNumber = this . curNumber + paramsNumber;
}
public consoleCount ( ) : void {
console. log ( this . curNumber) ;
}
组件的声明方式 上述示例中,我们是把html,css已经ts的逻辑写到同一个文件中的,在真正开发的过程中,如果逻辑较复杂这种内联式的组件声明是不推荐的,一般情况下我们常用的是外部引用文件的形式
,如下所示
import { Component } from '@angular/core' ;
@Component ( {
selector: 'app-outside-introduction' ,
templateUrl: './outside-introduction.component.html' ,
styleUrls: [ './outside-introduction.component.scss' ]
} )
export class OutsideIntroductionComponent { }
具体的组件OutsideIntroductionComponen
创建如下:
[图片上传失败...(image-3d8197-1637659554980)]
组件的父子组件传值 @Input()
和 @Output()
为子组件提供了一种与其父组件通信的方法。 @Input()
允许父组件更新子组件中的数据。相反,@Output()
允许子组件向父组件发送数
父组件向子组件里传值是通过属性
进行传值的
import { Component, Input } from '@angular/core' ;
@Component ( {
selector: 'app-outside-introduction' ,
templateUrl: './outside-introduction.component.html' ,
styleUrls: [ './outside-introduction.component.scss' ]
} )
export class OutsideIntroductionComponent {
@Input ( ) public showNumber: number;
}
< div> 显示父组件传进来的值{ { showNumber} } < / div>
< div> 父组件中的值:{ { defaultNum} } < / div>
< app- outside- introduction [ showNumber] = "defaultNum" > < / app- outside- introduction>
import { Component } from '@angular/core' ;
@Component ( {
selector: 'app-root' ,
templateUrl: './app.component.html' ,
styleUrls: [ './app.component.scss' ]
} )
export class AppComponent {
public defaultNum = 5 ;
}
上述核心代码其实只要两步:
1. 子组件通过@Input声明要进行通信的变量
@Input ( ) public showNumber: number;
2. 父组件在引入的时候通过属性绑定来传递在ts中定义好的变量
< app- outside- introduction [ showNumber] = "defaultNum" > < / app- outside- introduction>
export class AppComponent {
public defaultNum = 5 ;
}
子组件向父组件传值,通过事件的形式来实现 假如上述子组件有个累加器的功能,希望将内部不断累计的东西传递给父组件,并在父组件显示改怎么做呢?
子组件
import { Component, Input, Output, EventEmitter } from '@angular/core' ;
@Component ( {
selector: 'app-outside-introduction' ,
templateUrl: './outside-introduction.component.html' ,
styleUrls: [ './outside-introduction.component.scss' ]
} )
export class OutsideIntroductionComponent {
@Input ( ) public showNumber: number;
@Output ( ) public curCountChange = new EventEmitter < number> ( ) ;
public curCount = 1 ;
public addCount ( ) : void {
this . curCount++ ;
this . curCountChange. emit ( this . curCount) ;
}
}
父组件
< app- outside- introduction [ showNumber] = "defaultNum" ( curCountChange) = "handlCurCountChange($event)" > < / app- outside- introduction>
public handlCurCountChange ( value: number) : void {
this . valueFromChild = value;
}
详细介绍一下子组件中的@output
@Output()
- 一个装饰器函数,它把该属性标记为数据从子组件进入父组件的一种途径
curCountChange
- 这个 @Output()
的名字
EventEmitter<number>
- 这个 @Output()
的类型,就是子组件传给父组件的数据类型
new EventEmitter<number>()
- 使用 Angular 来创建一个新的事件发射器,它发出的数据是 number
类型的。
curCountChange.emit()
- 通过emit方法来向父组件传递值 最终父组件通过事件的形式接受子组件穿过来的值
组件的双向绑定 其实双向绑定就是特殊一点或者是一个更加简洁的父子组件传值,他是通过[()]
语法来组合属性和事件绑定,唯一特殊的点在于他们的命名,为了使双向数据绑定有效,@Output()
属性的名字必须遵循 inputChange
模式,其中 input
是相应 @Input()
属性的名字。例如,如果 @Input()
属性为 size
,则 @Output()
属性必须为 sizeChange
。 在这里我们不做过多的赘述,直接通过表单元素
中使用最多的[(ngModel)]
来认识双向绑定。
< input [ ( ngModel) ] = "inputValue" >
export class OutsideIntroductionComponent {
public inputValue = 5 ;
}
针对双向绑定要注意的点:
[(ngModel)]
语法只能设置数据绑定属性。 如果用户想在input框中的值更改的时候做一些额外的事情,应该怎么办? ===> 我们可以把这个组合模式拆解开来,具体如下:
< input [ ( ngModel) ] = "inputValue" ( ngModelChange) = "consoleValue($event)" >
public consoleValue ( value: string) : void {
console. log ( value) ;
}
组件的生命周期 angular组件的生命周期共有9个,但是在真正开发过程中用到只有3-4个,今天我们主要讲一下常用的生命周期吧~
[图片上传失败...(image-903c24-1637659554980)]
具体的生命周期请参考:生命周期详情
ts在angular组件中的使用 其实在上述所有的文件中,我们经常看到一些public,void这种关键字的声明,包括我们组件中用到的class,这些都是typeaaaa中的一些内容,不过我们常用的还是ts中一些类型的定义,在angular我们是要求所有的变量,函数都是要用类型定义的~ 首先我们先来简单的认识一下ts
ts是js的superType
,也就是说他更加的规范,而且可以在编译阶段
就能针对不规范的代码进行提示.
[图片上传失败...(image-4ec2c1-1637659554981)] 常见的一些类型定义:
以下为类型注解/ 类型定义
public a: number ;
public b: boolean ;
public c: string ;
public d: any ;
public e: number [ ] = [ 1 , 2 , 31 ] ;
public f: any [ ] = [ 1 , true , 'a' , false ] ;
enum Color {
Red,
Green,
Blue,
}
public backgroundColor = Color;
类型推断,像这些简单的类型,如果直接赋值的话,那么ts可以智能推断出这个什么类型
public curCount = 1 ;
public inputValue = 5 ;
[图片上传失败...(image-282c2d-1637659554981)]
在项目中引入ng-zorro组件库 在本项目下安装指定版本的组件库 npm install ng-zorro-antd@8.5.2 --save
npm install安装文件的具体位置分析
npm install xxx
: 安装项目到项目目录下,不会将模块依赖写入devDependencies
或dependencies
。
npm install -g xxx
: -g
是将模块安装到全局,具体安装到磁盘哪个位置,要看 npm cinfig prefix
的位置
npm install -save xxx
:-save
的意思是将模块安装到项目目录下,并在package
文件的dependencies
节点写入依赖。当是在项目所必须的,则用这个命令
npm install -save-dev xxx
:-save-dev
的意思是将模块安装到项目目录下,并在package
文件的devDependencies
节点写入依赖。当是开发过程中所依赖的需要用这个命令
在app.module中引入这个组件
[图片上传失败...(image-650ccc-1637659554981)]
在全局的style.scss文件中引入组件样式
@import "~ng-zorro-antd/style/index.min.css" ;
@import "~ng-zorro-antd/button/style/index.min.css" ;
在angular.json文件中引入svg和样式
{ "assets" : [
...
{ "glob" : "**/*" ,
"input" : "./node_modules/@ant-design/icons-angular/src/inline-svg/" ,
"output" : "/assets/" }
] ,
"styles" : [
...
"node_modules/ng-zorro-antd/ng-zorro-antd.min.css" ]
}
如想了解更加详细的引入UI组件,请参考快速安装ng-zorro-antd