Как отменить http-запрос в Angular 6? - PullRequest
0 голосов
/ 30 августа 2018

У меня есть страница с тремя компонентами: 1. Компонент списка продуктов, который получает некоторые продукты в качестве входных данных и отображает их. 2. Компонент Filters, который отображает список некоторых фильтров (т.е. размер, цвет, ...), а также отображает добавленные фильтры. 3. Основной компонент, который является корневым компонентом

Допустим, пользователь добавляет 1 фильтр, который запускает http-запрос для получения новых отфильтрованных продуктов, и в то время как запрос находится на рассмотрении, он удаляет добавленный фильтр, который запускает другой http-запрос для получения всех продуктов. Как отменить первый запрос, чтобы мы не отображали отфильтрованные продукты? Вот мой код:

class FiltersService {
  private _filters: any[];

  get filters() {
    return this._filters;
  }
  addFilter(filter) {
    this._filters.push(filter);
  }

  removeFilter(filter) {
    // Remove filter logic ...
  }

}

class DataService_ {
  constructor(private http: HttpClient) {

  }

  getProducts(filters) {
    return this.http.post<any[]>('api/get-products', filters)
  }

}


@Component({
  selector: 'app-main',
  template: `
  <div>
      <app-filters [filtersChanged]="onFiltersChange()"></app-filters>
      <app-products-list [products]="products"> </app-products-list>
</div>
`
})
class MainComponent {
  products: any[];

  constructor(private dataService: DataService_, private filtersService: FiltersService) {

  }

  ngOnInit() {
    this.setProducts()
  }

  setProducts() {
    let filters = this.filtersService.filters;
    this.dataService.getProducts(filters)
      .subscribe(products => this.products = products)
  }

  onFiltersChange() {
    this.setProducts();
  }
}
@Component({
  selector: 'app-filters',
  template: `
  <div>
 Filters : 
 <ul>
     <li *ngFor="let filter of filters" (click)="addFilter(filter)"> {{ filter.name }}</li>
 </ul>

 <hr>
 Added Filters:
 <ul>
 <li *ngFor="let filter of filtersService.filters"> {{ filter.name }}  <button (click)="removeFilter(filter)"> Remove</button></li>
</ul>
</div>

`
})
class FiltersComponent {
  filters = [{ name: 'L', tag: 'size' }, { name: 'M', tag: 'size' }, { name: 'White', tag: 'colour' }, { name: 'Black', tag: 'colour' }]
  @Output() filtersChanged = new EventEmitter()
  constructor(public filtersService: FiltersService) {

  }

  addFilter(filter) {
    const isAdded = this.filtersService.filters.find(x => x.name === filter.name);
    if (isAdded) return;
    this.filtersService.addFilter(filter);
    this.filtersChanged.emit()
  }

  removeFilter(filter) {
    this.filtersService.remove(filter);
    this.filtersChanged.emit()
  }

}

@Component({
  selector: 'app-products-list',
  template: `
  <div>
  <h1>Products</h1>
  <ul *ngIf="products.length">
      <li *ngFor="let product of products">
          {{product.name }}
      </li>
  </ul>
</div>
`
})
class ProductsListComponent {
  @Input() products
  constructor() {
  }

}

1 Ответ

0 голосов
/ 30 августа 2018

Короче говоря:

Самый простой способ справиться с такими ситуациями - использовать оператор switchMap. Это отменяет внутреннюю подписку, как только появляется новое событие.

Одна реализация будет:

class MainComponent {
  products: any[];
  private _filters$ = new Subject();

  constructor(private dataService: DataService_, private filtersService: FiltersService) {

  }

  ngOnInit() {
    this.setProducts()
  }

  setProducts() {
    this._filters$
        .switchMap((filters)=> this.dataService.getProducts(filters)) // or .let(switchMap...) if you are using rxjs >5.5
        .subscribe(products => this.products = products);
  }

  onFiltersChange() {
    this._filters$.next(this.filtersService.filters);
  }
}

Длинная история:

Что здесь происходит: Когда вы меняете фильтр, срабатывает onFilterChange. Затем вы отправляете последние фильтры (внутри this.filtersService.filters) через объект $ _filters (тема почти идентична EventEmitter).

Время назад во время инициализации компонента метод ngOnInit вызвал setProducts, который подписался на тему _filters $ для будущих событий (на данный момент ничего не произошло). Когда событие поступает в _filters $, мы запускаем метод getProducts dataservice, передавая ему фильтры, содержащиеся в событии. Мы будем ждать на этой линии, пока не завершится http-вызов. Как только он завершится, результат http-вызова будет присвоен продуктам компонента.

Если пока мы ожидаем ответа http, снова запускается onFiltersChange, то в switchMap будет происходить новое событие, и оно отменяет предыдущий запрос http, чтобы он мог обработать новое событие.

Это очень мощный подход, поскольку при смене одного оператора вы можете легко изменить поведение вашего приложения. Например, изменение switchMap на concatMap заставит запрос ждать завершения предыдущего (будет происходить последовательно). Изменение его на flatMap будет иметь то же поведение, что и исходный код, который вы разместили (запросы http будут выполняться, как только фильтры изменятся, не влияя на предыдущие, порядок ответов не будет предсказуемым) и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...