使用Observables,在延迟后显示加载指示器,但如果加载及时完成,则取消



在我的客户详细信息组件中,我有以下代码可以实现我想要的,但不是以我认为可能的反应/可观察的方式。

有没有一种方法可以使用反应式编程技术来实现这一点,而不是将this.isLoading = true;封装在if语句中?如果首先检索到客户,也许可以通过取消/删除延迟的可观察项?或者,我是不是走错了路?

export class CustomerDetailComponent implements OnInit {
customer: Customer;
errorMessage: string;
isLoading: boolean;
constructor(
private customerService: CustomerService,
private route: ActivatedRoute,
private router: Router,
private location: Location
) { }
ngOnInit() { 
let idParam = this.route.params
.distinctUntilChanged(params => params['id']);
idParam.subscribe(params => 
{
this.errorMessage = ''; 
});
idParam.delay(300).subscribe(params => 
{
if (!(this.customer && this.customer.id == params['id']))
this.isLoading = true;
});
idParam.switchMap((params: Params) => this.customerService.getCustomer(params['id']))
.subscribe(customer => 
{ 
this.customer = customer; 
this.isLoading = false;
},
error => this.errorMessage = error);
}
}

您可以沿着以下几行写一些东西:

function getCustomer(id) {
return Observable.of({'name': 'John', id}).delay(500);
}
Observable.of({'id': 42})
.distinctUntilChanged(params => params['id'])
.do(() => {
// this.errorMessage = '';
})
.switchMap((params) => {
return Observable.combineLatest(
Observable.of(true).delay(300).startWith(null), // delay Observable
getCustomer(params['id']).startWith(null), // customer Observable
function(delay, customer) { // selector function
if (customer) {
return customer;
}
if (delay && !customer) {
console.log('this.isLoading = true;');
}
return null;
})
.filter(customer => customer)
.distinctUntilChanged(customer => customer['id']);
})
.subscribe(
customer => {
console.log('this.isLoading = false;');
console.log(customer);
// this.customer = customer;
},
error => {
// this.errorMessage = error;
}
);

观看现场演示:https://jsbin.com/nebutup/5/edit?js,控制台

内部combineLatest()接收两个Observable:

  1. 300ms延迟
  2. 来自远程服务的客户(在此模拟演示中)

然后还有一个投影函数,用于选择我们想要进一步传播的内容。两个Observable都使用.startWith(null)来确保它们至少有一个项目被发出,这样combineLatest()就会被其中任何一个项目的更改触发。然后我们可以很容易地知道发出的第一个Observable是延迟还是客户。

然后还有filter()来删除所有null值,distinctUntilChanged()来确保我们不会两次发出同一个客户(这可以处理客户首先完成的情况)。

然后,当我们运行这个演示并首先触发延迟时,输出如下:

this.isLoading = true;
this.isLoading = false;
{ name: 'John', id: 42 }

这意味着我们首先显示装载,然后隐藏它。

然后,当我们将getCustomer()更改为首先完成时:

function getCustomer(id) {
return Observable.of({'name': 'John', id}).delay(100);
}

我们将得到以下内容:

this.isLoading = false;
{ name: 'John', id: 42 }

这意味着我们从不显示任何负载。

这里有一个带有可重用运算符的rxjs 6管道方法:

export function delayIndicator<T>(delay: number, start: () => void, complete: () => void): OperatorFunction<T, T> {
const loadingShown$ = timer(delay).pipe(
tap(() => start()),
mapTo(true),
startWith(false)
);
return (input$) =>
combineLatest([input$, loadingShown$]).pipe(
take(1),
map(([input, delayShown]) => {
if (delayShown) {
complete();
}
return input;
})
);
}
myObservable$.pipe(delayIndicator(300, () => this.loading = true, () => this.loading = false));

最新更新