0

I am very new to Angular and typescript and have been reading some posts about returning typed data. Some articles state you have to map your objects after returning them from the service, others just type the result in the call. What the easiest way in Angular 6 to get strongly typed data? The below doesn't work do I really need to map each property manually?

My model:

export interface MenuItem {
    menuItemsId:number;
    groupCode: number;
    groupDescription: string;
    subGroupCode:number;
    subGroupDescription:string;
    itemCode:number;
    itemDescription:string;
    grade:string;
    remark:string;
}

My service:

getValues():Observable<MenuItem[]>{
return  this.httpClient.get<MenuItem[]>("https://hafnia-asset-control-dev.azurewebsites.net/api/menuitems/cosmetic");  
}

Call:

var t = this.apiService.getValues().subscribe((data) => {
  this.menuItems  =  data;
  console.log(this.menuItems);

});

JSON result:

[{"menuItemsId":1,"groupCode":1000,"groupDescription":"xxx","subGroupCode":1000,"subGroupDescription":"SHELL","itemCode":1001,"itemDescription":"ccc","grade":"Int","remark":"String"},{"menuItemsId":2,"groupCode":1000,"groupDescription":"COSMETIC","subGroupCode":1000,"subGroupDescription":"xxx","itemCode":1002,"itemDescription":"xxx","grade":"Int","remark":"String"}

app.module:

providers: [AdalService, AdalGuard,{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }],

Interceptor:

import { Injectable } from '@angular/core';
import { HttpEvent,HttpHandler,HttpRequest,HttpInterceptor } from '@angular/common/http';
import { AdalService } from 'adal-angular4';
import { Observable } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
    constructor(private adalService: AdalService) { }

    intercept(req: HttpRequest<any>, next:HttpHandler): Observable<HttpEvent<any>>{
        const authHeader = this.adalService.userInfo.token;

        const authReq = req.clone({headers: req.headers.set('Authorization', `Bearer ${authHeader}`)});
        return next.handle(authReq);
    }

}
Thomas Segato
  • 4,567
  • 11
  • 55
  • 104
  • 1
    If your API data have the same properties, you don't need to map tham one by one, it should work. But note that the constructor you defined will NOT be called, as typing the service call is more for convenience and clarity – David Sep 24 '18 at 13:23
  • you should use `get` not `get>`, and it will only work if that URL returns an array which matches MenuItem, else you will appear to have no data – mast3rd3mon Sep 24 '18 at 13:25
  • @mast3rd3mon - `you should use get not get>` <= same thing, see [Array VS Type\[\] in Typescript](https://stackoverflow.com/q/36842158/1260204) – Igor Sep 24 '18 at 13:26
  • Thanks all. I have updated code makes sense. However I still get a list of Objects. See image in main post. – Thomas Segato Sep 24 '18 at 13:44
  • Thanks @mast3rd3mon I tried your suggestion, but still same:( typeof data[0] "object" – Thomas Segato Sep 24 '18 at 14:50
  • I have added JSON result, and changed model to interface. Also I removed type when calling service. I guess that isent needed if the service does the casting. – Thomas Segato Sep 24 '18 at 15:05

1 Answers1

1

All of the articles you read are somehow right.

When you do things like this.httpClient.get<YourType>, this is called type assertion. That means you will be treating the resulting object as a YourType, it will enable autocompletion in your IDE, throw errors at compilation when calling undefined properties, etc... but the object won't be a real instance of YourType at runtime.

If YourType contains only properties, then it's like you're using it as an interface, so it's fine: you just prevent yourself from misusing your object. On the other hand, when transpiled to Javascript, the type will never show and your objects will appear as literal objects.

If YourType contains non-static functions, getters, setters, that you want to call on instantiated objects, then you have to add a map this.httpClient.get<YourType>("url").pipe(map(x => new YourType(x)) and a constructor:

class YourType {
   // Properties...
   constructor(obj: any) {
      Object.assign(this, obj);
   }
}

This way you'll get real instances of YourType

Guerric P
  • 30,447
  • 6
  • 48
  • 86
  • 1
    Wondering if it can have something to do with I have a custom interceptor? I added it to main post. – Thomas Segato Sep 24 '18 at 15:35
  • I dont get the downvoting either. However just upvoted. – Thomas Segato Sep 24 '18 at 15:35
  • @ThomasSegato since you want an `Array` you have to map it as `Array`, did you do it? `this.httpClient.get("url").pipe(map(x => x.map(y => new MenuItem(y))))` – Guerric P Sep 24 '18 at 15:43
  • I was just told in this thread that was not needed when using interface? Only if its a class? – Thomas Segato Sep 24 '18 at 15:48
  • I'm sorry I don't get what you're expecting. I assumed you still had `"object"` when evaluating `typeof data[0]`. If this is your problem and you want to see `"MenuItem"` instead, then you have to do the mapping with the constructor, because if you don't do that, the type is not reflected in Javascript generated code at all. – Guerric P Sep 24 '18 at 15:51
  • Sorry @YoukouleleY you right I misunderstood you first post. I read it as it would be compiled into the typed object if there was no methods. I will go for your solution my mapping the object. Thanks. – Thomas Segato Sep 24 '18 at 17:18
  • @ThomasSegato I clarified my answer – Guerric P Sep 25 '18 at 12:49