Search code examples
angulartypescriptrxjsangular2-httprxjs-lettable-operators

Rxjs pipe not working with angular´s Http.get()


I have an Angular 4 Component that is calling a Service to get data. Not the strangest of situations. After I retrieve the data and need to transform it and filter it. Apparently the way to do this nowadays is to use pipe.

In my Component:

ngOnInit(): void {
    this.suService.getShippingUnitTypes().subscribe(res => {
        console.log("Getting shipping unit types: ", res);
    });
}

In my service:

getShippingUnitTypes(): any {
    const convertToJson = map(value => (value as any).json().XXETA_GRID_STATIC_LOV);
    const filterShippingUnit = filter(value => (value as any).LOV_TYPE == "SHIPPING_UNIT");

    return this.http.get(
        this.constantsService.LOOKUP_COLUMN_BATCH_URL
    ).pipe(convertToJson, filterShippingUnit);
}

The service imports the following:

import { Injectable } from '@angular/core';
import { Http, Response, RequestOptions, Headers, RequestMethod } from '@angular/http';
import { Observable, pipe } from 'rxjs/Rx';
import { map, filter } from 'rxjs/operators';

When debugging, the code never errors but simply never gets to the console.log() statement in the Component. If I remove the .pipe() and simply return the Observable the code logs what I would expect only without the transforming and filtering.

I'm very new to Rxjs and using Pipe. I'm obviously not understanding something.

Edited to Add Information:

I put tap into the pipe like such...

pipe(tap(console.log), convertToJson, tap(console.log), filterShippingUnit, tap(console.log))

I didn't know that tap existed but it is useful. The first two console logs give me what I would expect. The third one, right after the filterShippingUnit, doesn't do anything. It doesn't log a value at all. Not even null.

After convertToJson console.log spits out an array of 28 objects. One of the objects is:

{LOV_TYPE: "SHIPPING_UNIT", LOV_TAB_TYP_ITEM: Array(4)}

I would expect that object to be passed on based on the filterShippingUnit filter.


Solution

  • The problem is most likely here:

    const filterShippingUnit = filter(value => (value as any).LOV_TYPE == "SHIPPING_UNIT");
    

    Assuming that after parsing the body of the response to JSON, you get an array of type Foo, where Foo is defined as follows:

    interface Foo {
     LOV_TYPE: string;
     fooProperty: string;
     fooNumber: number;
    }
    

    You are trying to apply the filter to the array object, not to the objects contained in it.

    You have two options: flatten the array and emit its values as singular events, and then put them together again as an array, or map the array to a new one; the second one being the easiest as follows:

    const filterShippingUnit = map((list: Foo[])=> list
                  .filter(foo => foo.LOV_TYPE === "SHIPPING_UNIT"));
    

    The first approach could be implemented as:

    import { flatMap, toArray } from 'rxjs/operators';
    
    return this.http.get(this.constantsService.LOOKUP_COLUMN_BATCH_URL)
        .pipe(
          flatMap(response => response.json() as Foo[])
          map(foo => foo.LOV_TYPE === "SHIPPING_UNIT") // TypeScript will infer that foo is of type Foo
          toArray
         );
    

    As its easy to notice that you are just starting in angular, I would suggest you the following:

    • Define interfaces for everything that comes from a backend
    • Use the new HttpClient API from angular (Http is deprecated, see https://angular.io/guide/http)
    • I dont think is necessary to define constant functions to store operations that you will use in a stream (as suggested in the tutorial/guide you are following). You lose all the type information by doing that, if you dont declare the argument types explicitly. But dont trust me on this one, some people say that despite the fact that typescript can infer a type, its a good practice to declare it explicitly...