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

}
6
  • 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 Commented Sep 24, 2018 at 13:23
  • you should use get<MenuItem[]> not get<Array<MenuItem>>, and it will only work if that URL returns an array which matches MenuItem, else you will appear to have no data Commented Sep 24, 2018 at 13:25
  • @mast3rd3mon - you should use get<MenuItem[]> not get<Array<MenuItem>> <= same thing, see Array<Type> VS Type[] in Typescript Commented Sep 24, 2018 at 13:26
  • Thanks all. I have updated code makes sense. However I still get a list of Objects. See image in main post. Commented Sep 24, 2018 at 13:44
  • Thanks @mast3rd3mon I tried your suggestion, but still same:( typeof data[0] "object" Commented Sep 24, 2018 at 14:50

1 Answer 1

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

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

7 Comments

Wondering if it can have something to do with I have a custom interceptor? I added it to main post.
I dont get the downvoting either. However just upvoted.
@ThomasSegato since you want an Array you have to map it as Array, did you do it? this.httpClient.get<MenuItem[]>("url").pipe(map(x => x.map(y => new MenuItem(y))))
I was just told in this thread that was not needed when using interface? Only if its a class?
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.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.