How to map http response to client model type in Angular

依然范特西╮ 提交于 2020-01-05 03:48:19

问题


I have model MyObejct like this in client:

class MyObject{
 id: number;
 name: string;
 active: boolean;
}

And the http response json data like this:

[
 {
  id: "1",
  name: "object1",
  active: "true",
 }
]

The http response is key-value pair, and all the value type is string. So how can I map the http response to 'MyObject' type. The http get function is like this:

getMyObejct(name: string): Observable<MyObject> {
 const url = this.url + 'name/' + name;
 return this.http.get<MyObject>(url);  // This is part I'm confused
}

getAllObjects(): Observable<MyObject[]> {
 return this.http.get<MyObject>(this.url); // How to map this reponse to MyObject
}

The http response values are all string type, but MyObject has types number and boolean. How can I do that?


回答1:


It depends on whether you truly need an instance of MyObject, or just something with the same properties.

If you just need an object with the same properties, you can do:

return this.http.get<MyObject>(url).map( (obj) => {
  id: Number(obj.id), 
  name: obj.name,
  active: obj.active === 'true'
});

If you need an actual MyObject instance (e.g. if MyObject has methods), you can do:

return this.http.get<MyObject>(url).map( (obj) => Object.assign(new MyObject(), {
  id: Number(obj.id), 
  name: obj.name,
  active: obj.active === 'true'
}));



回答2:


You can transform the repsonse to the model like this

getAllObjects(): Observable<MyObject[]> {
   return this.http.get(this.url).pipe(
      map((data: any[]) => data.map((item: any) => {
          const model = new MyObject();
          Object.assign(model, item);
          return model;
       }))
       );
}

Or you can use a generic transformer class which can take any type, extract the predefined meta data and perform data transformation.

export class ModelMapper<T> {
  _propertyMapping: any;
  _target: any;
     constructor(type: { new(): T ;}){
        this._target = new type();
        this._propertyMapping = this._target.constructor._propertyMap;
     }

     map(source){
       Object.keys(this._target).forEach((key) => {
         const mappedKey = this._propertyMapping[key]
         if(mappedKey){
           this._target[key] = source[mappedKey];
         }
         else {
           this._target[key] = source[key];
         }
       });
       Object.keys(source).forEach((key)=>{
         const targetKeys = Object.keys(this._target);
         if(targetKeys.indexOf(key) === -1){
           this._target[key] = source[key];
         }
       });
      return this._target;
     }
}

Now import this class in service.ts and use this to transform the response json.

import { ModelMapper } from './modelMapper';

public get<T>(url: string, itemType: any): Observable<T> {
    if (!url) {
      return;
    }

    return this.http.get<T>(url)
      .pipe(
        map(data => data.map((item: any) => {
          return new ModelMapper(itemType).map(item);
        }
      ))
    );
  }
}

If you want to perform backend data field renaming, then you can create a PropertyMap decorator like below

export function propertyMap(sourceProperty : string) {
    return function (target: any, propertyKey : string) {
      if(!target.constructor._propertyMap) { 
        target.constructor._propertyMap = {};
      } 
      target.constructor._propertyMap[propertyKey] = sourceProperty;
    }
}

Then you use it on your model class

@propertyMap('namealias')
public name: string;



回答3:


You may want to look into class-transformer. The plainToClass method transforms a plain javascript object to an instance of a specific class.

import {plainToClass} from "class-transformer";

fetch(url).then((objects: MyObject[]) => {
    const realMyObjects = plainToClass(MyObject, objects);
    // now each object in realMyObjects is an instance of MyObject class
});

In your case, you'll need to enable implicit type conversion:

class MyPayload {
  prop: string
}

const result1 = plainToClass(MyPayload, { prop: 1234 }, { enableImplicitConversion: true });

/**
 *  result1 will be `{ prop: "1234" }` - notice how the prop value has been 
 *  converted to string.
 */




回答4:


To specify the response object type, first define an interface with the required properties. (Use an interface rather than a class; a response cannot be automatically converted to an instance of a class.)

interface MyObject{
 id: number;
 name: string;
 active: boolean;
}

getAllObjects(): Observable<MyObject[]> {
 return this.http.get<MyObject>(this.url); // now returns an Observable of MyObject[]
}

Read more about it here - https://angular.io/guide/http#typechecking-the-response



来源:https://stackoverflow.com/questions/58152047/how-to-map-http-response-to-client-model-type-in-angular

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!