我们可能遇到下面这种情形:
当前我们要编写一个auto-complete
组件,当用户输入的内容在数据库中有记录的话就按照记录保存,如果没有记录的话就先把用户输入的内容作为实体存在数据库中再进行保存。但是我们肯定不能只要用户输入的内容在数据库中没有找到就即使进行存储,如果这样的话只要用户改变一次输入框中的内容就会进行一次存储,显然这是不合理的。
所以我们要做的是——当用户已经确认输入完了所有内容点击了保存之后再进行上述操作。
那么问题就来了——如何在子组件中得知父组件点击了保存按钮呢?
前情提要:
我们规定了当点击保存按钮时会执行C层的onSubmit
方法来提交表单。
<form [formGroup]="formGroup" (ngSubmit)="onSubmit(formGroup)">
<app-vehicle-brand-auto-complete [formControlName]="formKeys.vehicleBrand" ></app-vehicle-brand-auto-complete> //这是我们要调用的子组件
<div>
<div>
<button [disabled]="formGroup.invalid" type="submit">保存
</button>
</div>
</div>
</form>
所以我们要做的就是在子组件中得知父组件中onSubmit
方法的执行情况。
起初我想的是angular会不会内置了这种方法,我们只需要把父组件的onSubmit
传过来就可以得知它的调用时间,但是经过测试并没有发现类似的方法。
要想得知执行情况首先我们要在子组件中用@Input注解声明一个observable类型的变量。
@Input()
doSubmit: Observable<void>
然后我们再在父组件中这样进行声明
/**
* 用于向子组件弹值,让子组件进行性判定
*/
doSubmitSubject = new Subject<void>();
/**
* 用于传递给子组件并让子组件进行订阅
*/
doSubmit = this.doSubmitSubject.asObservable();
此时doSubmitSubject
就有了向自组件弹射信息的功能,doSubmit
用来传递给子组件用来进行订阅。
我们如果想要实现在用户点击保存时执行子组件相应方法的话只需要在onSubmit方法中调用doSubmitSubject.next()进行传值即可。
onSubmit(formGroup: FormGroup) {
this.doSubmitSubject.next(null);
. . .
}
之后我们再在子组件中对传过来的doSubmit进行订阅即可
ngOnInit(): void {
this.doSubmit.subscribe(
//我们想要进行的操作
)
. . .
}
了解完方法之后我们还需要知道Subject
是什么,为什么靠它就可以实现上述功能。
export class Subject<T> extends Observable<T> implements SubscriptionLike {
. . .
}
查看其代码后我们发现Subject继承自Observable。
我们查看其中的asObservable方法:
asObservable(): Observable<T> {
const observable = new Observable<T>();
(<any>observable).source = this;
return observable;
}
官方给出的注释如下:
以该主体为源创建一个新的可观察对象。
您可以这样做,以创建对象的自定义观察者端逻辑,并对使用可观察对象的代码隐藏它。
返回值:
Observable 由Subject投射出的Observable
其中说明了我们可以自定观察者端逻辑,也就是说调用Subject.next()
就相当于我们日常使用的给Observable传值,从而触发子组件的订阅,进行相应操作。
但是经过以上操作后我们会发现由于操作都是异步进行的,这就导致了父组件会比子组件先完成存储操作,也就是说我们子组件相应的功能还没执行完,还没有把完整的实体传给父组件,父组件就完成了save操作。
那么这时我们就还需要用@OutPut字段新增一个beFinishi字段,告诉父组件我们已经执行完了,父组件接收到消息后再存储。
子组件:
@Output()
beFinish = new EventEmitter<void>();
ngOnInit(): void {
this.doSubmit.subscribe(() => {
xxxService
.subscribe(() => {
. . .
this.beFinish.emit(null);
})
})
父组件(C层):
finish = false;
onFinish(formGroup: FormGroup) {
this.finish = true;
this.onSubmit(formGroup);
}
onSubmit(formGroup: FormGroup) {
//若未完成=》向子组件弹值,说明已经输入完所有字段,执行了onSubmit方法
//若已完成=》调用M层进行相应操作
if(this.finish === false) {
this.doSubmitSubject.next(null);
} else {
const newVehicleBrand = new VehicleBrand({
id: formGroup.get(this.formKeys.vehicleBrand).value.id as number,
name: formGroup.get(this.formKeys.vehicleBrand).value.name as string
});
//在if语句中调用相应service进行存储操作
console.log(newVehicleBrand);
}
}
父组件V层
<app-vehicle-brand-auto-complete [formControlName]="formKeys.vehicleBrand" [doSubmit]="doSubmit" (beFinish)="onFinish(formGroup)"></app-vehicle-brand-auto-complete>
到此整个流程便结束了,为了便于理解有以下流程图:
很多时候先画一个逻辑正确的流程图是很有必要的,这样可以很好的防止我们写的代码进入死循环。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。