2

I have an API, that returns me the Profile info and I am fetching it in my HTTP service. The nested object User has a method getRole(). But when I am trying to call this method, I get

ERROR TypeError: _co.profile.user.getRole is not a function

When I fetch User from my other service, that only returns the User entity, I am able to call this function no problem.

What am I missing ?

Model

export class Profile {
  user: User;
  apiKeys: ApiKey[];
}

export class User {
  user: string;
  firstname: string;
  surname: string;
  email: string;
  role: Role;
  active: boolean;

  getRole() {
    switch (this.role) {
      case Role.READ: {
        return "READ";
      }
      case Role.WRITE: {
        return "WRITE";
      }
      case Role.ADMIN: {
        return "ADMIN";
      }
      default : {
        return "READ";
      }
    }
  }
}

ProfileService.ts

getProfile(): Observable<Profile> {
    return this.http.get<Profile>(this.profileUrl).pipe(catchError(this.handleError));
}

Component.ts

profile: Profile;
isProfileAvailable:boolean = false;

ngOnInit() {
  this.settingsService.getProfile().subscribe(profile => {
    this.profile = profile;
    this.isProfileAvailable = true;
   });
}

Component.html

<p-fieldset [legend]="Profile" *ngIf="isProfileAvailable">
    <div><b>User: </b>{{profile.user.firstname}}</div>
    <div><b>E-mail: </b>{{profile.user.email}}</div>
    <div><b>Role: </b>{{profile.user.getRole()}}</div>
</p-fieldset>
Tomas Lukac
  • 1,923
  • 2
  • 19
  • 37

3 Answers3

2

Calling http.get<Profile> does not automatically initialize an object of type Profile, therefore the property user is also not an object of type User and has not got the method getRole.

You can use Object.assign to initilize objects and it's properties. And because there are two class types (Profile, User), both can extend from a Base-Class to handle the assignation of the properties for both types.

Models:

// Use a Base class to assign all properties with objects both of type Profile and User
class Base {
    constructor(properties?: any) {
        (Object as any).assign(this, properties);
    }
}

export class User extends Base {
    user: string;
    firstname: string;
    surname: string;
    email: string;
    role: Role;
    active: boolean;

    getRole() {
        switch (this.role) {
            case Role.READ: {
                return "READ";
            }
            case Role.WRITE: {
                return "WRITE";
            }
            case Role.ADMIN: {
                return "ADMIN";
            }
            default : {
                return "READ";
            }
        }
    }  
}

export class Profile extends Base {
    user: User;
    apiKeys: ApiKey[];

    // take advantage of Object.assign
    // to initialize an instance of type Profile 
    // along with it's property user
    constructor(properties?: any) {
        super(properties);
        this.user = new User(properties.user);
    }
}

ProfileService.ts

getProfile(): Observable <Profile> {
    return this.http.get<Profile>(this.profileUrl).pipe(catchError(this.handleError));
}

Component.ts

profile: Profile;
isProfileAvailable:boolean = false;

ngOnInit()
{
    this.settingsService.getProfile().subscribe(profileData => {
        // assuming that profileData looks like this:
        /*
        {
            user: {
                user: 'user',
                firstname: 'firstname',
                surname: 'surname',
                email: 'email',
                active: true
            }
        }
        */
        this.profile = new Profile(profileData);
        this.isProfileAvailable = true;
    });
}
Blauharley
  • 4,186
  • 6
  • 28
  • 47
1

Blauharley answer worked like a charm.

However, since I have all my models in separate files, I had to export the Base class:

export class Base {
    constructor(properties?: any) {
        (Object as any).assign(this, properties);
    }
}

Everything works as expected.

Tomas Lukac
  • 1,923
  • 2
  • 19
  • 37
0

You are not currently creating instances of your class, so the class method getRole() is not available. With this.http.get<Profile> you are just telling the compiler that what you expect from the API conforms to your class, it doesn't actually create instances of your class or subclass User.

You need to add a constructor to your classes with the properties and actually create instances of your class. Here's a simplified example:

export class User {
  role: string;

  constructor(role: string) {
    this.role = role;
  }

  getRole() {
    switch (this.role) {
      case 'READ': {
        return "READ";
      }
      case 'WRITE': {
        return "WRITE";
      }
      default: {
        return "READ";
      }
    }
  }
}

Then need to create an instance of your class to have access to class methods:

ngOnInit() {
  let user = new User('WRITE');
  console.log(user.getRole());
}

STACKBLITZ

Furthermore, if this would be the only method in your class that you need, I would just suggest to use interfaces instead and write a standalone function that checks the role instead of going through the hassle of creating instances of class. This is a very opinionated thing though, so if you want to use classes and class specific methods, you do need to go the above route. I just like to use interfaces personally :)

AT82
  • 71,416
  • 24
  • 140
  • 167