How to handle POJO's, regarding tracking, properly?

Please consider following use-case and please let me know your thoughts :grinning:

//routes/user-edit.js
export default class EditUserRoute extends Route {
  model() {
    // i.e. GraphQL Request, which returns a POJO
    return {
      firstName: 'Michael',
      lastName: 'Knight',
      address: {
        street: 'Schoolstreet 22',
        town: 'New York'
      }
    };
  }
}
//templates/user-edit.js
<UserEdit @user={{this.model}} />
//components/user-edit.js
export default class UserEditComponent extends Component {
  @tracked user = null; // I think this tracked is for nothing, isn't it?

  constructor() {
    super(...arguments);
    this.user = this.args.user;
  }

  @action updateFirstName({ target }) {
     this.user.firstName = target.value; // I know, that {{user.firstName}} is not rerendered if this action is triggered. I just want to know, what's your recommended solution to this issue. Please see below.
   }
}
{{!-- ... --}}
<input placeholder="Firstname" type="text" value={{this.user.firstName}} {{on "input" this.updateFirstName}}>
{{!-- ... --}}
<input placeholder="Street" type="text" value={{this.user.address.street}} {{on "input" this.updateStreet}}>
{{!-- ... --}}
{{user.firstName}}
<br>
{{user.lastName}}
<br>
{{user.address.street}}

Solutions:

  1. Convert the POJO from the model hook into a native Class.
class Address {
  @tracked street;
  @tracked town;

  constructor({ street, town }) {
    this.street = street;
    this.town = town;
  }
}
class User {
  @tracked firstName;
  @tracked lastName;

  constructor({ firstName, lastName, address }) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.address = new Address(address);
  }
}
//routes/user-edit.js
export default class EditUserRoute extends Route {
  model() {
    // i.e. GraphQL Request, which returns a POJO
    return new User({
      firstName: 'Michael',
      lastName: 'Knight',
      address: {
        street: 'Schoolstreet 22',
        town: 'New York'
      }
    });
  }
}
  1. Keep the POJO and use { set } from '@ember/object'
  @action updateFirstName({ target }) { 
     set(this.user, 'firstName', target.value);
  }

For me 1. seems a bit costly. But what is better for the long run? Thanks

I would either:

  • use TrackedObject from https://github.com/pzuraq/tracked-built-ins

  • or use a more functional style that avoids mutation:

    @action updateFirstName({ target }) {
      this.user = { firstName: target.value, ...this.user};
    }
    

Also, yes, this.user = this.args.user doesn’t appear to be doing anything useful in this example. You can use this.args.user directly, it’s already tracked, and from the template you can access it like {{@user.firstName}} But if you do that, you can’t replace it like my second option above.

1 Like

@ef4 Thank you. I like your second option, the functional approach. It seems more native and reminds me of React.js :grinning:.

best regards