Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cancel http request in Angular 6?

I have a page with three components: 1. Products list component which gets some products as input and display them. 2. Filters component which displays some filters list i.e. (size, colour,...) and also display the added filters. 3. Main component which is the root component

Let say a user adds 1 filter which fires a http request to get new filtered products and while the request is pending he removes the added filter which fires another http request to fetch all the products How to cancel the first request so we don't display the filtered products? Here is my code:

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() {
  }

}
like image 761
SMH Avatar asked Aug 30 '18 11:08

SMH


People also ask

How do I cancel an HTTP request?

Cancelling an HTTP request is a common requirement. For example, you could have a queue of requests where a new request supersedes a pending request and that pending request needs to be cancelled. To cancel a request we call the unsubscribe function of its subscription. @ Component ( { /* ...

How to use httpcancelservice in angular?

The HttpCancelService service will use A Subject which acts as an Observer to Observables. Next, to keep an eye on HTTP calls and Router navigation, we will create an Angular Interceptor. This will also import the Service we created in the previous step. Replace below code in managehttp.interceptor.ts file as shown below:

How to make HTTP request from an angular app?

Before making HTTP requests from your Angular app you need to do a couple of things. 1. Add the HttpClientModule to the imports array of your AppModule like below on lines 3 and 10. 2. Import the HttpClient into your component and add it to the constructor () params like below on lines 2 and 8.

How do I cancel a request in a queue?

Cancelling an HTTP request is a common requirement. For example, you could have a queue of requests where a new request supersedes a pending request and that pending request needs to be cancelled. To cancel a request we call the unsubscribe function of its subscription.


2 Answers

Long story short:

Easiest way to handle such situations is by using the switchMap operator. What this does is cancel the internal subscription as soon as a new event comes along.

One implementation would be:

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);
  }
}

Long story:

What happens here is: When you change filter the onFilterChange is triggered. You then emit the latest filters (inside this.filtersService.filters) through the _filters$ Subject (a subject is almost identical to an EventEmitter).

Back in time during component initialization the ngOnInit method has called setProducts, which has subscribed to the _filters$ subject for future events (none has happened at this point). When an event arrives on _filters$ then we trigger the getProducts method of dataservice, passing it the filters that where contained in the event. We will be waiting on this line until the http call has completed. As soon as it completes the result of the http call will be assigned to the products of the component.

If while we are waiting for the http response to get back, onFiltersChange is fired again, then a new event will arive at the switchMap and it will cancel the previous http request so that it can handle the new event.

This is a very powerful approach as changing a single operator, you can easily change the behavior of your app. For instance, changing switchMap to concatMap will make the request wait for the previous one to complete (will happen serially). Changing it to flatMap will have the same behaviour as the original code you posted (http requests will happen as soon as filters change, without affecting previous ones, order of responses will not predictable) and so on.

like image 100
masimplo Avatar answered Sep 30 '22 05:09

masimplo


Note : to cancel the request just use unsubscribe.

For exmple

   const course$ = this.service$.getCourses(`/api/courses`).subscribe(courses => { console.log(courses) }

   setTimeout(() => course$.unsubscribe(),1000) // cancel the request
like image 43
mabdullahse Avatar answered Sep 30 '22 06:09

mabdullahse