1 基础语法
1.0
ng new my-app
ng serve --open
// 创建一个main组件, 系统为该组件命名为app-main供使用, <app-main></app-main>
ng generate component main (简写 ng g c main)
ng g s
ng g c session/info/msg (在session文件下的info文件夹创建msg组件)
1.1 ngFor 和 事件
- 便利元素
<ul>
<li class="her" *ngFor="let hero of hero" (click)="onSelect(hero)">
<span>{{hero.id}}</span>
<span>{{hero.name}}</span>
</li>
</ul>
export class HerosComponent implements OnInit {
constructor() { }
hero = HEROES
selectedHero: Hero;
onSelect(hero: Hero): void {
this.selectedHero = hero;
console.log('hero', hero)
}
ngOnInit() {
console.log('生命钩子函数-ngOnInit')
}
}
- 遍历块
<ng-template ngFor let-item [ngForOf]="userAllMsgArr" let-i="index" [ngForTrackBy]="trackByAllMsg" >
<div *ngIf="item.isTrue">
<li>{{i}}---{{item.name}}</li>
</div>
</ng-template>
1.1 事件传参
- # list表示dom,
<div class="user" *ngFor="let item of userList" #list (click)="clickUser(item, list)"></div>
- $event
<div class="user" *ngFor="let item of userList" #list (click)="clickUser(item, $event)"></div>
1.1 获取dom元素
<div class="msg-box" #msgBox></div>
import { Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-chat',
templateUrl: './chat.component.html',
styleUrls: ['./chat.component.css']
})
export class ChatComponent implements OnInit {
@ViewChild('msgBox') msgBox: ElementRef
constructor() { }
testFn(dm): void {
this.msgBox.nativeElement
}
}
1.2 ngIf
<div class="hero" *ngIf="selectedHero"></div>
1.3 父组件往子组件传值Input
- 父组件box
// box.component.html
<div class="box">
<h3>box component</h3>
<app-box-top [num]=100></app-box-top>
</div>
- 子组件box-top
// box-top.component.html
<div class="box-top">
<h4>box-top component</h4>
<h4>{{num}}</h4>
</div>
import { Component, OnInit, Input, Output } from '@angular/core';
@Component({
selector: 'app-box-top',
templateUrl: './box-top.component.html',
styleUrls: ['./box-top.component.css']
})
export class BoxTopComponent implements OnInit {
// 声明对外暴露的接口, 用于接受父组件的数据源输入, 必须是Number类型
@Input() num: Number;
constructor() { }
ngOnInit() {
}
}
1.4 行内样式ngStyle
<div class="box-top">
<div [ngStyle]="setStyle()">ngStyle</div>
</div>
export class BoxTopComponent implements OnInit {
constructor() { }
setStyle() {
return {
'font-size': '22px',
'color': 'blue'
}
}
ngOnInit() {
}
}
1.5 双向数据绑定ngModel
必须先在根模块中引入FormsModule
import { FormsModule } from '@angular/forms';
@NgModule({
// ...
imports: [
FormsModule
],
})
export class AppModule { }
<div class="box-top">
<h4>{{person}}</h4>
<input type="text" value="person" [(ngModel)]="person">
</div>
export class BoxTopComponent implements OnInit {
constructor() { }
person: string = 'tom'
ngOnInit() {}
}
1.6 类
<div class="box-top">
<h4 [ngClass]="{box-title: sty.hasTitle}">设置类</h4>
</div>
1.7 ViewChild实现调用子组件方法
- 父组件box
<div class="box">
<div (click)="runBoxTopClick()">调用子组件box-top的testClick方法</div>
</div>
import { Component, OnInit, ViewChild } from '@angular/core';
import { BoxTopComponent } from '../box-top/box-top.component'
@Component({
selector: 'app-box',
templateUrl: './box.component.html',
styleUrls: ['./box.component.css']
})
export class BoxComponent implements OnInit {
@ViewChild(BoxTopComponent) boxTopFn: BoxTopComponent;
constructor() { }
runBoxTopClick() {
console.log('box调用子组件box-top里的testClick方法')
this.boxTopFn.testClick()
}
ngOnInit() {
}
}
- 子组件box-top
import { Component, OnInit, Input, Output } from '@angular/core';
@Component({
selector: 'app-box-top',
templateUrl: './box-top.component.html',
styleUrls: ['./box-top.component.css']
})
export class BoxTopComponent implements OnInit {
constructor() { }
testClick() {
console.log('box-top')
}
ngOnInit() {
}
}
1.8 组件内容嵌入
- 父组件box
<div class="box">
<h3>box</h3>
<app-box-child select="[name=middle]">
<div class="middle">
<h4>嵌入内容到子组件, 此h4会嵌入到子组件中</h4>
</div>
<div class="bottom">
<h4>嵌入内容到子组件, 此内容不会到子组件中, 不会显示在页面中</h4>
</div>
<div name="top">
<h5>嵌入内容到子组件, 此h5会嵌入到子组件中</h5>
</div>
</app-box-child>
</div>
- 子组件box-child
<div class="box-child">
<ng-content select="[name=top]"></ng-content>
<ng-content select=".middle"></ng-content>
</div>
1.9 事件参数
// [value] input值的绑定
<input type="number" name="num" [value]="person.num" (input)="inputChange($event)" />
person: Object = {
num: 0
}
inputChange(e) :void {
this.person.num = e.target.value
}
1.10 使用表单指令
- 在根模块中导入FormsModule
import { FormsModule } from @angular/forms;
// 在所有模块中都能使用
@NgModule({
FormsModule
})
1.11 内置管道(过滤器)
<div class="pip">
<span>{{this.mydate | date: "yyyy-mm-dd hh:mm:ss"}}</span>
<button (click)="getNowTime()">获取当前时间</button>
</div>
export class MainComponent implements OnInit {
constructor() { }
mydate: Object
getNowTime(): void {
this.mydate= new Date()
}
}
1.12 自定义管道
- 使用指令创建一个成绩等级的管道
ng generate pipe scoresLevel
- scores-level.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'scoresLevel'
})
export class ScoresLevelPipe implements PipeTransform {
transform(value: Number): String {
console.log('scoresLevel-----', value)
if (value >= 90 && value <= 100) {
return 'A';
} else if (value >= 80 && value < 90) {
return 'B';
} else if (value >= 70 && value < 80) {
return 'C';
} else if (value < 70) {
return 'D';
}
}
}
- 使用管道
<div class="diy-pip">
<div>{{myScores | scoresLevel}}</div>
<input type="number" [(ngModel)]="myScores" >
</div>
import { Component, OnInit, Input, EventEmitter } from '@angular/core';
@Component({
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css']
})
export class MainComponent implements OnInit {
constructor() { }
myScores: Number = 88
}
1.13 父子组件公用一个服务实例
- commonService.ts
import { Injectable } from '@angular/core';
export class CommonService {
products: object[] = []
addProducts(info: Object): void {
this.products.push(info)
}
clearProducts(): void {
this.products = []
}
}
- app.component
<div class="add-product">
<input type="text" [(ngModel)]="product.title" placeholder="名称">
<input type="text" [(ngModel)]="product.des" placeholder="详情">
<input type="text" [(ngModel)]="product.price" placeholder="价格">
<input type="text" [(ngModel)]="product.addr" placeholder="产地">
<button (click)="addProducts()">addProducts</button>
</div>
<app-main></app-main>
import { Component } from '@angular/core';
import { CommonService } from './commonService'
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [CommonService]
})
export class AppComponent {
product: Object = { title: '', des: '', price: '', addr: '' }
constructor( private _CommonService: CommonService) {}
addProducts(): void {
this._CommonService.addProducts(this.product)
console.log('-app-', this._CommonService.products)
}
}
- 子组件main.component
<div class="common-service">
<button (click)="getProductList()">获取商品列表</button>
<li *ngFor="let item of productList">
<span>{{item.title}}</span> -
<span>{{item.des}}</span> -
<span>{{item.price}}</span> -
<span>{{item.addr}}</span>
</li>
</div>
import { Component, OnInit, Input, EventEmitter } from '@angular/core';
import { CommonService } from '../commonService'
@Component({
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css']
})
export class MainComponent implements OnInit {
productList: Array<object>
constructor( private _CommonService: CommonService) { }
getProductList(): void {
this.productList = this._CommonService.products
}
}
1.14 服务器通信get
npm install --save rxjs rxjs-compat
ng generate service ajax
- 根路径引入HttpModule
import { HttpModule } from '@angular/http';
@NgModule({
imports: [
HttpModule
],
})
- ajax.service.ts
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/Rx';
const url_getStudents: string = 'https://127.0.0.1:3000/studentsList'
@Injectable({
providedIn: 'root'
})
export class AjaxService {
constructor( private _http: Http ) { }
getPruductList(): Observable<any[]> {
return this._http.get(url_getStudents)
.map(((res) => {
console.log('res.json()', res.json())
return res.json()
}))
}
}
- main.component
<div class="http">
<button (click)="getPruductList()">get</button>
</div>
import { Component, OnInit, Input, EventEmitter } from '@angular/core';
import { CommonService } from '../commonService'
import { AjaxService } from '../ajax.service'
@Component({
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css']
})
export class MainComponent implements OnInit {
constructor(
private _AjaxService: AjaxService
) { }
getPruductList(): void {
this._AjaxService.getPruductList().subscribe(contacts => {
console.log('contacts', contacts)
}, err => {
console.log('err', err)
})
}
}
1.15 服务器通信post
ng generate service ajax
- 根路径引入HttpModule
import { HttpModule } from '@angular/http';
@NgModule({
imports: [
HttpModule
],
})
- Ajax.service.ts
import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/Rx';
const url_addStudents: string = 'https://127.0.0.1:3000/add'
@Injectable({
providedIn: 'root'
})
export class AjaxService {
constructor( private _http: Http ) { }
addProductList(): Observable<any[]> {
// let headers = new Headers({'Content-Type': 'application/x-www-form-urlencoded'})
let headers = new Headers({'Content-Type': 'application/json'})
let options = new RequestOptions({headers:headers})
return this._http.post(url_addStudents, {name: 'tom'}, options)
.map(((res) => {
console.log('res.json()----post', res.json())
return res.json()
}))
}
}
- Main.component
<div class="http">
<button (click)="addProductList()">post</button>
</div>
import { Component, OnInit, Input, EventEmitter } from '@angular/core';
import { AjaxService } from '../ajax.service'
@Component({
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css']
})
export class MainComponent implements OnInit {
constructor(
private _AjaxService: AjaxService
) { }
addProductList(): void {
this._AjaxService.addProductList().subscribe(contacts => {
console.log('contacts', contacts)
}, err => {
console.log('err', err)
})
}
}
1.16 使用socket.io
- polyfills.ts
(window as any).global = window;
- app.component.ts
import * as socketIo from "socket.io-client";
export class AppComponent {
io = socketIo
ngOnInit(): void {
console.log('--socket--', this.io)
}
}
1.7 访问暂时不存在的对象
export class ChatComponent implements OnInit {
@Input() userAllMsg: object
constructor() { }
ngOnInit() {}
ngOnChanges(changes: object): void {
if (this.userAllMsg) {
// 访问暂时不存在的对象时需要用[] 不能用点语法
this.userAllMsgArr = this.userAllMsg['userAllMsg']
}
}
}
2 生命周期
2.1 ngOnChanges
ngOnChanges当前晋档@Input装饰器显示指定的变量变化时被调用
- 父组件box
<div class="box">
<h2>box</h2>
<button (click)="addBoxNum()">改变num</button>
<app-box-child [num]="boxNum"></app-box-child>
</div>
import { Component, OnInit, ViewChild } from '@angular/core';
import { BoxTopComponent } from '../box-top/box-top.component'
@Component({
selector: 'app-box',
templateUrl: './box.component.html',
styleUrls: ['./box.component.css']
})
export class BoxComponent implements OnInit {
@ViewChild(BoxTopComponent) boxTopFn: BoxTopComponent;
constructor() { }
addBoxNum(): void {
this.boxNum += 1
}
boxNum: Number = 12
ngOnInit() {
}
}
- 子组件box-child
<div class="box-child">
<h4>父组件传过来的num: {{num}}</h4>
</div>
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'app-box-child',
templateUrl: './box-child.component.html',
styleUrls: ['./box-child.component.css']
})
export class BoxChildComponent implements OnInit {
@Input() num: Number;
ngOnChanges(data): void {
console.log('父元素传过来的值改变了', data)
}
constructor() { }
ngOnInit() {
}
}
- 运行结果
[外链图片转存失败(img-2ZfkdrFe-1563117055663)(assets/ngOnChanges.gif)]
2.2 ngOnInit
该方法内可用于获取数据, 类似于vue的created
2.3 ngDoCheck
3 路由
3.1 初始
- app.routes.ts
import { Routes } from '@angular/router'
import { ListComponent } from './list/list.component'
export const rootRouterConfig: Routes = [
{path: 'list', component: ListComponent}
]
- app.module.ts
import { rootRouterConfig } from './app.routes'
import { NgModule, ModuleWithProviders } from '@angular/core';
import { RouterModule } from '@angular/router'
let rootRouteModule:ModuleWithProviders = RouterModule.forRoot(rootRouterConfig)
@NgModule({
imports: [
rootRouteModule
],
})
export class AppModule { }
- App.component.html
<div class="route">
<h3>route</h3>
<a [routerLink]="['/list']">goto-list</a>
<router-outlet></router-outlet>
</div>
3.2 路由策略
是否使用hash
let rootRouteModule:ModuleWithProviders = RouterModule.forRoot(
rootRouterConfig, {useHash: false}
)
// HashLocationStrategy策略
// useHash: true => http://localhost:4200/#/list
// PathLocationStrategy策略
// useHash: false => http://localhost:4200/list
- 使用PathLocationStrategy策略时需在服务器将所有路由重定向到首页, 因为应用会将请求路径原封不动发往后台
3.3 激活路由样式
被激活的路由自动赋予atv类
<div routerLinkActive="atv">
<a [routerLink]="['/list']">goto-list</a>
</div>
<div routerLinkActive="atv">
<a [routerLink]="['/detail']">goto-detail</a>
</div>
.atv {
background: red;
}
3.4 默认跳转路由
- app.component.ts
import { Router } from '@angular/router'
export class AppComponent {
constructor(
private _router: Router
) {
_router.navigateByUrl('/list')
}
}
3.4 js跳转路由
- app.component.html
<div class="route-fn" (click)="jsGoToList()">js-route</div>
- app.component.ts
import { Router } from '@angular/router'
export class AppComponent {
constructor(
private _router: Router
) {}
jsGoToList(): void {
this._router.navigateByUrl('/list?num=8') // localhost:4200/#/list?num=8
// 或者this._router.navigate(['/list'], {queryParams: {num: 8}}) localhost:4200/#/list?num=8
}
}
3.5 路由传参
// 实现路由带参数
localhost:4200/#/list?num=8
// 按钮
<a [routerLink]="['/list']" [queryParams] = "{num: 10}">goto-list</a>
// js
this._router.navigate(['/list'], {queryParams: {num: 8}})
this._router.navigateByUrl('/list?num=8')
3.6 获取路由的参数
- app.component.ts
<div routerLinkActive="atv">
<a [routerLink]="['/detail']" [queryParams] = "{num: 10, from: 'a', addr: 'usa'}">to-detail</a>
</div>
- detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'
@Component({
selector: 'app-detail',
templateUrl: './detail.component.html',
styleUrls: ['./detail.component.css']
})
export class DetailComponent implements OnInit {
constructor(
private _activatedRoute: ActivatedRoute
) { }
ngOnInit() {
this._activatedRoute.queryParams.subscribe(params => {
console.log(params) // {num: "10", from: "a", addr: "usa"}
})
}
}
3.7 子路由
- app.routes.ts
import { Routes } from '@angular/router'
import { DetailComponent } from './detail/detail.component'
import { InfoComponent } from './info/info.component'
export const rootRouterConfig: Routes = [
{path: 'detail', component: DetailComponent, children: [
// path为空表示默认显示的路由
// {path: '', component: Info2Component}
{path: 'info', component: InfoComponent}
]}
]
- detail.component.html
<div class="detail">
<h3>detail-component</h3>
<a [routerLink]="['/detail/info']" [queryParams] = "{name: 'tom'}">goto-detail-info</a>
<router-outlet></router-outlet>
</div>
3.8 附属路由
同一个页面只能有一个router-outlet , 可以有多个附属路由
- app.routes.ts
import { Routes } from '@angular/router'
import { DetailComponent } from './detail/detail.component'
import { InfoComponent } from './info/info.component'
export const rootRouterConfig: Routes = [
{path: 'detail', component: DetailComponent, children: [
{path: 'info', component: InfoComponent},
{path: 'addr', component: AddrComponent, outlet='card'},
{path: 'age', component: AgeComponent, outlet='card'}
]}
]
- detail.component.html
<div class="detail">
<h3>detail-component</h3>
<a [routerLink]="['/detail/info']" [queryParams] = "{name: 'tom'}">goto-detail-info</a>
<router-outlet></router-outlet>
<router-outlet name="card"></router-outlet>
</div>
4 ng2的特点
- 和ng1相比, 对手机端的支持更好, 体积更小;
- ng和vue的dom渲染不同
支持pwa
新的 CLI 命令ng add 将使你的项目更容易添加新功能。ng add使用软件包管理器来下载新的依赖包并调用安装脚本,它可以通过更改配置和添加额外的依赖包(如 polyfills)来更新你的应用。
你可在新的ng new应用程序中尝试以下动作:
- ng add @angular/pwa:添加一个 app manifest 和 service worker,将你的应用程序变成 PWA。
- ng add @ng-bootstrap/schematics:将ng-bootstrap添加到你的应用程序中。
- ng add @angular/material:安装并设置 Angular Material 和主题,注册新的初始组件 到ng generate中。
- ng add @clr/angular@next:安装设置 VMWare Clarity。
依赖注入
我们在组件需要某个服务的实例时,不需要手动创建这个实例,只需要在构造函数的参数中指定这个实例的变量,以及这个实例的类,然后angular会根据这个类的名字找到providers属性中指定的同名的provide,再找到它对应的useclass,最终自动创建这个服务的实例。
使用面向对象的代码制造一辆车, 下面代码中的汽车一旦制造, 发动机, 门, 车身都不能更改
import Engine from './engine';
import Doors from './doors';
import Body from './body';
export default class Car {
engine: Engine;
doors: Doors;
body: Body;
constructor() {
this.engine = new Engine();
this.body = new Body();
this.doors = new Doors();
}
run() {
this.engine.start();
}
}
解决依赖问题, 下面代码再车被制造出来之后仍然可以更改汽车的发动机, 车身, 门
let engine = new NewEngine();
let body = new Body();
let doors = new Doors();
this.car = new Car(engine, body, doors);
this.car.run();
angular的依赖注入
@Injectable()
export default class Car {
constructor(
private engine: Engine,
private body: Body,
private doors: Doors) {}
run() {
this.engine.start();
}
};
rxjs
Rxjs是一种针对异步数据流的编程。它将一切数据,包括HTTP请求,DOM事件或者普通数据等包装成流的形式,然后用强大丰富的操作符对流进行处理,能以同步编程的方式处理异步数据,并组合不同的操作符来所需要的功能。
- 获取数据需要订阅
const ob = http$.getSomeList(); //getSomeList()返回某个由`Observable`包装后的http请求
ob.subscribe((data) => console.log(data));
ob.subscribe({
next: d => console.log(d),
error: err => console.error(err),
complete: () => console.log('end of the stream')
})
// 直接给subscribe传入一个函数会被当做是next函数。这个完整的包含3个函数的对象被称为observer(观察者),表示的是对序列结果的处理方式。next表示数据正常流动,没有出现异常;error表示流中出错,可能是运行出错,http报错等等;complete表示流结束,不再发射新的数据。在一个流的生命周期中,error和complete只会触发其中一个,可以有多个next(表示多次发射数据),直到complete或者error。
5 注意点
5.1 正则compile报错
let reg = new RegExp("/::\\)|/::~|/::B|/::\\||/:8-\\)|", 'g');
reg.compile(reg)
// error TS2554: Expected 0 arguments, but got 1.
有时候你会遇到这样的情况,你会比TypeScript更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。
通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript会假设你,程序员,已经进行了必须的检查。
类型断言有两种形式:
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
- 消除错误
let reg = new RegExp("/::\\)|/::~|/::B|/::\\||/:8-\\)|", 'g');
(<any>reg).compile(reg)
5.1 angular.json
里面内容是严格json格式, 不能出现注释不然会报错
Unexpected token / in JSON at position
6 使用material
6.1 配置
npm install --save @angular/material @angular/cdk
npm install --save @angular/animations
npm install --save hammerjs
// app.module
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MaterialModule } from './material.module';
@NgModule({
imports: [
MaterialModule
],
})
export class AppModule { }
// material.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material';
import {MatTableModule} from '@angular/material/table';
@NgModule({
imports: [
MatButtonModule,
MatTableModule
// 要使用的组件
],
exports: [
MatButtonModule,
MatTableModule
// 要使用的组件
],
})
export class MaterialModule { }
// main.ts
import 'hammerjs';
// style.css
@import "~@angular/material/prebuilt-themes/indigo-pink.css";
6.2 table
<mat-table [dataSource]="orderList" class="mat-elevation-z8">
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef class="print-order-num"> 姓名 </mat-header-cell>
<mat-cell *matCellDef="let element" class="print-order-num"> {{element.name}} </mat-cell>
</ng-container>
<ng-container matColumnDef="addr">
<mat-header-cell *matHeaderCellDef> 地址 </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.addr}} </mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>
// 用于显示列
displayedColumns: string[] = ['name', 'addr'];
orderList: Array<object> = [
{name: 'tom', addr: 'usa'},
{name: 'jack', addr: 'uk'}
]