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

时间:2017-01-16 01:40:07

标签: angular typescript rxjs reactive-programming rxjs5

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

不是将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);
  }
}

2 个答案:

答案 0 :(得分:6)

你可以写下这些内容:

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,console

内部combineLatest()收到两个Observables:

  1. 300毫秒延迟
  2. 来自远程服务的客户(在本演示模拟中)
  3. 然后还有投影功能用于选择我们想要进一步传播的内容。两个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 }
    

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

答案 1 :(得分:0)

这是带有可重用运算符的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));