目录
表单
响应式表单,更健壮,可以监控表单控件中的运行中的变化,扩展性和复用性更强。
模板表单,使用简单,我们更多的关注点在自己逻辑上的管理,不易扩展。
响应式 | 模板驱动 | |
---|---|---|
建立(表单模式) | 显式,在组件类中创建。 | 隐式,由组件创建。 |
数据模式 | 结构化 | 非结构化 |
可预测性 | 同步 | 异步 |
表单验证 | 函数 | 指令 |
可变性 | 不可变 | 可变 |
可伸缩性 | 访问底层 API | 在 API 之上的抽象 |
响应式表单和模板驱动表单共享了一些底层构造块。
1. FormControl
实例用于追踪单个表单控件的值和验证状态。
2. FormGroup
用于追踪一个表单控件组的值和状态。
3. FormArray
用于追踪表单控件数组的值和状态。
4. ControlValueAccessor
用于在 Angular 的 FormControl
实例和原生 DOM 元素之间创建一个桥梁。
两种表单最本质的区别是在执行过程中,模板表单需要ControlValueAccessor作为数据的转化桥梁,而响应表单不需要,需要自己去跟踪这些变化,无论是从表现到模型还是模型到变现,那么ControlValueAccessor是如何转化跟踪的那?在观察到数据发声变化的时候ControlValueAccessor会调用NgModel.viewToModelUpate()方法发出一个ngModelChange事件,该事件会进行修改数据。
响应表单应用
1. 导入ReactiveFormsModule
2. 创建FormControl实例, xxx = new FormControl('')
3. 将创建的实例注册到具体控件,<input type="text" [formControl]="xxx"/>
4. 修改数据 xxx.setValue(new xxx)
上面是单个fromControl实例,我们可以用formGroup来跟踪一组实例。
1. 创建FormGroup实例, xxxGroup = new FromGroup({属性1: fromControl1, 属性2: fromControl2})
2. 将实例注册到表单
<form [formGroup]="xxxGroup" (ngSubmit)="onSubmit()">
<input type="text" formControlName="属性1">
<input type="text" formControlName="属性2">
</form>
对于复杂的表单,我们可以用FormGroup嵌套的方式来管理,这样能使控件更原子化。这样对于只修改部分数据也是支持的,可以使用patchValue()方法。手动构建这个结构比较麻烦的话,可以使用FormBuilder模块来简化构建过程。
自定义验证器
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} | null => {
const forbidden = nameRe.test(control.value);
return forbidden ? {'forbiddenName': {value: control.value}} : null;
};
}
模板表单应用
模板表单比较常用,基于数据绑定不用关注模板实例跟踪,基础上的应用不再阐述。
关于验证器,因为模板表单不能访问模板实例,所以自定义的验证器不能像响应表单一样传入进去,而是应该用扩展指令如下
@Directive({
selector: '[appForbiddenName]',
providers: [{provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true}]
})
export class ForbiddenValidatorDirective implements Validator {
@Input('appForbiddenName') forbiddenName: string;
validate(control: AbstractControl): {[key: string]: any} | null {
return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control)
: null;
}
}
---------------------------------------------------------------------
<input id="name" name="name" class="form-control"
required minlength="4" appForbiddenName="bob"
[(ngModel)]="xxx.name" #name="ngModel" >
对于异步验证可以实现AsyncValidator接口,异步验证更多用于与后台的交互场景,针对每个控件都进行验证会增加后台压力,我们可以推迟验证时机[ngModelOptions]="{updateOn:'blur/submit'}"。
可观察者对象
说明
可观察者(Observable)在Angular中被大量应用,那么什么是可观察者那?其实本质就是订阅发布模式,发布者管理主题,同时作为数据更新后的发布出口,观察者订阅主题,主题是一个事件/委托,观察者是具体的执行函数,通过事件+=赋值把不同的观察者挂载到具有相同特性的主题上,当发布者检测到数据更新后,传值给主题,主题调用具体观察者(执行函数)来进行数据更新。以上就是订阅发布的基本流程,在Angular中也是采用这个流程,不过功能提供的很全更有引用Rxjs库,关于Rxjs库在下一张详细阐明。通过一个实例来看看订阅发布是如何在变种为可观察对象的。
// new Observable初始化一个可观察者对象,其实就是一个发布者, 传入的参数是订阅器
const sequence = new Observable(subscriber);
// subscriber是订阅器,作用类似于事件被挂载(+=)后,当检测到数据变化了,真正去调用事件的过程
function subscriber(observer) {
observer.next(1);
observer.complete();
return {unsubscribe() {}};
}
// 观察者订阅,subscribe方法内的参数对象其实就是观察者,也就是真正需要被执行的函数部分,我们应用作用最关心的部分
sequence.subscribe({
next(num) { console.log(num); },
complete() { console.log('Finished sequence'); }
});
单播与多播
单播与多播,从字面上来理解就是一个发布者发送消息给一个订阅者=>单播,一个发布者发送给多个订阅者=>多播,这个理解没问题,不过这里有一个小细节,一个订阅挂载到一个发布这相当于一条流的管道,如果通过多个订阅者挂载到多个发布者虽然也能实现多播的效果但是这不是多播,这是多个一对一,是多条一对一的流的管道而已,真正的多播应该是这样在观察者内用数组/链表记录下注册的订阅者,每次发送消息的时候是遍历数组中的订阅者发送通知,也就是在一个管道内给多端发送消息。
Angular中内置的可观察者对象如下
1. EventEmitter,EventEmitter扩展了Observable,添加了一个emit方法,当调用emit方法时会发数据发送到观察者的处理函数上。
2. Http,通过HttpClient模块调用后台请求时会返回可观察者对象,是基于Promise的Http Api。可以调用unsubscribe取消,也可获取进度和重试。
3. Async管道
4. 路由器(router),router.event.pipe,activatedRouter.url
5. 响应表单,valueChanges, statusChanges
可观察者与其他技术对比
1. 与Promise对比
操作 | 可观察对象 | 承诺 |
---|---|---|
创建 | | |
转换 | | |
订阅 | | |
取消订阅 | | 承诺被解析时隐式完成。 |
2. 与事件Api对比
可观察对象 Observable | 事件 API | |
---|---|---|
创建与取消 | | |
订阅 | | |
配置 | 监听按键,提供一个流来表示这些输入的值。 | 不支持配置。 |