「 Angular 4.0从入门到实战 打造股票管理网站」学习笔记
init 1 2 3 4 5 6 7 8 9 10 ng config -g cli.packageManager yarn ng new $project --routing -S --style=scss yarnpkg add @angular/material @angular/cdk @angular/animations ng g c $name
模块 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @NgModule ({ declarations: [ 组件, 指令, 管道 ], imports: [ 依赖模块 ], providers: [ 服务 ], bootstrap: [ 主组件 ] })
组件
component
路由
route
app-routing.module.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 const routes: Routes = [ { path: '' , redirectTo: '/home' , pathMath: 'full' , }, { path: 'home' , component: HomeComponent, data: [ { isProd: true , }, ], }, { path: 'children' , children: [ { path: '' , component: Children1Component, }, { path: 'xxx/:id' , component: Children2Component, }, ], }, { path: '**' , redirectTo: '' , }, ];
html 中 1 2 3 4 5 <a [routerLink ]="['/product', id]" [queryParams ]="{debug: 1}" > XXXX</a > <router-outlet > </router-outlet >
component.ts 跳转 1 2 3 4 5 6 7 constructor (private router: Router ){}this .router.navigate(["/" , id], { queryParams: { debug: 1 } })
获取 1 2 3 4 5 6 7 8 9 constructor (private routeInfo: ActivatedRoute ) { routeInfo.params.subscribe((params: Params ) => this .productId = params["id" ]); routeInfo.queryParams.subscribe((params: Params ) => this .test = params["test" ]); this .id = routeInfo.snapshot.params['id' ]; this .isProd = routeInfo.snapshot.data[0 ]['isProd' ]; }
辅助路由
aux
路由守卫 routes 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { path: 'product/:id' , component: ProductComponent, resolve: { product: ProductResolveGuard }, canActivate: [LoginGuard], canDeactivate: [UnsavedGuard], data: [{ isProd: true }], }
canActivate 1 2 3 4 5 6 7 8 9 10 11 12 export class LoginGuard implements CanActivate { canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable<boolean > | Promise <boolean > | boolean { let loggedIn: boolean = Math .random() < 0.5 ; if (!loggedIn) { console .log('LoginGuard:用户未登录' + new Date ()); } return loggedIn; } }
CanDeactivate 1 2 3 4 5 export class UnsavedGuard implements CanDeactivate<ProductComponent> { canDeactivate() { return window .confirm('你还没有保存.确定要离开么?' ); } }
Resolve 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Injectable ()export class ProductResolveGuard implements Resolve<ProductComponent> { constructor (private router: Router ) {} resolve( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable<ProductComponent> | Promise <ProductComponent> | ProductComponent { let productId: number = route.params['id' ]; if (productId == 1 ) { return undefined ; } else { this .router.navigate(['/home' ]); return undefined ; } } }
依赖注入
DI
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 providers: [ LoggerService, { provide: ProductService, useFactory: (logger: LoggerService, appConfig ) => { if (appConfig.isDev) { return new ProductService(logger); } else { return new AnotherProductService(logger); } }, deps: [LoggerService, 'APP_CONFIG' ], }, { provide: 'APP_CONFIG' , useValue: { isDev: false , }, }, ];
数据绑定 插值表达式
属性表达式
事件绑定
bind
响应式编程 RxJS 学习笔记
管道 1 2 3 4 5 6 7 8 9 10 11 @Pipe ({ name: 'multiple' , }) export class MultiplePipe implements PipeTransform { transform(value: number , args?: number ): any { if (!args) { args = 1 ; } return value * args; } }
组件间通信 路由属性 获取路由属性
输入属性 1 2 3 @Input ()price: number ;
1 2 <child-component [price ]="some-value" > </child-componen >
输出属性 1 2 3 4 5 6 @Output ('priceChange' )lastPrice:EventEmitter<number > = new EventEmitter(); this .lastPrice.emit(10 );
1 2 <child-component (lastPrice )="priceHandle($event)" > </child-component >
1 2 3 4 priceHandle(event: number ) { console .log('price: ' , event) }
中间人模式(有共同的父组件) 1 2 3 4 5 @Output ('priceChange' )lastPrice:EventEmitter<number > = new EventEmitter(); this .lastPrice.emit(10 );
1 2 3 <child1-component (lastPrice )="priceHandle($event)" > </child1-component > <child2-component [lastPrice ]="lastPrice" > </child2-component >
组件生命周期
life
表单 数据模型 由 angular/form
模块中特定的类(FormControl
, FormGroup
, FormArray
)组成
两种表单比较
模板式表单
响应式表单
import
FormsModule
ReactiveFormsModule
如何构造
通过组件模板中相关指令
通过编写 typescript
代码
数据模型创建
由 angular
基于模板中的指令隐式创建
编码明确创建数据模型
能否直接访问数据模型
不能
能
HTML
直接生成
自己编写绑定数据
模板式表单
NgFrom
会自动添加到 <form>
表单上
手动添加 NgFrom
: <form #myForm="ngForm">
<form>
表单的提交不会被触发
实现 (ngSubmit)="onSubmit(myForm.value)"
在 NgForm
元素下 寻找标记为 NgModel
属性的元素(需要指定name
属性才能绑定)
响应式表单
formGroup
和 formControl
需要属性绑定语法
formGroupName
, formControlName
, formArrayName
不需要 属性绑定语法
formGroupName
, formControlName
, formArrayName
只能用在 formGroup
指令覆盖的范围内
指令对照表
类名
模板式表单指令
响应式表单指令
FormGroup
ngFrom ngModelGroup
formGroup formGroupName
FormControl
ngModel
formControl formControlName
FormArray
formArrayName
校验
1 2 3 4 5 6 function mobileValidator (control: FormControl ): any { let value = (control.value || '' ) + '' ; var mobileReg = /^(((13[0-9]{1})|(15[0-9]{1})|(18[0-9]{1}))+\d{8})$/ ; let valid = mobileReg.test(value); return valid ? null : { mobile: true }; }
1 2 3 4 5 6 7 8 9 10 11 function equalValidator (group: FormGroup ): any { let password: FormControl = group.get('password' ) as FormControl; let pconfirm: FormControl = group.get('pconfirm' ) as FormControl; let valid: boolean = false ; if (password && pconfirm) { valid = password.value === pconfirm.value; } return valid ? null : { equal: { description: '密码和确认密码不匹配!' } }; }
1 2 3 function mobileAsyncValidator (control: FormControl ): any { return Observable.of(valid ? null : { mobile: true }).delay(5000 ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 constructor (fb: FormBuilder ) { this .formModel = fb.group({ mobile: ['' , mobileValidator, mobileAsyncValidator], username: ['' , [Validators.required, Validators.minLength(5 )]], passwordsGroup: fb.group( { password: ['' , Validators.minLength(6 )], pconfirm: ['' ], }, { validator: equalValidator } ), }) } onSubmit() { let isValid: boolean = this .formModel.get('username' ).valid; let errors: any = this .formModel.get('username' ).errors; if (this .formModel.valid) { console .log(this .formModel.value); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <form [formGroup ]="formModel" (submit )="onSubmit()" > <div [hidden ]="formModel.get('username').valid || formModel.get('username').untouched" > <div [hidden ]="!formModel.hasError('required', 'username')" > 用户名是必填项 </div > <div [hidden ]="!formModel.hasError('minlength', 'username')" > 用户名最小长度是5 </div > </div > <div > 手机号:<input type ="number" formControlName ="mobile" > </div > <div [hidden ]="!formModel.get('mobile').pending" > 正在校验手机号合法性 </div > <div [hidden ]="formModel.get('mobile').valid || formModel.get('mobile').pristine" > <div [hidden ]="!formModel.hasError('mobile', 'mobile')" > 请输入正确的手机号 </div > </div > <div formGroupName ="passwordsGroup" > <div > 密码:<input type ="password" formControlName ="password" > </div > <div [hidden ]="!formModel.hasError('minlength', ['passwordsGroup','password'])" > 密码最小长度是6 </div > <div > 确认密码:<input type ="password" formControlName ="pconfirm" > </div > <div [hidden ]="!formModel.hasError('equal', 'passwordsGroup')" > {{formModel.getError('equal', 'passwordsGroup')?.description}} </div > </div > </form >
1 2 3 4 5 6 7 8 9 10 @Directive ({ selector: '[mobile]' , providers: [{ provide: NG_VALIDATORS, useValue: mobileValidator, multi: true }], }) export class MobileValidatorDirective { constructor ( ) {} }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 constructor ( ) { }ngOnInit() {} onSubmit(value:any , valid:boolean ){ console .log(valid); console .log(value); } usernameValid:boolean = true ; usernameUntouched:boolean = true ; onUsernameInput(form:NgForm) { if (form) { this .usernameValid = form.form.get("username" ).valid; this .usernameUntouched = form.form.get("username" ).untouched; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <form #myForm ="ngForm" (ngSubmit )="onSubmit(myForm.value, myForm.valid)" novalidate > <div > 用户名:<input ngModel required minlength ="6" name ="username" type ="text" (input )="onUsernameInput(myForm)" > </div > <div [hidden ]="usernameValid || usernameUntouched" > <div [hidden ]="!myForm.form.hasError('required','username')" > 用户名是必填项 </div > <div [hidden ]="!myForm.form.hasError('minlength','username')" > 用户名最小长度是6 </div > </div > </form >
和服务器通讯 HttpClient
build
添加环境配置(如 staging
) 编辑 angular.json
, 仿照 projects.architect.build.configurations.staging
下添加环境变量
build
的时候加入 --configuration
1 2 ng build --prod --configuration=${env} --output-path dist