8

I'm playing around with Angular2, and was hoping someone could offer some advice on how to achieve this;

For example, if my model currently looks like this for an employee:

export class Employee {
    constructor(
        public firstName: string,
        public lastName: string,
        public email: string,
        public days: string,
        public hours: string, 
    ){}
}

and I want to to place days/hour into their own object, how could that be achieved?

(i.e like..)

public availability: {
        public days: string,
        public hours: string
},

and then would the http get request stay the same like below?

getEmployees() {
      return this.http.get('...')
             .map((response: Response) => {
                 const data = response.json().obj;
                 let objs: any[] = [];

                 for(let i = 0; i < data.length; i++) {
                     let employee = new Employee(
                     data[i].firstName,
                     data[i].lastName, 
                     data[i].email, 
                     data[i].availability.days,
                     data[i].availability.hours
                     );

                     objs.push(employee)
                 }
                 return objs
             })
          }

Just to clarify, I would like my get request to return something like;

var obj = {
    firstName: "test",
    lastName: "test",
    email: "test",
    availability: {
      days: "test",
      hours: "test"
    }
  }

Hope someone can help out! I'm tried to look around, but haven't come across anything that can help.

confusedandenthused
  • 201
  • 2
  • 3
  • 10

3 Answers3

20

Something like this

export class Employee {
    constructor(
        public firstName: string,
        public lastName: string,
        public email: string,
        public availability: Availability // refer to type Availability  below
    ){}
}

export class Availability {
    constructor(
        public days: string,
        public hours: string
    ){}
}

Http get request should stay the same, then change on how you create new instance of employee

let employee = new Employee(
    data[i].firstName,
    data[i].lastName,
    data[i].email,
    new Availability(
          data[i].availability.days,
          data[i].availability.hours
    )
);
Michael
  • 1,692
  • 1
  • 17
  • 19
  • 1
    Hi, I'm getting errors using this example - `Cannot find name 'Availability'.` is what appears in the service, and in the class file - `';' expected`. I think the `export class Availability` won't be visible inside the Employee class unless you import it? – confusedandenthused Nov 16 '16 at 16:37
  • @confusedandenthused yeah sorry, the `Availability` class should have its constructor, I have fixed it, then import `Availability ` class in the `Employee` class and in your service. – Michael Nov 17 '16 at 01:19
  • 1
    Thanks - that got it working! Also worth noting that you have to import the new class into the component to get it to work. – confusedandenthused Nov 17 '16 at 08:39
  • what if the `model` has one hundred property? you can't do it like this. – Lin Du Aug 03 '17 at 13:06
  • 1
    You need to define interfaces instead of classes to work properly. – lentyai Oct 11 '17 at 05:38
3

Personally I did ( for Ionic project ) something like

export class Availability {
    days: string  = "";
    hours: string = "";
}

export class Employee {
    firstName: string = "";
    lastName:  string = "";
    email:     string = "";

    availability = new Availability()
}

So if I use these models in a <form> I receive an empty structure of this Employee class.

It also work when I declare variables like employeeObservable : Observable<Employee[]>; when I query a firebase DB for example ...

Ricky Levi
  • 7,298
  • 1
  • 57
  • 65
2

For anyone showing up to this a couple years later, there's a helpful library that can help with this called class-transformer.

Using this library has been the easiest: https://github.com/typestack/class-transformer

import { Type } from 'class-transformer';

export class Employee {
    firstName: string;
    email: string;
    days: string;
    hours: string;

    @Type(() => Availability)
    availablity: Availablity

    constructor(args: Employee) {
      Object.assign(this, args);
    }
}

export class Availability {
    days: string;
    hours: string;

    constructor(args: Availability) {
      Object.assign(this, args);
    }
}

A few things changed:

  1. The use of the @Type decorator comes from the class-transformer module. This allows you to transform nested objects. Here is the documentation: https://github.com/typestack/class-transformer#working-with-nested-objects
  2. We've added a constructor which allows you to create instances of these Classes and pass through attributes to them of their own respective types. Take a look at this post Converting httpClient answer to model objects [Angular 6] as it shines more light onto whats happening here.

Then within your service this is how your code changes:

import { plainToClass } from 'class-transformer';
import { Employee } from './Employee'

getEmployees() {
  return this.http.get('...')
    .map((response: Response) => plainToClass(Employee, response.json().obj as Employee[]))

plainToClass will take the raw JSON response and transform it into instances of your Employee Class. If you console.log out the result of getEmployees() you will see that it returns an Array of Employees that each have an attribute called availability of type Availability.

jetset
  • 882
  • 1
  • 9
  • 15