3

So I'm getting the following JSON structure from my asp.net core api:

{
  "contentType": null,
  "serializerSettings": null,
  "statusCode": null,
  "value": {
     "productName": "Test",
     "shortDescription": "Test 123",
     "imageUri": "https://bla.com/bla",
     "productCode": null,
     "continuationToken": null
  }
}

I have the following typescript function that invokes the API to get the above response:

public externalProduct: ProductVM;


getProductExternal(code: string): Observable<ProductVM> {
    return this.http.get("api/product?productCode=" + code)
        .map((data: ProductVM) => {
            this.externalProduct = data; //not working...
            console.log("DATA: " + data);
            console.log("DATA: " + data['value']);
            return data;
        });    
}

ProductVM:

export interface ProductVM {

    productName: string;
    shortDescription: string;
    imageUri: string;
    productCode: string;
    continuationToken: string;
}

My problem is that I can't deserialize it to ProductVM. The console logs just produce [object Object]

How can I actually map the contents of the value in my json response to a ProductVM object?

Is it wrong to say that data is a ProductVM in the map function? I have tried lots of different combinations but I cannot get it to work!

I'm unsure whether I can somehow automatically tell angular to map the value array in the json response to a ProductVM object or if I should provide a constructor to the ProductVM class (it's an interface right now), and extract the specific values in the json manually?

1
  • What is the type that your API is returning? Do you also return a similar ProductVM? Looks like you have some sort of wrapper on your API return. The actual data you want is on the value property. Also, why you want to map on the result? http get returns an Observable.. so you can just subscribe to it. Commented Nov 16, 2017 at 15:21

5 Answers 5

6

The data object in the map method chained to http is considered a Object typed object. This type does not have the value member that you need to access and therefore, the type checker is not happy with it.

Objects that are typed (that are not any) can only be assigned to untyped objects or objects of the exact same type. Here, your data is of type Object and cannot be assigned to another object of type ProductVM.

One solution to bypass type checking is to cast your data object to a any untyped object. This will allow access to any method or member just like plain old Javascript.

getProductExternal(code: string): Observable<ProductVM> {
  return this.http.get("api/product?productCode=" + code)
    .map((data: any) => this.externalProduct = data.value);    
}

Another solution is to change your API so that data can deliver its content with data.json(). That way, you won't have to bypass type checking since the json() method returns an untyped value.

Be carefull though as your any object wil not have methods of the ProductVM if you ever add them in the future. You will need to manually create an instance with new ProductVM() and Object.assign on it to gain access to the methods.

Sign up to request clarification or add additional context in comments.

16 Comments

.map(data => Object.assign(new ProductVM(), data))should work too.
Be very careful with nested objects though as they will not be typed unless instanciated manually.
Hmm, just as in srAxi's answer. I cannot seem to call anything on data. If I write data.value I get "property 'value' does not exist on type 'Object'. Any idea why I'm getting this compile error? Also if I say data.json() it will also complain. Hovering over data shows me that it is of type 'Object'.
Did you try with just data? Not data.value
This one liner should work then: .map((data: any) => Object.assign(new ProductVM(), data.value)) right?
|
2

From angular documentation: Typechecking http response

You have to set the type of returned data when using new httpClient ( since angular 4.3 ) => this.http.get<ProductVM>(...

public externalProduct: ProductVM;    
getProductExternal(code: string): Observable<ProductVM> {
        return this.http.get<ProductVM>("api/product?productCode=" + code)
            .map((data: ProductVM) => {
                this.externalProduct = data; // should be allowed by typescript now
                return data;
            });    
    }

thus typescript should leave you in peace

Comments

1

Have you tried to replace

this.externalProduct = data;

with

this.externalProduct = data.json();

Hope it helps

Comments

1
getProductExternal(code: string): Observable<ProductVM> {
    return this.http.get("api/product?productCode=" + code)
        .map(data => {
            this.externalProduct = <ProductVM>data;
            console.log("DATA: " + this.externalProduct);
            return data;
        });    
}

So, first we convert the response into a JSON. I store it into response just to make it cleaner. Then, we have to navigate to value, because in your data value is the object that corresponds to ProductVM.

I would do it like this though:

Service

getProductExternal(code: string): Observable<ProductVM> {
        return this.http.get(`api/product?productCode=${code}`)
            .map(data => <ProductVM>data)
            .catch((error: any) => Observable.throw(error.json().error || 'Server error'));    
    }

Component

this.subscription = this.myService.getProductExternal(code).subscribe(
  product => this.externalProduct = product,
  error => console.warn(error)
);

6 Comments

Oh. So you're not restricting data to be of any type. That makes totally sense, why would it be a ProductVM at that point. So this line "this.externalProduct = <ProductVM>response.value" will do the actual deserialization to ProductVM object? I'll try this and get back to you!
@DSF If you feel like, try the 2nd approach. I believe is cleaner. I hope it helps. ;)
I'm getting a compile error when using json(). "Property json does not exist on type Object". I'm using: import { HttpClient } from "@angular/common/http";
Remove json(). My bad. HttpClient already applies json() to responses...
Then I'm just getting "Property 'value' does not exist on type 'Object'. Hovering over data tells me that it's of type 'Object', but value doesn't exist on Object, how do I get past this error?
|
0

I used this approach in a client which uses the method

HttpClient.get<GENERIC>(...). 

Now it is working. Anyway, I do not understand, why I do not receive a type of T back from the http client, if I don't use the solution provided in the answer above.

Here is the client:

// get
get<T>(url: string, params?: [{key: string, value: string}]): Observable<T> {
var requestParams = new HttpParams()

if (params != undefined) {
  for (var kvp of params) {
    params.push(kvp);
  }
}

return this.httpClient.get<T>(url, {
  observe: 'body',
  headers: this.authHeaders,
  params: requestParams
}).pipe(
  map(
    res => <T>res
  )
);
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.