Как добавить в массив, если нет ответа в течение 1 секунды? - PullRequest
0 голосов
/ 28 апреля 2020

У меня есть перехват, который слушает запросы / ответы.

Я пытался запустить спиннер, только если запросы приходят дольше 1 секунды:

 @Injectable()
export class LoadingInterceptor implements HttpInterceptor {
  private requests: HttpRequest<any>[] = [];

  constructor(private spinnerService: SpinnerService) {}

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    this.requests.push(req);
    this.spinnerService.isLoading.next(true);

    return new Observable((observer) => {
      next.handle(req).subscribe(
        (event) => {
          if (event instanceof HttpResponse) {
            this.removeRequest(req);
            observer.next(event);
          }
        },
        () => {
          this.removeRequest(req);
        },
        () => {
          this.removeRequest(req);
        }
      );
    });
  }

  private removeRequest(request: HttpRequest<any>) {
    const index = this.requests.indexOf(request);

    if (index >= 0) {
      this.requests.splice(index, 1);
    }

    this.spinnerService.loadingStop.next();
    this.spinnerService.loadingStop.complete();
    this.spinnerService.isLoading.next(this.requests.length > 0);
  }
}

Сервис спиннера:

 constructor() {
    this.isLoading
      .pipe(debounceTime(100), delay(1000), takeUntil(this.loadingStop))
      .subscribe((status: boolean) => (this.loadingStatus = status));
  }

Для этого я добавил это:

.pipe(debounceTime(100), delay(1000), takeUntil(this.loadingStop))

Но у меня это не работает ... Как показать счетчик, если ответ приходит больше 1 секунды?

Ответы [ 2 ]

2 голосов
/ 28 апреля 2020

Использует оператор iif для немедленной остановки загрузки.

это то, как должен выглядеть перехватчик:

constructor(private spinnerService: SpinnerService) { }

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  this.spinnerService.start(request.url);

  return next.handle(request).pipe(
    finalize(() => () => this.spinnerService.stop(request.url))
  );
}

это служба загрузки:

@Injectable()
export class SpinnerService {
  private _loading: BehaviorSubject<boolean>;
  private _request: Set<string>;
  private _delayTime: number;

  constructor() {
    this._loading = new BehaviorSubject(false);
    this._request = new Set();
    this._delayTime = 1000;
  }

  isLoading(time?: number): Observable<boolean> {
    return this._loading.asObservable().pipe(
      // uses switchMap to cancel the previous event
      switchMap(isLoading =>
        // use iif to put delay only for true value
        iif(
          () => isLoading,
          of(isLoading).pipe(
            delay(time !== undefined ? time : this._delayTime),
          ),
          of(isLoading),
        ),
      ),
    );
  }

  start(request: string = 'default', delayTime?: number): void {
    if (delayTime !== undefined)
      this._delayTime = delayTime;

    this._request.add(request);
    this._loading.next(true);
  }

  stop(request: string = 'default'): void {
    this._request.delete(request);

    if (!this._request.size)
      this._loading.next(false);
  }
}

и так должно выглядеть в шаблоне

@Component({
  selector: 'my-app',
  template: `<div *ngIf="isLoading$ | async">loading...</div>`,
})
export class AppComponent  {
  isLoading$: Observable<boolean>;

  constructor(private spinnerService: SpinnerService) {
    this.isLoading$ = this.spinnerService.isLoading();
  }
}
1 голос
/ 28 апреля 2020

Для предотвращения мерцания индикатора загрузки (я пропустил обработку нескольких запросов).

@Injectable()
export class LoadingInterceptor implements HttpInterceptor {

  constructor(private spinnerService: SpinnerService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>> {
    this.spinnerService.start();
    return next.handle(req).pipe(finalize( () => this.spinnerService.stop()));
  }
}

debounceTime (500) в сервисе spinner делает свое дело:

export class SpinnerService {

  private readonly state = new BehaviorSubject<boolean>(true);
  readonly state$ = this.state.asObservable()
    .pipe(
       debounceTime(500), 
       distinctUntilChanged()
  );

  constructor() {}

  public start() {
    this.state.next(true);
  }

  public stop() {
    this.state.next(false);
  }
}

Компонент, чтобы увидеть это в действии:

export interface Post {
  id: string;
  title: string;
  body: string;
}

@Component({
  selector: 'app-posts',
  templateUrl: './posts.component.html',
  styleUrls: ['./posts.component.css'],
})
export class PostsComponent implements OnInit {
  readonly posts$: Observable<Post[]> = this.httpClient
    .get<Post[]>('https://jsonplaceholder.typicode.com/posts')
    .pipe(shareReplay(1));

  readonly state$ = this.spinnerService.state$;

  constructor(
    private spinnerService: SpinnerService,
    private httpClient: HttpClient
  ) {}

  ngOnInit() {}
}

HTML:

<p>List of Posts</p>

<ng-container *ngIf="(state$ | async);  else printResult">
  <h1>Loading...</h1>
</ng-container>

<ng-template #printResult>
  <ng-container *ngIf="posts$ | async as posts">
    <p *ngFor="let post of posts">
      {{ post.title }}
    </p>
  </ng-container>
</ng-template>

Решение с помощью перехватчика несколько крупнозернистое. В какой-то момент вам может понадобиться более мелкозернистое решение. Например, чтобы показать индикатор загрузки для нескольких параллельных запросов / компонентов. Другое решение дано в сообщении в блоге Нила .

Существует множество вариантов решения вашей проблемы. Надеюсь, это поможет.

...