Search code examples
angularangular-httpngoninit

Angular 8 getRole is not a function in nested object


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>

Solution

  • 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;
        });
    }