Ant Design of Angular
目录
- 1 常见引用
- 2 cli
- 3 事件
- 4 生命周期
- 5 管道
- 6 接口
- 7 指令
- 8 ngif
- 9 ng-for
- 10 .map
- 11 数组操作
- 12 ngSwitchCase
- 13 按钮禁用
- 14 事件绑定
- 15 模板引用
- 16 注入服务
- 17 ng-content 指定子内容输出
- 18 ng-template ng-container
- 19 Input 装饰器
- 20 使用 Output 装饰器
- 21 自定义双向绑定
- 22 @ViewChild @ContentChild @ViewChildren @ContentChildren
- 23 平行组件间传递
- 24 子组件中使用@Inject调用父组件中的变量和方法
- 25 ngClass
- 26 ngStyle
- 27 @ts-ignore
- 28 offsetHeight
- 29 NgNonBindable
- 30 viewContainerRef.createComponent const childInjector = Injector.create技巧
- 31 获取页面上的元素
- 32 应该如何引入第三方 UI 库,如 bootstrap
- 33 数据绑定
- 34 下载
- 35 表单
- 36 路由
- 37 安装
- 38 调试
- 39 Angular官方教程
- 40 cli
- 41 问题解决
常见引用
antd 官方doc Angular 4.x 修仙之路 Angular CLI 终极指南 Angular 4 指令快速入门 ng-alain alain.git delon TypeScript 高级类型整理 TypeScript 类型兼容性整理 TypeScript 枚举使用整理 antd-mobile
cli
参考以下顺序有助于组织结构
ng generate interface base/models/ResposeInfo ng generate enum base/models/ResultStatus ng generate enum base/models/FailStatus
ng generate module builder ng generate module builder/programe
ng generate component builder/programe <router-outlet></router-outlet>
ng generate component builder/programe/views/programe-index ng generate component builder/programe/components/programe-filter ng generate component builder/programe/components/programe-grid ng generate component builder/programe/components/programe-form ng generate component builder/programe/components/programe-select
ng generate service builder/programe/services/programe
class component directive enum guard interface module pipe service
ng generate module builder/project ng generate module builder/project/systemconfig ng generate component builder/project/systemconfig/views/initconfig
事件
<input #myInput type="text"> <button (click)="onClick($event, myInput.value)">点击</button>
鼠标
<input #myInput type="text"> <button (click)="onClick($event, myInput.value)">点击</button>
键盘
<input #myInput type="text" (keydown.enter)="onEnter($event, myInput.value)"> <button (click)="onClick($event, myInput.value)">点击</button>
生命周期
https://segmentfault.com/a/1190000008716308
管道
{{address | json}}
接口
interface Address {
province: string;
city: string;
}
export class UserComponent {
name: string;
address: Address;
// ...
}
指令
- 使用 HostBinding 装饰器,实现元素的属性绑定
- 利用 Input 装饰器,定义指令的输入属性
- 通过 Attribute 装饰器来获取指令宿主元素的属性值
定义
import { Directive, HostBinding, HostListener, Input, Attribute } from '@angular/core';
@Directive({
selector: '[greet]'
})
export class GreetDirective {
@Input() greet: string;
@HostBinding() get innerText() {
return this.greet;
}
@HostListener('click',['$event'])
onClick(event) {
this.greet = 'Clicked!';
console.dir(event);
}
constructor(@Attribute('author') public author: string) {
console.log(author);
}
}
使用
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h2>Hello, Angular</h2>
<h2 [greet]="'Hello, Semlinker!'"
author="semlinker">Hello, Angular</h2>
`,
})
export class AppComponent { }
- ngif相反指令
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[exeUnless]'
})
export class UnlessDirective {
@Input('exeUnless')
set condition(newCondition: boolean) {
if (!newCondition) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
constructor(private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef) {
}
}
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h2 *exeUnless="condition">Hello, Semlinker!</h2>
`,
})
export class AppComponent {
condition: boolean = false;
}
ngif
<div *ngIf="condition">...</div>
ng-for
我们使用 let item of items; 语法迭代数组中的每一项,另外我们使用 index as i 用来访问数组中每一项的索引值。除了 index 外,我们还可以获取以下的值:
<ul>
<li *ngFor="let message of mailService.messages; index as i;">
{{i}} - {{message}}
</li>
</ul>
- first: boolean - 若当前项是可迭代对象的第一项,则返回 true
- last: boolean - 若当前项是可迭代对象的最后一项,则返回 true
- even: boolean - 若当前项的索引值是偶数,则返回 true
- odd: boolean - 若当前项的索引值是奇数,则返回 true
.map
data.map((i: STData, index: number) => {
i.disabled = index === 0;
return i;
});
Object.keys(obj).forEach(function(key,i,v){
console.log(key)
console.log("---------")
console.log(i);
console.log("---------")
console.log(v)
})
数组操作
ngSwitchCase
- ngSwitchCase。
按钮禁用
[nzLoading]="loading"
事件绑定
事件绑定语法
<date-picker (dateChanged)="statement()"></date-picker>
模板引用
<input #myInput type="text"> <button (click)="onClick(myInput.value)">点击</button>
注入服务
对于 Type 类型(函数类型) 的对象,我们一般使用 constructor(private mailService: MailService) 方式进行注入。而 Inject 装饰器一般用来注入非 Type 类型的对象。
constructor(
@Inject(MailService) private mailService,
@Inject('apiUrl') private apiUrl
) {}
}
ng-content 指定子内容输出
import {
Component, HostBinding,
} from '@angular/core';
@Component({
selector : '[nz-menu-group]',
template : `
<div class="ant-menu-item-group-title">
<ng-content select="[title]"></ng-content>
</div>
<ul class="ant-menu-item-group-list">
<ng-content></ng-content>
</ul>`,
})
export class NzMenuGroupComponent {
@HostBinding('class.ant-menu-item-group') _nzMenuItemGroup = true;
}
使用
<li nz-menu-group>
<span title>Item 1</span>
<ul>
<li nz-menu-item>Option 1</li>
<li nz-menu-item>Option 2</li>
</ul>
</li>
ng-template ng-container
- 在 Angular 中,我们可以通过 ViewChild 装饰器来获取视图中定义的模板元素,然后利用 ViewContainerRef 对象的 createEmbeddedView() 方法,创建内嵌视图
- ngTemplateOutlet 该指令用于基于已有的 TemplateRef 对象,插入对应的内嵌视图。在应用 NgTemplateOutlet 指令时,我们可以通过 [ngTemplateOutletContext] 属性来设置 EmbeddedViewRef 的上下文对象。绑定的上下文应该是一个对象,此外可通过 let 语法来声明绑定上下文对象属性名。
<ng-container *ngTemplateOutlet="templateRefExp; context: contextExp"></ng-container>
- ngOutletContext
- TemplateRef
TemplateRef 实例用于表示模板对象。用于表示内嵌的 template 模板元素,通过 TemplateRef 实例,我们可以方便创建内嵌视图(Embedded Views),且可以轻松地访问到通过 ElementRef 封装后的 nativeElement。需要注意的是组件视图中的 template 模板元素,经过渲染后会被替换成 comment 元素。
- ViewContainerRef
ViewContainerRef 实例提供了 createEmbeddedView() 方法,该方法接收 TemplateRef 对象作为参数,并将模板中的内容作为容器 (comment 元素) 的兄弟元素,插入到页面中。 ViewContainerRef:用于表示一个视图容器,可添加一个或多个视图。通ViewContainerRef 实例,我们可以基于 TemplateRef 实例创建内嵌视图,并能指定内嵌视图的插入位置,也可以方便对视图容器中已有的视图进行管理。简而言之,ViewContainerRef 的主要作用是创建和管理内嵌视图或组件视图。
- <ng-template> 用于定义模板,使用 * 语法糖的结构指令,最终都会转换为 <ng-template> 模板指令,模板内的内容如果不进行处理,是不会在页面中显示。
- <ng-container> 是一个逻辑容器,可用于对节点进行分组,但不作为 DOM 树中的节点,它将被渲染为 HTML中的 comment 元素,它可用于避免添加额外的元素来使用结构指令
- 另外需要注意的是使用 let 语法创建模板局部变量,若未设置绑定的值,则默认是上下文对象中 $implicit 属性对应的值。为什么属性名是 $implicit 呢?因为 Angular 不知道用户会如何命名,所以定义了一个默认的属性名。 即 let-name="$implicit" 与 let-name 是等价的。
import { Component, TemplateRef, ViewContainerRef, ViewChild,
AfterViewInit } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<ng-template #tpl>
Hello, Semlinker!
</ng-template>
`,
})
export class AppComponent implements AfterViewInit{
@ViewChild('tpl')
tplRef: TemplateRef<any>;
constructor(private vcRef: ViewContainerRef) {}
ngAfterViewInit() {
this.vcRef.createEmbeddedView(this.tplRef);
}
}
@Component({
selector: 'ng-template-outlet-example',
template: `
<ng-container *ngTemplateOutlet="greet"></ng-container>
<hr>
<ng-container *ngTemplateOutlet="eng; context: myContext"></ng-container>
<hr>
<ng-container *ngTemplateOutlet="svk; context: myContext"></ng-container>
<hr>
<ng-template #greet><span>Hello</span></ng-template>
<ng-template #eng let-name><span>Hello {{name}}!</span></ng-template>
<ng-template #svk let-person="localSk"><span>Ahoj {{person}}!</span></ng-template>
`
})
class NgTemplateOutletExample {
myContext = {$implicit: 'World', localSk: 'Svet'};
}
Input 装饰器
定义
export class SimpleFormComponent implements OnInit {
//@Input('message') msg: string;
@Input() message: string;
/** async data */
@Input()
get nzAjaxData() {
return this.data;
}
set nzAjaxData(data) {
this._isAjax = true;
this.data = data;
}
// ...
}
传值
template: `
<h3>{{title}}</h3>
<app-simple-form *ngFor="let message of mailService.messages;"
[message]="message">
</app-simple-form>
`
使用 Output 装饰器
- Output 装饰器的作用是用来实现子组件将信息,通过事件的形式通知到父级组件。
- 只能通过事件形式,将内组件的值传给外组件 ,void类型,不能有返回值
- 内组件update.emit({text: message})-->外组件onUpdate(message.id, $event.text)
在介绍 Output 属性装饰器前,我们先来介绍一下 EventEmitter 这个幕后英雄:
let numberEmitter: EventEmitter<number> = new EventEmitter<number>(); numberEmitter.subscribe((value: number) => console.log(value)); numberEmitter.emit(10);
定义
import {Component, OnInit, Input, Output, EventEmitter} from '@angular/core';
@Component({
selector: 'app-simple-form',
template: `
<div>
{{message}}
<input #myInput type="text" [(ngModel)]="message">
<button (click)="update.emit({text: message})">更新</button>
</div>
`,
styles: []
})
export class SimpleFormComponent implements OnInit {
@Input() message: string;
@Output() update = new EventEmitter<{text: string}>();
ngOnInit() { }
}
使用
@Component({
selector: 'app-root',
template: `
<h3>{{title}}</h3>
<ul>
<li *ngFor="let message of mailService.messages;">
{{message.text}}
</li>
</ul>
<app-simple-form *ngFor="let message of mailService.messages;"
[message]="message.text"
(update)="onUpdate(message.id, $event.text)">
</app-simple-form>
`
})
自定义双向绑定
父组件和子组件引用同一个对象.达到双向绑定目的
- dict-select
- 子组件定义一个变量childata
- 使用@input定义get data(){return childata} set(data){this.childdata=data}
- 父组件使用 [(data)]=xxx 做双向绑定
定义组件
<label for="">UserName:</label> <input type="text" [(ngModel)]="userName" (ngModelChange)="change()">
视图层只有一个label和一个input标签,这是一个最简单的表单。
其中ngModelChange是当input发生变化时,触发的事件。
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
@Component({
// tslint:disable-next-line:component-selector
selector: 'binding-test',
templateUrl: './binding-test.component.html',
styleUrls: ['./binding-test.component.css']
})
/**
* 自定义组件双向数据绑定
*/
export class BindingTestComponent implements OnInit {
@Input() public userName;
@Output() public userNameChange = new EventEmitter();
constructor() { }
ngOnInit() {
}
/**
* change
*/
public change(userName: string) {
this.userNameChange.emit(this.userName);
}
}
@Input() public userName; @Output() public userNameChange = new EventEmitter(); 注意:Output中EventEmitter类型属性的名字必须为 Input属性对应名字+Change
使用
<binding-test [(userName)]="testBinding"></binding-test>
app.component:模板:TestBinding
其中binding-test标签是自定义组件,利用[()]符号进行双向绑定,下面p标签显示所绑定的值。
@ViewChild @ContentChild @ViewChildren @ContentChildren
- @ViewChild @ContentChild @ViewChildren @ContentChildren 又是什么
- @ViewChild 选择组件模板内的节点
- @ContentChild 选择当前组件引用的子组件 @ContentChild(组件名)
- @ViewChildren 和 @ContentChildren 则为对应的复数
import { Component, ContentChild, AfterContentInit } from '@angular/core';
import { ChildComponent } from './child.component';
@Component({
selector: 'exe-parent',
template: `
<p>Parent Component</p>
<ng-content></ng-content>
`
})
export class ParentComponent implements AfterContentInit {
@ContentChild(ChildComponent)
childCmp: ChildComponent;
ngAfterContentInit() {
console.dir(this.childCmp);
}
}
import { Component } from '@angular/core';
@Component({
selector: 'exe-child',
template: `
<p>Child Component</p>
`
})
export class ChildComponent {
name: string = 'child-component';
}
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<h4>Welcome to Angular World</h4>
<exe-parent>
<exe-child></exe-child>
</exe-parent>
`,
})
export class AppComponent { }
ContentChild 与 ViewChild 的异同点
相同点
都是属性装饰器
都有对应的复数形式装饰器:ContentChildren、ViewChildren
都支持 Type<any>|Function|string 类型的选择器
不同点
ContentChild 用来从通过 Content Projection 方式 (ng-content) 设置的视图中获取匹配的元素
ViewChild 用来从模板视图中获取匹配的元素
在父组件的 ngAfterContentInit 生命周期钩子中才能成功获取通过 ContentChild 查询的元素
在父组件的 ngAfterViewInit 生命周期钩子中才能成功获取通过 ViewChild 查询的元素
平行组件间传递
https://segmentfault.com/a/1190000008886598#articleHeader8 Subject 其实是观察者模式的实现,所以当观察者订阅 Subject 对象时,Subject 对象会把订阅者添加到观察者列表中,每当有 subject 对象接收到新值时,它就会遍历观察者列表,依次调用观察者内部的 next() 方法,把值一一送出。
Subject 之所以具有 Observable 中的所有方法,是因为 Subject 类继承了 Observable 类,在 Subject 类中有五个重要的方法:
- next - 每当 Subject 对象接收到新值的时候,next 方法会被调用
- error - 运行中出现异常,error 方法会被调用
- complete - Subject 订阅的 Observable 对象结束后,complete 方法会被调用
- subscribe - 添加观察者
- unsubscribe - 取消订阅 (设置终止标识符、清空观察者列表)
我们可以利用 RxJS Subject 来实现组件通信,具体示例如下:
- message.service.ts
import { Injectable } from '@angular/core';
import {Observable} from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class MessageService {
private subject = new Subject<any>();
sendMessage(message: string) {
this.subject.next({ text: message });
}
clearMessage() {
this.subject.next();
}
getMessage(): Observable<any> {
return this.subject.asObservable();
}
}
- home.component.ts
import { Component } from '@angular/core';
import { MessageService } from '../_services/index';
@Component({
moduleId: module.id,
templateUrl: 'home.component.html'
})
export class HomeComponent {
constructor(private messageService: MessageService) {}
sendMessage(): void { // 发送消息
this.messageService.sendMessage('Message from Home Component to App Component!');
}
clearMessage(): void { // 清除消息
this.messageService.clearMessage();
}
}
- app.component.ts
import { Component, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { MessageService } from './_services/index';
@Component({
moduleId: module.id,
selector: 'app',
templateUrl: 'app.component.html'
})
export class AppComponent implements OnDestroy {
message: any;
subscription: Subscription;
constructor(private messageService: MessageService) {
this.subscription = this.messageService.getMessage()
.subscribe(message => { this.message = message; });
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
- ProgrameService
@Injectable()
export class ProgrameService {
filterSubject = new Subject();
filterObservable = this.filterSubject.asObservable();
}
- ProgrameFilterComponent 推送事件,观察者执行
flushData() { // 推送数据更新 this.programeService.filterSubject.next(this.getParams()); }
- ProgrameGridComponent 注册为观察者
constructor(private programeService: ProgrameService) { super(); programeService.filterObservable.subscribe(params => { this.params = params; this.flushData(); }); }
子组件中使用@Inject调用父组件中的变量和方法
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'my-parent',
templateUrl: './parent.component.html',
styleUrls: [ './parent.component.css' ],
})
export class ParentComponent implements OnInit {
constructor() {}
ngOnInit(): void {
}
sayHello(){
console.log("Hello!")
}
}
import { Component, OnInit, Inject, forwardRef} from '@angular/core';
import { ParentComponent } from './parent.component';
@Component({
selector: 'my-child',
templateUrl: './child.component.html',
styleUrls: [ './child.component.css' ],
})
export class ChildComponent implements OnInit {
constructor(
@Inject(forwardRef(()=>ParentComponent)) public parent:ParentComponent
) {}
ngOnInit(): void {
this.parent.sayHello(); //"Hello!"
}
}
ngClass
<!-- 使用布尔值 -->
<div [ngClass]="{bordered: false}">This is never bordered</div>
<div [ngClass]="{bordered: true}">This is always bordered</div>
<!-- 使用组件实例的属性 -->
<div [ngClass]="{bordered: isBordered}">
Using object literal. Border {{ isBordered ? "ON" : "OFF" }}
</div>
<!-- 样式名包含'-' -->
<div[ngClass]="{'bordered-box': false}">
Class names contains dashes must use single quote
</div>
<!-- 使用样式列表 -->
<div class="base" [ngClass]="['blue', 'round']">
This will always have a blue background and round corners
</div>
ngStyle
<div [ngStyle]="{color: 'white', 'background-color': 'blue'}">
Uses fixed white text on blue background
</div>
需要注意的是, background-color 需要使用单引号,而 color 不需要。这其中的原因是,ng-style 要求的参数是一个 Javascript 对象,color 是一个有效的 key,而 background-color 不是一个有效的 key ,所以需要添加 对于一些场合,我们也可以直接利用 Angular 属性绑定的语法,来快速设置元素的样式。
设置元素的背景颜色
<div [style.background-color="'yellow'"]>
Use fixed yellow background
</div>
设置元素的字体大小
<!-- 支持单位: px | em | %-->
<div>
<span [ngStyle]="{color: 'red'}" [style.font-size.px]="fontSize">
Red Text
</span>
</div>
@ts-ignore
private notification: NzNotificationService,
private personcheckService: PersoncheckService,
private translate: TranslateService) {
personinfoService.filterObservable.subscribe(params => {
// console.log(params);
// @ts-ignore
let {apportionmentType}=params;
this.apportionmentType=apportionmentType;
});
}
offsetHeight
内容高度
this.viewHeight = this.elementView.nativeElement.offsetHeight;
NgNonBindable
ngNonBindable 指令用于告诉 Angular 编译器,无需编译页面中某个特定的HTML代码片段。
viewContainerRef.createComponent const childInjector = Injector.create技巧
定义未初始化值
picker: ComponentRef<DatePickerComponent>;
初始化触发
@HostListener('click')
togglePicker(): void {
if (!this.picker) {
this.showPicker();
} else {
this.hidePicker();
}
}
showPicker(): void {
.......
const options = new DatePickerOptions();
Object.assign(options, this._defaultOptions, {
hidePicker: (event): void => {
this.hidePicker();
}
});
const optionalParams: Array<keyof DatePickerDirective> = [
'mode',
'minDate',
'maxDate',
'value',
'mask',
'title',
'okText',
'dismissText',
'disabled',
'locale',
'appendToBody',
'onOk',
'onDismiss',
'onValueChange'
];
optionalParams.forEach(param => {
if (typeof this[param] !== 'undefined') {
(options as any)[param] = this[param];
}
});
const componentFactory: ComponentFactory<DatePickerComponent> = this._cfr.resolveComponentFactory(
DatePickerComponent
);
#初始化控件并传递值到构造函数
const childInjector = Injector.create([
{
provide: DatePickerOptions,
useValue: options
}
]);
this.picker = this._viewContainerRef.createComponent(
componentFactory,
this._viewContainerRef.length,
childInjector
);
......
}
DatePickerComponent的构造函数
constructor(
public elementRef: ElementRef,
public options: DatePickerOptions,
public localeProviderService: LocaleProviderService
) {
}
获取页面上的元素
@Component({
selector: 'my-comp',
template: `
<input type="text" />
<div> Some other content </div>
`
})
export class MyComp {
constructor(el: ElementRef) {
el.nativeElement.querySelector('input').focus();
}
}
推荐的方式
@Component({
selector: 'my-comp',
template: `
<input #myInput type="text" />
<div> Some other content </div>
`
})
export class MyComp implements AfterViewInit {
@ViewChild('myInput') input: ElementRef;
constructor(private renderer: Renderer) {}
ngAfterViewInit() {
this.renderer.invokeElementMethod(
this.input.nativeElement, 'focus');
}
}
应该如何引入第三方 UI 库,如 bootstrap
若要引入第三方 UI 库,可以在 .angular-cli.json 文件中,配置对应的样式文件地址,具体如下:
{
"apps": {
"styles": [
"styles.css",
"../node_modules/bootstrap/dist/css/bootstrap.min.css"
]
}
}
数据绑定
模板:Title
双向
[(ngModel)]="message"
单项
[ngModel]="message"
下载
let url = environment.BASE_SERVER_URL + 'excelExport/export?processor=personInfoExcelExportProcessor'; window.open(url); URL.revokeObjectURL(url);
表单
Angular 中有两种表单:
Template-driven Forms (类似于 AngularJS 1.x 中的表单 )
Reactive Forms
- Template-driven 表单的特点
- 使用方便
- 适用于简单的场景
- 通过 [(ngModel)] 实现数据双向绑定
- 自动生成 Form Model (异步)
- 最小化组件类的代码
- 不易于单元测试
- Reactive 表单的特点
- 比较灵活
- 适用于复杂的场景
- 简化了HTML模板的代码,把验证逻辑抽离到组件类中
- 手动创建 Form Model (同步)
- 方便的跟踪表单控件值的变化
- 易于动态添加表单控件
- 易于单元测试
Template-driven Forms
- 在 Angular 表单中,我们通过 ngModel 指令来实现双向绑定。
- 目前 Angular 支持的内建 validators 如下:
- required - 设置表单控件值是非空的
- email - 设置表单控件值的格式是 email
- minlength - 设置表单控件值的最小长度
- maxlength - 设置表单控件值的最大长度
- pattern - 设置表单控件的值需匹配 pattern 对应的模式
- 在 Angular 中,我们可以通过 #userName="ngModel" 方式获取 ngModel 对象,然后通过 userName.valid 判断表单控件是否通过验证。
- 需要注意的是,在使用 <form> 标签后,我们的 username 输入框,必须添加 name 属性。
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<form #loginForm="ngForm" (ngSubmit)="onSubmit(loginForm.value)">
<input
type="text"
required
minlength="3"
name="username"
[(ngModel)]="username"
#userName="ngModel">
<hr>
{{userName.valid}}
<div *ngIf="userName.errors?.required">请您输入用户名</div>
<div *ngIf="userName.errors?.minlength">
用户名的长度必须大于 {{userName.errors?.minlength.requiredLength}},当前的长度为
{{userName.errors?.minlength.actualLength}}
</div>
<button type="submit">提交</button>
{{loginForm.value | json}}
</form>
`,
})
export class AppComponent {
username = 'semlinker';
onSubmit(value) {
console.dir(value);
}
}
ngModelGroup
ngModelGroup 指令是 Angular 表单中提供的另一特殊指令,可以对表单输入内容进行分组,方便我们在语义上区分不同性质的输入。例如联系人的信息包括姓名及住址,现在需对姓名和住址进行精细化信息收集,姓名可精细化成姓和名字,地址可精细化成城市、区、街等。
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<form #loginForm="ngForm" (ngSubmit)="onSubmit(loginForm.value)">
<fieldset ngModelGroup="user">
<input
type="text"
required
minlength="3"
name="username"
[(ngModel)]="username"
#userName="ngModel">
<hr>
<div *ngIf="userName.errors?.required">请您输入用户名</div>
<div *ngIf="userName.errors?.minlength">
用户名的长度必须大于 {{userName.errors?.minlength.requiredLength}},当前的长度为
{{userName.errors?.minlength.actualLength}}
</div>
<input type="password" ngModel name="password">
</fieldset>
<button type="submit">提交</button>
<hr>
{{loginForm.value | json}}
</form>
`,
})
export class AppComponent {
username = 'semlinker';
onSubmit(value) {
console.dir(value);
}
}
以上代码成功运行后,模板:LoginForm.value 的输出结果:
{ "user": { "username": "semlinker", "password": "123" } }
表单添加验证状态样式
在 Angular 表单中,若验证通过则会在表单控件上添加 ng-valid 类,若验证失败则会在表单控件上添加 ng-invalid 类。
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
styles: [`
input.ng-invalid {
border: 3px solid red;
}
input.ng-valid {
border: 3px solid green;
}
`
],
template: `
<form #loginForm="ngForm" (ngSubmit)="onSubmit(loginForm.value)">
<fieldset ngModelGroup="user">
<input
type="text"
required
minlength="3"
name="username"
[(ngModel)]="username"
#userName="ngModel">
<hr>
<div *ngIf="userName.errors?.required">请您输入用户名</div>
<div *ngIf="userName.errors?.minlength">
用户名的长度必须大于 {{userName.errors?.minlength.requiredLength}},当前的长度为
{{userName.errors?.minlength.actualLength}}
</div>
<input type="password" required ngModel name="password">
</fieldset>
<button type="submit">提交</button>
<hr>
{{loginForm.value | json}}
</form>
`,
})
export class AppComponent {
username = 'semlinker';
onSubmit(value) {
console.dir(value);
}
}
表单控件的状态
表单控件除了 valid 状态外,还包含哪些状态? 在 Angular 中表单控件有以下 6 种状态,我们可以通过 #userName="ngModel" 方式获取 ngModel 对象,进而获取控件的状态信息。具体状态如下:
- valid - 表单控件有效
- invalid - 表单控件无效
- pristine - 表单控件值未改变
- dirty - 表单控件值已改变
- touched - 表单控件已被访问过
- untouched - 表单控件未被访问过
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
styles: [`
input.ng-invalid {
border: 3px solid red;
}
input.ng-valid {
border: 3px solid green;
}
`
],
template: `
<form #loginForm="ngForm" (ngSubmit)="onSubmit(loginForm.value)">
<fieldset ngModelGroup="user">
<input
type="text"
required
minlength="3"
name="username"
[(ngModel)]="username"
#userName="ngModel">
<hr>
<p>Name控件的valid状态:{{userName.valid}} - 表示控件有效</p>
<p>Name控件的invalid状态:{{userName.invalid}} - 表示控件无效</p>
<p>Name控件的pristine状态:{{userName.pristine}} - 表示控件值未改变</p>
<p>Name控件的dirty状态:{{userName.dirty}} - 表示控件值已改变</p>
<p>Name控件的touched状态:{{userName.touched}} - 表示控件已被访问过</p>
<p>Name控件的untouched状态:{{userName.untouched}} - 表示控件未被访问过</p>
<div *ngIf="userName.errors?.required">请您输入用户名</div>
<div *ngIf="userName.errors?.minlength">
用户名的长度必须大于 {{userName.errors?.minlength.requiredLength}},当前的长度为
{{userName.errors?.minlength.actualLength}}
</div>
<input type="password" required ngModel name="password">
</fieldset>
<button type="submit">提交</button>
<hr>
{{loginForm.value | json}}
</form>
`,
})
export class AppComponent {
username = 'semlinker';
onSubmit(value) {
console.dir(value);
}
}
Reactive Form
要使用 Reactive Form 需要以下几个步骤:
- 导入 ReactiveFormsModule
import { ReactiveFormsModule } from "@angular/forms";
- 在 NgModule 的 imports 属性值对应的数组中,添加 ReactiveFormsModule
@NgModule({
imports: [
BrowserModule,
ReactiveFormsModule
],
})
export class AppModule { }
- 绑定 form 元素的 formGroup 属性
<form (ngSubmit)="save()" [formGroup]="signupForm"></form>
- 关联 input 元素对应的 FormControl 实例
<input type="text" name="userName" placeholder="请输入用户名" formControlName="userName">
- 使用 FormGroup
- 创建 FormGroup 属性
- 创建 FormGroup 实例
- 创建每个 FormControl 实例
signupForm: FormGroup;
this.signupForm = new FormGroup({
userName: new FormControl('', [Validators.required, Validators.minLength(3)]),
email: new FormControl('', [Validators.required, Validators.email]),
...
});
- 使用 FormBuilder
要使用 FormBuilder 需要以下几个步骤:
- 导入 FormBuilder
- 注入 FormBuilder 实例
- 使用 FormBuilder 实例
FormBuilder 创建 Form Control 语法
方式一
this.signupForm = this.fb.group({
userName: 'semlinker',
hasAddress: false
});
方式二
this.signupForm = this.fb.group({
userName: {value: 'semlinker', disabled: true}
hasAddress: {value: true, disabled: false}
});
方式三
this.signupForm = this.fb.group({
userName: [''],
hasAddress: [{value: true, disabled: false}]
})
路由
https://blog.csdn.net/weixin_42706227/article/details/81543694 (八)、Angular4.0 路由守卫
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-root',
template: `
<div class="app">
<h3>Our app</h3>
<router-outlet></router-outlet>
</div>
`
})
export class AppComponent implements OnInit {
constructor(private router: Router) {}
ngOnInit() {
setTimeout(() => {
this.router.navigate(['/settings']);
}, 5000);
}
}
constructor(
private router: Router,
private routerParams: ActivatedRoute
) { }
ngOnInit() {
this.routerParams.snapshot.paramMap.get('id') // 此时我可以通过这个快照拿到这个id,但是仅仅只能拿到一次
}
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.route.paramMap.subscribe((params: ParamMap)=>{
const id = params.get('id')
})
}
安装
cd xxx npm install -g @angular/cli@latest ng new PROJECT-NAME cd PROJECT_NAME cnpm install ng-zorro-antd --save
- 直接用下面的代码替换 /src/app/app.module.ts 的内容
注意:在根 module 中需要使用 NgZorroAntdModule.forRoot(),在子 module 需要使用 NgZorroAntdModule
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { NgZorroAntdModule } from 'ng-zorro-antd';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
BrowserAnimationsModule,
NgZorroAntdModule.forRoot()
],
bootstrap: [AppComponent]
})
export class AppModule { }
调试
ng serve --port 0 --open
如果需要实时调试AoT效果,请使用以下命令启动
ng serve --port 0 --open --aot
5. 构建和部署
ng build ng build --prod #会报错,不知道为啥
Angular官方教程
https://www.angular.cn/tutorial
cli
ng g component hero-detail ng g service Hero #不用加Service,会自动以Service结尾
问题解决
Cannot assign to a reference or variable
问题原因是,ngModel 会去查找 ts文件中的 this.item ,然而我们并没有在ts文件中定义 item。
换句话说,ngModel中传入的值必须是文件的本地的变量,临时的item不会被接受。
<nz-collapse-panel *ngFor="let item of childs; let i=index" nzHeader="子女{{childs[i].name}}"
[nzActive]="panelactive">
<personinfo-child-edu-form *ngIf="childs[i].editStatus !== 'DELETE'"
[(item)]="childs[i]"></personinfo-child-edu-form>
</nz-collapse-panel>