-1

I have this code. Notice that the serialization is simply renaming the template_items property to template_items_attributes:

export class Template {
  constructor(
  ) {}

  public id: string
  public account_id: string
  public name: string
  public title: string
  public info: string
  public template_items: Array<TemplateItem>

  toJSON(): ITemplateSerialized {
    return {
      id: this.id,
      account_id: this.account_id,
      name: this.name,
      title: this.title,
      info: this.info,
      template_items_attributes: this.template_items
    }
  }
}


export interface ITemplateSerialized {
  id: string,
  account_id: string,
  name: string,
  title: string,
  info: string,
  template_items_attributes: Array<TemplateItem>
}

Creating an object locally works fine and stringify calls the toJSON() method.

However, once I send that object to the API:

  private newTemplate(name: string): Template {
    let template = new Template();
    template.name = name;
    template.account_id = this._userService.user.account_id;
    // next 5 lines are for testing that toJSON() is called on new obj
    let item = new TemplateItem();
    item.content = "Test"
    template.template_items.push(item);
    let result = JSON.stringify(template);
    console.log('ready', result); // SHOWS the property changes
    return template;
  }

  postTemplate(name: string): Observable<any> {
    return this._authService.post('templates', JSON.stringify(this.newTemplate(name)))
      .map((response) => {
        return response.json();
      });
  }

It is saved and returned, but from that point on when I stringify and save again it does NOT call toJSON().

  patchTemplate(template: Template): Observable<any> {
    console.log('patching', JSON.stringify(template)); // DOES NOT CHANGE!
    return this._authService.patch('templates' + `/${template.id}`, JSON.stringify(template))
      .map((response) => {
        return response.json();
      });
  }

Why does toJSON() only work on new objects?

rmcsharry
  • 5,363
  • 6
  • 65
  • 108
  • 1
    Post the code hidden behind "once I send that object to the API, save and return it, from that point on when I stringify and save again it does NOT call toJSON()". – JB Nizet Oct 21 '17 at 12:10
  • 2
    At a guess, because you're not actually converting the API response back into a `Template` instance, leaving at as a regular object that has the right *properties* but none of the *methods*. – jonrsharpe Oct 21 '17 at 12:16
  • For those who want to understand this more, it seems the way to go is to use Decorators and Reflectors. Full discussion here: https://stackoverflow.com/questions/22885995/how-do-i-initialize-a-typescript-object-with-a-json-object – rmcsharry Oct 21 '17 at 12:35
  • 1
    Related: https://stackoverflow.com/q/45844055/3001761, https://stackoverflow.com/q/46839214/3001761, https://stackoverflow.com/q/46717706/3001761. It seems to be quite a common misconception. – jonrsharpe Oct 21 '17 at 12:42
  • There is an experimental feature in Typescript for this here: https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Decorators.md – rmcsharry Oct 21 '17 at 12:44

1 Answers1

1

In fact, your question has nothing to do with Angular or Typescript, it's just some JavaScript and the logic of how serialization work and why do we serialize objects.

I send that object to the API, save and return it

When you return an "object" from an API, you're returning a string which you parse as a JSON serialized object. Then you get a plain JavaScript object, not an instance of your class.

Object prototype in JavaScript does not have toJSON method, and even if it had, it's not the method you've written inside the Template class, so it won't be called.

You don't even need a server call to replicate this, just do

const obj = JSON.parse(JSON.stringify(new Template()))
obj.toJSON // undefined

And you'll see that obj is not an instance of Template. It's simply an object which simply happens to have all the fields as your original object made as a Template instance, but it's not an instance of that class.

Lazar Ljubenović
  • 18,976
  • 10
  • 56
  • 91
  • Thanks. Stupid me thought classes in Typescript would behave like C# or Java classes. I just read some blogs, now I understand. Thanks for the explanation. – rmcsharry Oct 21 '17 at 12:21