3

I am trying to map the json returned from the backend to proper typescript classes on the angular side.

Here is my backend code:

findMessagesWithOtherUserAccount(otherId:Number):Observable<Message[]> {
    return this.http.get('/api/message/find-messages-with-other-useraccount/' + otherId)
        .map(this.extractMessages);
}

private extractMessages(res:Response) {
    let body = res.json();
    return body || {};
}

Notice the Observable of Message. Here Message is the type I need because I have added business logic to the class as follows:

import {UserAccount} from "../useraccount/useraccount.model";

export class Message {

    constructor(id:number,
                sender:UserAccount,
                recipient:UserAccount,
                sendDate:Date,
                messageRead:boolean,
                text:string) {
        this.id = id;
        this.sender = sender;
        this.recipient = recipient;
        this.sendDate = sendDate;
        this.messageRead = messageRead;
        this.text = text;
    }

    id:number;
    sender:UserAccount;
    recipient:UserAccount;
    sendDate:Date;
    messageRead:boolean;
    text:string;

    getCounterparty(user:UserAccount):UserAccount {
        if (!this.sender) return null;
        return (user.id !== this.sender.id) ? this.sender : this.recipient;
    }

    isSender(user:UserAccount):boolean {
        return user.id === this.sender.id;
    }

    isRecipient(user:UserAccount):boolean {
        return user.id === this.recipient.id;
    }

    isNew(user:UserAccount):boolean {
        return !this.messageRead && this.isRecipient(user);
    }
}

I try to refer to the isRecipient method from a component:

   getSenderFirstName(message:Message):string {
        if (message.isRecipient(this.currentUserAccount)) {
            return this.otherUserAccount.firstName;
        }
        return 'Moi';//FIXME: i18n/translate
    }

However, I get this error:

browser_adapter.js:81 TypeError: message.isRecipient is not a function
    at MessageConversationComponent.getSenderFirstName (message-conversation.component.js:50)

Indicating that message is not typed (other than a plain js object)...

Here is the full component:

export class MessageConversationComponent implements OnInit {

    messagesWithOtherUserAccount:Message[];
    currentUserAccount:UserAccount;
    otherUserAccount:UserAccount;

    constructor(private messageService:MessageService,
                private userAccountService:UserAccountService,
                private routeSegment:RouteSegment) {
    }

    ngOnInit() {
        this.messageService.findMessagesWithOtherUserAccount(2)
            .subscribe(param=>this.messagesWithOtherUserAccount = param);
        this.userAccountService.retrieveOtherUserAccount(2)
            .subscribe(param=> this.otherUserAccount = param); 
   this.userAccountService.currentUserAccount$.subscribe(param=>this.currentUserAccount = param);
    }

    getSenderFirstName(message:Message):string {
        if (message.isRecipient(this.currentUserAccount)) {
            return this.otherUserAccount.firstName;
        }
        return 'Moi';//FIXME: i18n/translate
    }
}

and the template:

            <div *ngFor="let message of messagesWithOtherUserAccount" class="media col-xs-12">
                <div class="media-body Lui" ng-class="getMessageClasses(message)">
                    <div class="media-heading">
                        {{getSenderFirstName(message)}} <small><span am-time-ago="message.sendDate"></span></small>
                    </div>
                    <p class="message-text">{{message.text}}</p>
                </div>
            </div>
balteo
  • 23,602
  • 63
  • 219
  • 412
  • Typescript doesn't provide any extra methods to convert a plain object to a class instance. You should define a interface for `Message` which only contains plain properties. – Zen May 21 '16 at 16:56
  • Marking the return type of your function to be of a TypeScript type won't make the actual object be an instance of that type. The object you receive doesn't have the prototypical functions for the class Message you defined – Bruno Garcia May 21 '16 at 16:59
  • Possible duplicate of [How do I cast a JSON object to a typescript class](http://stackoverflow.com/questions/22875636/how-do-i-cast-a-json-object-to-a-typescript-class) – Mark Rajcok May 21 '16 at 17:52

1 Answers1

3

You can't just cast JSON to a class instance. If you actually need a class with methods you need to create it with new SomeClass() like:

private extractMessages(res:Response) {
    let body = res.json();

    if(body) {
      return new Message(
        body.id,
        new UserAccount(body.sender),
        new UserAccount(body.recipient),
        new Date(body.sendDate),
        body.messageRead,
        body.text);
    } else {
      return new Message(
    }
}

If you only want typed access to properties you can use an interface instead of a class and cast to this interface instead to get autocompletion and static type checking.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567