Cast angular http response into class
Asked Answered
T

2

7

I am trying to cast a response object from an HTTP Post request in my angular project to a Person class that I have defined. I have defined a generic post method in an HTTP Service, and am calling that in my person service with the generic replaced with Person. So, I would think, since I have done that the HTTP response should be a Person, but it's not - it's just an Object. I need it to be a Person because my Person class has some custom logic on it that I need to access. I could write a helper method in my person service, but I feel like this should work - especially since VS Code intellisense says that the response in my component is a Person when I hover my mouse over it.

Here is my code:

http.service.ts

@Injectable()
export class HttpService {
    baseUrl = 'https://baseurl.com';

    constructor(private http: HttpClient) { }

    post<T>(endpointUrl: string, body: any): Observable<T> {
        const fullUrl = this.baseUrl + endpointUrl;
        return this.http
            .post<T>(fullUrl, body)
            .pipe(
                map(response => response as T)
            );
    }
}

person.service.ts

@Injectable()
export class PersonService {

    constructor(private httpService: HttpService) { }

    newPerson(body: Person): Observable<Person> {
        return this.httpService.post<Person>('/people', JSON.stringify(body));
    }
}

person.component.ts

@Component({
    selector: 'app-person',
    templateUrl: './person.component.html',
    styleUrls: ['./person.component.css'],
})
export class PersonComponent {
        person: Person = new Person();

        onSubmit(id: number) {
        if (id == 0) {
            console.log(this.person);                              // Person {id: 0, ...
            console.log(this.person.constructor.name);             // Person
            let body = this.person.fromFormGroup(this.formGroup);

            this.personService.newPerson(body)
                .subscribe(response => {                           // VS Code intellisense says this: (parameter) response : Person
                    this.person = response as Person;
                    console.log(this.person);                      // {id: 72, ...
                    console.log(this.person.constructor.name);     // Object

                    // Trying a different way
                    this.person = <Person>response;
                    console.log(this.person);                      // {id: 72, ...
                    console.log(this.person.constructor.name);     // Object
                })

        }
    }

}
Teerell answered 1/6, 2019 at 19:58 Comment(1)
Possible duplicate of Angular 6: ERROR TypeError: "... is not a function" - but it isMisery
W
20
newPerson(body: Person): Observable<Person> {
  return this.httpService.post<Person>('/people', JSON.stringify(body));
}

The HttpClient.post() method can not return type Person, because JSON responses are just cast to types. The default type is just Object, but you need to create a new instance of Person for each response. If the type was an interface then there would be no problem.

You can create a new instance of Person and then assign the values to that instance.

newPerson(body: Person): Observable<Person> {
  return this.httpService.post('/people', JSON.stringify(body)).pipe
    map(value => Object.assign(new Person(), value)
  );
}
Wineglass answered 1/6, 2019 at 21:6 Comment(1)
Thanks very much! I'm new to TS, seeing the Object.assign() method was very helpful.Teerell
T
5

Response from the http service will be a json object which is deserialised to a javascript object. Your person service cannot convert JavaScript object received in the api response to person object out of the box. Remember typescript code is transpiled to javascript code. To convert the javascript object to your Person class object you will have to manually instantiate your class object and fill the properties from the response of the api by projecting the response to person object. You can do the following [notice the map operator used to project the response to person]-

person.service.ts

@Injectable()
export class PersonService {

  constructor(private httpService: HttpService) {}

  newPerson(body: Person): Observable<Person> {
    return this.httpService.post<Person>('/.people', JSON.stringify(body))
      .pipe(
        map(response => {

          const person = new Person();
          //fill the person props from response
          return person;
        }),
      );
  }
}
Transience answered 1/6, 2019 at 20:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.