咱们可能遇到上面这种情景:
以后咱们要编写一个 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>
到此整个流程便完结了,为了便于了解有以下流程图:
很多时候先画一个逻辑正确的流程图是很有必要的,这样能够很好的避免咱们写的代码进入死循环。