Angular 2+ HTTP-запрос - Показывать счетчик загрузки в течение минимального времени как для успешных, так и для ошибок. - PullRequest
2 голосов
/ 19 июня 2020

Требование :

Мне нужно показывать счетчик загрузки в пользовательском интерфейсе каждый раз, когда выполняется HTTP-запрос c.

Чтобы иметь приятный визуальный аспект, я решил показать счетчик на экране на минимум 1 секунду , даже если запрос длится меньше ( на самом деле длится от 0,1 до 3–4 минут, поэтому лучше удерживать спиннер не менее 1 секунды ). Итак, условия следующие:

  • если запрос занимает меньше , чем 1 с, счетчик будет отображаться в течение 1 секунды
  • , если запрос занимает дольше , чем 1 с, счетчик будет показывать, пока не закончится.

Я знаю, что этот подход может быть спорным с точки зрения UI / UX, но я предпочитаю рассматривать его как техническую проблему.

Код, который я пробовал:

Как было обнаружено в другой реализации SO, я пробовал подход с combineLatest - объедините Observable, который принимает 1 с и Наблюдается для HTTP-запроса.

load() {
  this.loading = true; // this will show the spinner
  combineLatest(timer(1000), this.service.apiCall())
    .pipe(
      finalize(()=> {
        this.loading = false; // this will hide the spinner
      }),
      map(x => x[1])
    )
    .subscribe(x => {
      console.log(x);
    });
}

Это хорошо работает, если HTTP-запрос возвращается со статусом 200.

Проблема:

Код выше не работает, если HTTP-запрос возвращает ошибку (4 / 5xx). Observables заканчивают sh сразу после завершения HTTP-запроса.

Я хочу, чтобы счетчик имел такое же поведение, даже если запрос завершился первым, с ошибкой.

Я сделал простой Stackblitz, в котором можно играть с разными запросами: https://stackblitz.com/edit/spinner-with-min-duration-zcp7hc

Спасибо!

Ответы [ 2 ]

1 голос
/ 19 июня 2020

Вы можете использовать BlockUIModule и предоставить delayStop в 1 секунду.

@NgModule({
  declarations: [AppComponent],
  imports: [
    BlockUIModule.forRoot({
      delayStop: 1000,
      template: LoaderComponent
    })
  ],

Это будет работать для сценария успеха и ошибки ios. Дополнительно вы можете предоставить шаблон для настраиваемого сообщения с компонентом ввода, например, Loader Component

loader.component.ts

import { Component, OnInit } from "@angular/core";

@Component({
  selector: "ds-loader",
  templateUrl: "loader.component.html"
})
export class LoaderComponent implements OnInit {

  message: string;
  constructor() {}

  ngOnInit() {}
}

loader.component. html

    <div class="loader"></div>
        <p>{{message}}</p> 

Чтобы использовать его внутри компонента, например:

@Component({
  selector: "ds-block-ui-search",
  templateUrl: "block-ui-search.component.html",
  animations: [onLoad],
  providers: []
})
export class BlockUiSearchComponent implements OnInit {

  @BlockUI() blockUI;


  ngOnInit() {


    this.blockUI.start("Loading...");

    this.blockUiSearchSearvice.getYourData().subscribe(
      res => {

        this.blockUI.stop();
        //Custom Logic

      },
      err => {
        this.blockUI.stop();

      }
    );
  }
}
1 голос
/ 19 июня 2020

Из документации rxjs:

Если хотя бы один Observable был передан в combLatest и все переданные Observables что-то испустили, в результате Observable завершится, когда все объединенные потоки завершатся. ... С другой стороны, если какие-либо наблюдаемые ошибки ,commonLatest немедленно выдаст ошибку, а все остальные Observables будут отписаны. подпрограмма перехвата с использованием конвейера catchError, чтобы она не могла выдать ошибку оператору combineLatest. Примерно так будет работать.

load() {
  this.loading = true; // this will show the spinner
  combineLatest(timer(1000), 
    this.service.apiCall().pipe(
        catchError(err)=>{
            return of(err); // Return observable wrapped with error.
        }))
    .pipe(
      finalize(()=> {
        this.loading = false; // this will hide the spinner
      }),
      map(x => x[1])
    )
    .subscribe(x => {
      console.log(x);
      //Check in the result if there is an error in http
      if(x instanceof HttpErrorResponse) {
           // do what you want in error scenario.
      }
    });
}

Демо Stackblitz: https://stackblitz.com/edit/spinner-with-min-duration-ls9dq7

...