Angular入门
本人对前端技术并不精通,所以文章主要以笔记的形式记录。
环境搭建
首先安装Node,上官网下载:nodejs.org/zh-cn/
然后安装angular:
npm install -g @angular/cli // 最新版本
npm install -g @angular/cli@x.x.x // 指定版本
创建angular项目:
ng new project_name // 创建angular项目
cd project_name // 进入根目录
ng serve // 启动服务
安装的时候根据需要选择,我选择了CSS的。
在浏览器输入:http://localhost:4200 即可看到页面
目录介绍
- node_modules # 项目依赖包
- src # 代码模块
- app # 业务代码
app-routing.module.ts
app.component.css
app.component.html
app.component.ts
app.module.ts
- assets # 静态资源
- environments # 项目环境配置
favicon.ico # 浏览器tab标签栏的图标
index.html # 单页面应用的入口
main.ts # 项目入口,是项目启动的地方
style.css # 全局公共样式
package-lock.json # npm包依赖的配置文件
package.json # 同上
其他没讲的暂时用不上
根模块
首先来看看app.module.ts文件
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
- declarations:用来存放组件、指令
组件
组件是Angular应用的主要构造块,每个组件包括如下部分:
- 一个HTML模板,用于声明页面要渲染的内容
- 一个用于定义行为的Typescript类
- 一个CSS选择器,用于定义组件在模板中的使用方式
- (可选)要应用在模板上的CSS样式
自动创建组件
使用Angular Cli创建组件十分简单:
-
在终端中,进入到应用目录,我放在/src/app里
-
运行:
ng generate component hello-world
默认情况下,该命令会在app目录下创建以下内容:
- 以该组件命名的文件夹
- 组件文件:hello-world.component.ts
- 模板文件:hello-world.component.html
- CSS文件:hello-world.component.css
- 测试文件:hello-world.component.spec.ts
然后我们进入app.module.ts文件,可以看到Angular Cli创建组件的时候会自动帮我们在该配置文件中导入:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HelloWorldComponent } from './hello-world/hello-world.component'; // 1. 导入hello-world组件
@NgModule({
declarations: [
AppComponent,
HelloWorldComponent // 2. 声明hello-world组件
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
手动创建组件
手动创建组件的话,创建上面说明的文件,然后手动在app.module.ts中导入(import)和声明(declarations)即可。
使用组件
编辑hello-world模板的内容:
hello-world.component.html:
<p>hello-world works!</p>
启动服务后默认显示的内容是app.component.html,现在将这里面的内容清空掉,改成:
<app-hello-world></app-hello-world>
进入网页,可以看到刚刚的修改生效了,页面显示的是hello-world模板的内容。
模板
插值和表达式,在hello-world.component.html中可以通过插值和表达式来显示值:
在hello-world.component.ts组件中定义一个变量:
import { Component } from '@angular/core';
@Component({
selector: 'app-hello-world',
templateUrl: './hello-world.component.html',
styleUrls: ['./hello-world.component.css']
})
export class HelloWorldComponent implements OnInit {
public title = "这里是一段插值"
}
然后在hello-world.component.html模板中使用:
<p>hello-world works!</p>
<p>{{ title }}</p>
显示效果:
除了导入文件,可以在当前ts文件内写一些简单的模板内容或者CSS样式:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-hello-world',
template: '<h1>{{title}}</h1>',
styles: ['h1 { color: red;}']
})
export class HelloWorldComponent implements OnInit {}
模板表达式
模板表达式会产生一个值并绑定给目标的某个属性:
<p>The sum of 1 + 1 is {{1 + 1}}.</p>
也可以调用宿主组件的方法:
<p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}.</p>
表达式也可以引用模板中的属性
<h4>{{ h4Value }}</h4>
<img [src]="imgUrl">
命名冲突
在属性命名中要注意防止冲突,避免不必要的麻烦。示例:
hello-world.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-hello-world',
templateUrl: './hello-world.component.html',
styleUrls: ['./hello-world.component.css']
})
export class HelloWorldComponent implements OnInit {
public customers = [{value: 'Ebony'}, {value: 'Chiho'}]
public customer = 'Padma'
}
hello-world.component.html
<h4>{{ h4Value }}</h4>
<img [src]="imgUrl">
<div>
<h1>Hello, {{ customer }}</h1>
<ul>
<li *ngFor="let customer of customers">{{ customer.value }}</li>
</ul>
</div>
ngFor中的customer处于的上下文中,所以指向的是数组中的customer,即Ebony 和 Chiho。经过测试,当数组中的customer修改为customer2,则插值中的customer会指向customer属性,即Padma,所以customer.value会报错。
模板语句
在模板中调用组件的方法
hello-world.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-hello-world',
templateUrl: './hello-world.component.html',
styleUrls: ['./hello-world.component.css']
})
export class HelloWorldComponent implements OnInit {
public deleteHero(): void {
console.log("deleteHero");
}
}
hello-world.component.html
<button (click)="deleteHero()">Delete Hero</button>
点击按钮时,Angular就会调用组件中的deleteHero方法。
管道
管道用来对字符串、货币金额、日期和其他显示数据进行转换和格式化。
示例:使用管道将日期进行格式化
hello-world.component.ts
public birthday = new Date(1988, 3, 15);
hello-world.component.html
<p>日期从{{ birthday }}转换成{{ birthday | date}}</p>
<p>日期从{{ birthday }}转换成{{ birthday | date: 'MM/dd/yy' }}</p>
显示如下:
属性绑定和传值
属性绑定
要绑定到元素的属性,需要将其括在方括号内:[],该括号会将属性标为目标属性,目标属性就是要对其进行赋值的DOM属性。例如下面代码中的目标属性是img元素的src属性:
<img [src]="imgUrl">
传值
传值一般有三种情况:
- 父组件向子组件传值
- 子组件向父组件传值
- 双向传值
使用**@Input()和@Output()**,@Input()允许父组件更新子组件中的数据,@Output()允许子组件向父组件发送数据。
**注意:**在这个部分里,我们会用到Input,Output和EventEmitter模块,所以需要在import里面导入,这一点别忘了。
父组件向子组件传值
因为是在app.component.html中引用hello-world模板,所以在这里app是父组件,hello-world是子组件。
首先是子组件:
hello-world.component.ts
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-hello-world',
templateUrl: './hello-world.component.html',
styleUrls: ['./hello-world.component.css']
})
export class HelloWorldComponent implements OnInit {
// 子控件的值
@Input() public sonNumber = 1;
// 随机改变值
public changeSonNumber() {
this.sonNumber = Math.random();
}
}
hello-world.component.html
<div>子组件中的值:{{ sonNumber }}</div>
<button (click)="changeSonNumber()">点击改变子组件的值</button>
然后是父组件:
app.component.ts
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public defaultNumber = 0;
public changeDefaultNumber() {
this.defaultNumber = Math.random();
}
}
app.component.html
<app-hello-world [sonNumber]="defaultNumber"></app-hello-world>
<div>父组件中的值:{{ defaultNumber }}</div>
<button (click)="changeDefaultNumber()">点击改变父组件的值</button>
显示效果:
此时点击按钮《点击改变子组件的值》,只有子组件在变化,
点击按钮《点击改变父组件的值》,父组件和子组件都会变化。
子组件向父组件传值
首先是子组件:
hello-world.component.ts
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-hello-world',
templateUrl: './hello-world.component.html',
styleUrls: ['./hello-world.component.css']
})
export class HelloWorldComponent implements OnInit {
// 子控件的值
@Input() public sonNumber = 1;
// 命名规范要注意是:xxxChange
// 因为子控件的值是sonNumber,所以这里就要命名成:sonNumberChange
@Output() public sonNumberChange = new EventEmitter<number>();
// 随机改变值
public changeSonNumber() {
this.sonNumber = Math.random();
// 发送值改变的信号
this.sonNumberChange.emit(this.sonNumber)
}
}
hello-world.component.html
和前面一样,没有改变,为了方便阅读也贴出来
<div>子组件中的值:{{ sonNumber }}</div>
<button (click)="changeSonNumber()">点击改变子组件的值</button>
然后是父组件:
app.component.ts
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public defaultNumber = 0;
public changeDefaultNumber() {
this.defaultNumber = Math.random();
}
// 接收到子组件传来的值
public handleSonNumberChange(value: any) {
this.defaultNumber = value
}
}
app.component.html
<app-hello-world [sonNumber]="defaultNumber" (sonNumberChange)="handleSonNumberChange($event)"></app-hello-world>
<div>父组件中的值:{{ defaultNumber }}</div>
<button (click)="changeDefaultNumber()">点击改变父组件的值</button>
显示效果一样,但是:
点击按钮《点击改变子组件的值》,父组件和子组件都会变化,
点击按钮《点击改变父组件的值》,父组件和子组件都会变化。
由此就实现了子组件向父组件传值的目的。
双向绑定
在上面我们可以发现,子组件向父组件传值比较麻烦,Angular提供了一个更加方便的双向绑定功能,语法是方括号和圆括号的组合:[()]
首先是子组件:
hello-world.component.ts
我们用一个新的变量sonSize来做
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-hello-world',
templateUrl: './hello-world.component.html',
styleUrls: ['./hello-world.component.css']
})
export class HelloWorldComponent implements OnInit {
// 双向绑定
@Input() public sonSize = 1;
@Output() public sonSizeChange = new EventEmitter<number>();
// 随机改变值
public changeSonSize() {
this.sonSize = Math.random();
// 发送值改变的信号
this.sonSizeChange.emit(this.sonSize);
}
}
hello-world.component.html
和前面一样,没有改变,为了方便阅读也贴出来
<div>点击改变子组件中sonSize的值:{{ sonSize }}</div>
<button (click)="changeSonSize()">点击改变子组件中sonSize的值</button>
然后是父组件:
app.component.ts
父组件
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public defaultSize = 0;
public changeDefaultSize() {
this.defaultSize = Math.random();
}
}
app.component.html
<app-hello-world [(sonSize)]="defaultSize"></app-hello-world>
<div>父组件中defaultSize的值:{{ defaultSize }}</div>
<button (click)="changeDefaultSize()">点击改变父组件defaultSize的值</button>
显示效果:
双向绑定能够实现子组件和父组件之间的双向通讯。
双向绑定对比上面,在父组件中省略了一些代码,通过[()]语法将父组件的属性和事件一并绑定了。
生命周期
列举一些常用的:
app.component.ts
import {
AfterViewInit,
Component,
Input,
OnChanges,
OnDestroy,
OnInit,
SimpleChanges,
} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent
implements OnChanges, OnInit, AfterViewInit, OnDestroy
{
public ngOnChanges(changes: SimpleChanges): void {
// @Input()属性值变更的时候触发此函数
console.log('ngOnChanges');
}
public ngOnInit(): void {
// 组件初始化的时候调用一次
console.log('ngOnInit');
}
public ngAfterViewInit(): void {
// 试图更新之后或者说DOM元素渲染之后调用一次
console.log('ngAfterViewInit');
}
public ngOnDestroy(): void {
// 组件销毁的时候调用,在这里清除定时器或者异步
console.log('ngOnDestroy');
}
}