1、angular 介绍
Angular 是一个由 Google维护的开源JavaScript框架,用于在HTML和JavaScript中构建Web应用程序。它包括:
- Angular CLI 可以快速搭建框架,创建module,service,class,directive等;
- 具有webpack的功能,代码分割,按需加载;
- 代码打包压缩;
- 模块测试;
- 热部署,有改动立即重新编译,不用刷新浏览器;而且速度很快
- 有开发环境,测试环境,生产环境的配置,不用自己操心;
- sass,less的预编译Angular CLI都会自动识别后缀来编译;
- typescript的配置,Angular CLI在创建应用时都可以自己配置;
- 在创建好的工程也可以做一些个性化的配置,webpack的具体配置还不支持,未来可能会增加;
- Angular CLI创建的工程结构是最佳实践,生产可用。
2、angular安装
//安装脚手架
npm install -g angular-cli
//创建项目
ng new project_name(项目名称)
//启动项目
cd project_name
ng serve --open
3、angular常用命令
内容 | 命令 |
---|---|
component | ng g component 名称 |
dirctive | ng g directive 名称 |
pipe | ng g pipe名称 |
sevice | ng g sevice名称 |
class | ng g class名称 |
interface | ng g interface名称 |
enum | ng g enum名称 |
module | ng g module名称 |
4、组件
1、创建组件
输入命令快速创建组件
ng g component 名称
\\也可以简写 ng g c 名称
例如输入命令 ng g c hello
即可生成一个hello文件夹,其中有4个文件
.html文件为网页布局代码
.scss文件为样式代码
.spec.ts文件为单元测试代码
.ts文件为业务逻辑代码,需要编写ts类型
在 .ts文件中会生成如下代码
@Component({
selector: 'app-hello',
templateUrl: './hello.component.html',
styleUrls: ['./hello.component.scss']
})
selector:组件的命名
template:组件的内联模板
templateUrl:组件模板文件的 URL
styleUrls:组件样式文件
styles:组件内联样式
2、生命周期
Angular 会按以下顺序执行钩子方法。你可以用它来执行以下类型的操作。
钩子方法 | 用途 | 时机 |
---|---|---|
ngOnChanges() | 当 Angular 设置或重新设置数据绑定的输入属性时响应。 该方法接受当前和上一属性值的 SimpleChanges 对象注意,这发生的非常频繁,所以你在这里执行的任何操作都会显著影响性能。 | 在 ngOnInit() 之前以及所绑定的一个或多个输入属性的值发生变化时都会调用。注意,如果你的组件没有输入,或者你使用它时没有提供任何输入,那么框架就不会调用 ngOnChanges() 。 |
ngOnInit() | 在 Angular 第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。 | 在第一轮 ngOnChanges() 完成之后调用,只调用一次。 |
ngDoCheck() | 检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应。 | 紧跟在每次执行变更检测时的 ngOnChanges() 和 首次执行变更检测时的 ngOnInit() 后调用。 |
ngAfterContentInit() | 当 Angular 把外部内容投影进组件视图或指令所在的视图之后调用。 | 第一次 ngDoCheck() 之后调用,只调用一次。 |
ngAfterContentChecked() | 每当 Angular 检查完被投影到组件或指令中的内容之后调用 | ngAfterContentInit() 和每次 ngDoCheck() 之后调用 |
ngAfterViewInit() | 当 Angular 初始化完组件视图及其子视图或包含该指令的视图之后调用 | 第一次 ngAfterContentChecked() 之后调用,只调用一次。 |
ngAfterViewChecked() | 每当 Angular 做完组件视图和子视图或包含该指令的视图的变更检测之后调用。 | ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用。 |
ngOnDestroy() | 每当 Angular 每次销毁指令/组件之前调用并清扫。 在这儿反订阅可观察对象和分离事件处理器,以防内存泄漏 | 在 Angular 销毁指令或组件之前立即调用。 |
3、组件通信
1、父向子传递数据
父组件通过@Input
给子组件绑定属性设置输入类数据 (类似vue中的props)
//父组件
<app-hello [name]="'小明'"></app-hello>
//子组件
import { Component, Input } from '@angular/core';
@Input()
name!: string;
ngOnInit(): void {
console.log(this.name)
}
2、子向父传递数据
子组件通过@Output
弹射触发事件,再通过emit发送数据。
父组件再在子组件绑定相应的事件即可
//子组件
import { Component, Output,EventEmitter } from '@angular/core';
@Output() addList = new EventEmitter()
pushList(v:string){
this.addList.emit(v)
}
//父组件
<app-hello (addList)="addListFun($event)" [name]="'小明'"></app-hello>
list:number[] = [1,2,3,4]
addListFun(num:number){
this.list.push(num)
}
3、父组件触发子组件方法
angular中通过ViewChild
获取子组件实例,获取子组件数据 (类似vue中的ref)
//给子组件绑定 #mychild
<app-hello #myChild [name]="'小明'"></app-hello>
<button (click)="getView($event)">获取</button>
@ViewChild('myChild') child: any;
constructor() { }
getView(e:any){
this.child.setInpValue('我是一段数据')
}
5、模板
1、插值语法
<h3>hello {{ name }}</h3>
2、属性绑定
1、id绑定
<h3 [id]="'new-Id'">hello</h3>
2、 class类绑定
//单一类绑定
<h3 [class.new-Dom]="true">hello</h3>
//多重类绑定
<h3 [class]="'new-Dom title-dom min-title'">hello</h3>
<h3 [class]="{'new-Dom':true,'title-dom':false}">hello</h3>
<h3 [class]="['new-Dom','title-dom']">hello</h3>
//ngClass写法
<h3 [ngClass]="{'active': isActive}">hello {{ 1+1 }}</h3>
3、行内样式style绑定
//单一样式绑定
<h3 [style.width]="'300px'">hello</h3>
//带单位的单一样式绑定
<h3 [style.width.px]="'300'">hello</h3>
//多重样式绑定
<h3 [style]="'background:red;color:#fff'">hello</h3>
<h3 [style]="{'background':'red','color':'#fff'}">hello</h3>
//ngStyle写法
<h3 [ngStyle]="{'color': 'red'}">hello</h3>
<h3 [ngStyle]="{'font-size': isMax ? '24px' : '12px'}">hello</h3>
3、条件判断
ngIf是直接影响元素是否被渲染,而非控制元素的显示和隐藏
<div *ngIf="isMax">Max title</div>
<div *ngIf="isMin">Min title</div>
ngSwitch判断
<ul [ngSwitch]="status">
<li *ngSwitchCase="1">已支付</li>
<li *ngSwitchCase="2">订单已经确认</li>
<li *ngSwitchCase="3">已发货</li>
<li *ngSwitchDefault>无效</li>
</ul>
4、循环语句
<div *ngFor="let color of colors let i=index let odd=odd">
{{odd}}
{{i}}
{{color}}
</div>
odd是布尔值,指示当前索引是否是偶数索引
5、事件绑定
<button (click)="onSave($event)">Save</button>
export class AppComponent {
onSave(){
console.log('点击了按钮')
}
}
6、数据双向绑定
<div>
输入: <input [(ngModel)]="userName">
<h1>你输入了: {{userName}}</h1>
</div>
export class AppComponent {
userName='';
}
7、模板引用变量
在模板中,要使用 #
来声明一个模板变量。(类似vue中的ref)下列模板变量 #userName
语法在 <input>
元素上声明了一个名为 userName
的变量
<input #userName placeholder="请输入用户名" />
可以在组件模板中的任何地方引用某个模板变量
<button (click)="callUserName(userName.value)">Call</button>
export class AppComponent {
callUserName(v){
console.log(v)
}
}
- 如果在组件上声明变量,该变量就会引用该组件实例。
- 如果在标准的 HTML 标记上声明变量,该变量就会引用该元素。
- 如果你在
<ng-template>
元素上声明变量,该变量就会引用一个TemplateRef
实例来代表此模板。
8、表单控件
要注册一个表单控件,就要导入 FormControl
类并创建一个 FormControl
的新实例,将其保存为类的属性。
<input type="text" [formControl]="name">
<p>Value: {{ name.value }}</p>
import { FormControl } from '@angular/forms';
export class NameEditorComponent {
name = new FormControl('');
}
修改name值可以通过FormControl
提供的 setValue()
方法
updateName() { this.name.setValue('Tina');}
9、表单控件分组
表单中通常会包含几个相互关联的控件。响应式表单提供了两种把多个相关控件分组到同一个输入表单中的方法。
要将表单组添加到此组件中,请执行以下步骤。
- 创建一个
FormGroup
实例。 - 把这个
FormGroup
模型关联到视图。 - 保存表单数据。
创建一个 FormGroup 实例
在组件类中创建一个名叫 loginForm
的属性,并设置为 FormGroup
的一个新实例。要初始化这个 FormGroup
,请为构造函数提供一个由控件组成的对象,对象中的每个名字都要和表单控件的名字一一对应
import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
@Component({
selector: 'app-profile-editor',
templateUrl: './profile-editor.component.html',
styleUrls: ['./profile-editor.component.css']
})
export class ProfileEditorComponent {
loginForm = new FormGroup({
userName: new FormControl(''),
password: new FormControl(''),
});
}
//模板渲染
<form [formGroup]="loginForm">
<label>
账号:
<input type="text" formControlName="userName">
</label>
<label>
密码:
<input type="text" formControlName="password">
</label>
</form>
10、表单验证
1、普通验证
表单元素添加required
关键字表示必填,通过绑定ngModel
的引用可以拿到到当前组件的信息,通过引用获取到验证的信息
<form action="">
账号:<input required #nameInp="ngModel" type="text" [(ngModel)]="fromData.name" name="userName">
<br>
<span>{{nameInp.valid }}</span>
<hr>
密码:<input required #pasInp="ngModel" type="text" [(ngModel)]="fromData.password" name="password">
<br>
<span>{{pasInp.valid }}</span>
<hr>
<button (click)="subBtnFUn(nameInp)">提交</button>
</form>
export class AppComponent {
fromData={
name:'',
password:''
};
subBtnFUn(obj){
console.log(obj)
}
}
2、自定义验证
import { FormGroup, FormBuilder,Validators } from '@angular/forms';
//构造函数里注入FormBuilder
constructor(private fb:FormBuilder) { }
//错误提醒数据
formErrors = {
'title': '',
'content': ''
};
//在组件类的初始化函数里对表单中的元素的校验进行定义,并调用表单的valueChanges方法,检测表单的输入的变化
ngOnInit():void {
this.taskInfo.isComplete = 1;
this.tasksForm = this.fb.group({
userName: ['', [Validators.required,
Validators.maxLength(18),
Validators.minLength(6) ] ],
password: ['', [this.passWordVal]],
phone: ['', [Validators.required,this.phoneVal],]
});
phoneVal(phone: FormControl): object {
const value = phone.value || '';
if(!value) return {desc:'请输入手机号'}
const valid = /[0-9]{11}/.test(value);
return valid ? {} :{desc:'联系电话必须是11位数字'}
}
passWordVal(password:FormControl):object{
const value = password.value || '';
const valid = value.match(/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/);
return valid ? {} : {passwordValidator: {desc:'密码至少包含 数字和英文,长度6-20'}}
}
}
11、管道
管道的作用就是传输。不同的管道具有不同的作用。(其实就是处理数据,类似vue2中的filter)
常用管道如下:
管道 | 功能 |
---|---|
DatePipe | 日期管道,格式化日期 |
JsonPipe | 将输入数据对象经过JSON.stringify()方法转换后输出对象的字符串 |
UpperCasePipe | 将文本所有小写字母转换成大写字母 |
LowerCasePipe | 将文本所有大写字母转换成小写字母 |
DecimalPipe | 将数值按照特定的格式显示文本 |
CurrentcyPipe | 将数值进行货币格式化处理 |
SlicePipe | 将数组或者字符串裁剪成新子集 |
PercentPipe | 将数值转百分比格式 |
注意管道要小写
<p>{{ 'Angular' | uppercase }}</p>
<p>{{dateStr | date:'yyyy-MM-dd HH:mm:ss'}} </p>
6、服务
angular中,把从组件内抽离出来的代码叫服务,服务的本质就是函数
官方认为组件不应该直接获取或保存数据, 它们应该聚焦于展示数据,而把数据访问的职责委托给某个服务。而服务就充当着数据访问,逻辑处理的功能。把组件和服务区分开,以提高模块性和复用性。通过把组件中和视图有关的功能与其他类型的处理分离开,可以让组件类更加精简、高效。
使用命令ng g s xxx创建一个服务,通过**@Injectable()**装饰器标识服务。
//导入Injectable装饰器
import { Injectable } from '@angular/core';
//使用Injectable装饰器声明服务
@Injectable({
//作用域设定,'root'表示默认注入,注入到AppModule里
providedIn: 'root',
})
export class TestService {
}
依赖注入
在这个例子中,providedIn: 'root'
指定 Angular 应该在根注入器中提供该服务,从而实现根注入器将服务注入,它就在整个应用程序中可用了。
- ‘root’ :注入到AppModule,提供该服务,所有子组件都可以使用(推荐)
- null : 不设定服务作用域(不推荐)
- 组件名:只作用于该组件(懒加载模式)
服务文件
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class TestService {
constructor() {}
list:Array<string>=['angular','react','vue']
getList(){
return this.list
}
}
调用服务的文件
import { Component, OnInit } from '@angular/core';
import {ListService} from '../hero.service'
@Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss'],
providers: [ListService]
})
export class TestComponent implements OnInit {
constructor(private ListService:ListService) { }
list:Array<string> | undefined
ngOnInit(): void {
this.list = this.ListService.getList()
}
}
7、路由
路由器是一个调度中心,它是一套规则的列表,能够查询当前URL对应的规则,并呈现出相应的视图.
路由是列表里面的一个规则,即路由定义,它有很多功能字段:
- path字段,表示该路由中的URL路径部分
- Component字段,表示与该路由相关联的组件
每个带路由的Angular应用都有一个路由器服务的单例对象,通过路由定义的列表进行配置后使用。
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import {HomeComponent} from './home/home.component'
const routes: Routes = [
{path:'home',component:HomeComponent}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
//路由导航
<a [routerLink]="['/home']">home</a>
<a [routerLink]="['/hello']">hello</a>
//组件渲染输出
<router-outlet></router-outlet>
1、路由嵌套
const routes: Routes = [
{path:'home',
component:HomeComponent,
children:[
{path:'hello',component:HelloComponent}
]
},
];
在home Component内这是router-outlet
路由出口,即可在home 路由内渲染子级路由
//home template
<h2>home Component</h2>
<a [routerLink]="['/home/hello']">hello</a>
<router-outlet></router-outlet>
2、路由传参
1、query
在a标签上添加一个参数queryParams,并通过this.routerinfo.snapshot.queryParams
获取参数
<a [routerLink]="['/hello']" [queryParams]="{id:3}" >hello</a>
import {ActivatedRoute} from '@angular/router';
constructor(private routerinfo:ActivatedRoute) { }
ngOnInit() {
//id为参数名字
this.id=this.routerinfo.snapshot.queryParams["id"]
}
2、param
修改路由配置文件path
,路由导航a
标签routerLink
后面数组的第二个参数为传递的值,并且通过subscribe
请阅的方式获取name
参数。
{
path: 'hello/:name',
component:HelloComponent,
},
//我们在后面添加/:name此时name即为传递的参数名字
//a标签设置如下,注意顺序不可错乱。
<a [routerLink]="['/hello','我是url传递参数']" [queryParams]="{id:3}" >hello</a>
ngOnInit() {
this.routerinfo.params.subscribe((params:Params)=>{
this.name=params['name']
})
}