Thomas Segato
Thomas Segato

Reputation: 5225

Setting typed object keeps being null - Object.Assign?

I have the code shown below. When I try to set data returned from a service (the assetGroup variable) the value being set is null. My best bet is it is because not all nested arrays are typed, and needs to be mapped. So my question is two fold, is this the reason why the object continue being null after I set it? And if yes can you somehow do an assign that makes a deep mapping or whats the best approach here?

export class Asset {
    constructor(obj: any) {
        Object.assign({}, obj);
    }
    assetId:number;
    assetSubGroupId:number;
    code: number;
    name: string;
}

export class AssetGroup {

    constructor(obj: any) {
        Object.assign(this, obj);
     }
    assetGroupId:number;
    code: number;
    name: string;
    assetSubGroupList:Asset[];   
}

export class AssetSubGroup {

    constructor(obj: any) {
        Object.assign(this, obj);
     }
    assetSubGroupId:number;
    assetGroupId:number;
    code: number;
    name: string;
    assetList: Asset[];   
}

This is my service:

getAssetGroup(id:number):Observable<AssetGroup>{
    var url = `http://xxx.azurewebsites.net/api/assetgroups/${id}`;
    return  this.httpClient.get<AssetGroup>(url).pipe(map(x => new AssetGroup(x)));    
  }

My component:

private  assetGroup:  AssetGroup;

loadAssets(id)
{   
  this.apiService.getAssetGroup(id).subscribe((data) => {
    this.assetGroup  =  data;      
    console.log(this.assetGroup);  // <---- assetGroup is NULL?
  });
}

Debugger: enter image description here

Upvotes: 0

Views: 277

Answers (1)

tommueller
tommueller

Reputation: 2496

Yes you need to explicitly map the response to your classes. The generics in Typescript just work to ensure that you do not try to access any properties that do not exist, but HttpClient does not create instances of the classes for you. For more also check this discussion on github: https://github.com/angular/angular/issues/20770

In order for you to work with this, you have two options:

  • for now your classes can just be transferred into interfaces as they do not have any functionality but only describe a contract which properties have to exist. So your example is a perfect scenario for actually just using interfaces. For more on this check this link: https://medium.com/front-end-hacking/typescript-class-vs-interface-99c0ae1c2136
  • if you actually do need the types to be classes you will have to explicitly create instances of them.

You will need to write something like this:

export class AssetGroup {

    constructor(obj: any) {
        assetGroupId = obj.assetGroupId
        code = obj.code;
        name = obj.name;
        assetSubGroupList = any.assetSubGroupList.map(
            asset => new Asset(asset)
        );
    }

    assetGroupId:number;
    code: number;
    name: string;
    assetSubGroupList:Asset[];   

}

Your code does not work, because Object.assign does not return an Object type Asset as it merges an Object of Asset with an Object of type any and thus cannot know what the output type can be described as.

Upvotes: 1

Related Questions