Problem tracking nested structure

I’m inserting a Glimmer component in topic-above-footer-buttons. This Glimmer component attempts to show some metadata about the topic. I am serializing these to topic.my_metadata

When the metadata is updated from a background job(!), the topic refreshes via MessageBus.publish("/topic/#{topic.id}", { reload_topic: true, refresh_stream: true })

Now this correctly updates in the template:

{{this.args.topic.my_metadata.status}}

However, when I access it via a getter

get statusMessage() {
  return this.args.topic.my_metadata.status;
}

then

{{ this.statusMessage }}

does not update.

(And the same goes for all getters that I have that depend on this.args.topic.my_metadata in any way. Which is the actual problem).

What am I doing wrong?

I’ve already tried a lot, including defining custom events, copying topic.my_metadata to a @tracked metadata and then using this.metadata, etcetera.

1 Like

I think you are only guaranteed a getter will be re-evaluated if one of the values it relies on is tracked (and that changes)

so in this case I think {{this.args.topic.my_metadata.status}} is better, though btw you should write it like: {{@topic.my_metadata.status}}

2 Likes

Well, it’s not a matter of “better” or not, there is a lot of logic in Javascript evaluating the metadata and for instance deciding of a button should be shown, like

get showButton() {
   // do some heavy stuff on this.args.topic.my_metadata.foo and this.args.topic.my_metadata.bar
}

so I need the getters and I need them to reevaluate somehow.

Additionally, the Glimmer documentation says args and its values are automatically tracked.

If the template is able to autotrack them, why can’t the getter?

Even this works, passing the metadata as an argument

{{this.getStatus this.args.topic.my_metadata}}

with

  getStatus(arg) {
    return arg.status;
  }

This is an unfortunate part of us being midway-through converting from Ember’s ‘classic’ reactivity system (get/set/computed) to the ‘octane’ @tracked / native-getters.

So as discussed above, the problem with your example is that you’re accessing a non-@tracked property from a native getter.

Accessing the full path directly from a template will work, although as you say, it limits the logic available.

Alternatively, using .get() will allow Ember’s new autotracking system to work against classic (i.e. non-@tracked) properties.

So in this case, it would be

get statusMessage() {
  return this.args.topic.get("my_metadata.status");
}
4 Likes

thanks David

could you also solve this with @computed or is that a no-no?

  @computed('args.topic.my_metadata.status')
  get statusMessage() {
    // this might be a lot more complicated
    return this.args.topic.my_metadata.status;
  }

I believe this would have the benefit of caching the result if the computation was significant.

(obviously trivial in this example but something @RGJ alluded to)

@computed is part of Ember’s classic reactivity system. It’s not officially deprecated, but we try to avoid adding it to new code.

If your getter is complicated enough to warrant caching, then you can use @cached:

import { cached } from "@glimmer/tracking";

...

@cached
get statusMessage() {
  // this might be a lot more complicated
  return this.args.topic.my_metadata.status;
}
1 Like

This works like a charm. Thank you for saving the remainder of my Friday @david !!! Much appreciated!!

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.