问题
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