Emberjs: Use args as tracked property

Viewed 877

I am doing a store query in the controller and then passing the result down to a child component.

// Controller
@tracked comment;

@action
async fetchComment() {
  const comment = await this.store.query('note', {
    filter: {
      listing: this.listing.id,
    },
  });

  if (comment && comment.length > 0) {
    this.comment = comment.firstObject;
  }
}


// Template
<MyComponent @comment={{this.comment}} />

I want to use the arg to populate a tracked property in the child component.

// Component
@tracked comment = this.args.comment;

I found this does not work but works well as a getter. However with a getter I am unable to set this to null when deleting that record.

I have also tried to set the tracked property in the Constructor but that didnt work either.

I suspect this has something to do with passing in a promise or store object in the args because this works fine with static data.

1 Answers

why your code does not work

this code can not work:

@tracked comment = this.args.comment;

this is because comment on the controller is initially undefined but will later bet set to comment.firstObject when the network request is done and the await in your fetchComment function returns. Generally everythings on args basically always behaves like its @tracked (while more accurate you would describe it as getters). So this usually will just update fine. But the assignment @tracked comment = this.args.comment; only happens once when you create the component, so you no longer depend on updates on args.

why you can not set this.args.comment to null

If you use a getter or directly always use this.args.comment you can not change this reference. This is because this.args is always readonly. you can change objects on this.args.something, but you never can change the reference or primitive value on this.args.

Sidenote: this is only true if the component was called with <AngleBracket /> syntax. For the older {{curly-component}} syntax this is not true. So this does not depend on the component itself but how the component gets called.

you could notify the controller to remove the reference

one good thing to do is to pass down a deleteComment action to the component that basically does something like this.comment = null on the controller. then you use this.args.comment directly or by a getter and you can call this.args.deleteComment() to set comment on the controller to null, which will update anything that uses this.args.comment or a getter that returns this.args.comment.

this is essentially because in your architecture the controller owns the data (because it loads it). so the controller is also responsible to delete it.

if you use ember-data you can check isDeleted

if its a ember-data model (which it probably is if you call this.store) then it has a isDeleted property. you can use this to check if the record is deleted, since ember-data records dont disappear if they get deleted. which is exactly because of problems like this.

how you use another property to shadow a argument

you could do something like this to shadow an argument in your component:

@tracked commentIsDeleted = false;
get comment() {
  return this.commentIsDeleted
    ? null
    : this.args.comment;
}

this way at first this.comment will work like a normal getter, but you can shadow delete it by setting this.commentIsDeleted = true;. From that on this.comment will be null.

Related