Проблема отслеживания вложенной структуры

Я вставляю компонент Glimmer в topic-above-footer-buttons. Этот компонент Glimmer пытается отобразить некоторые метаданные о теме. Я сериализую их в topic.my_metadata.

Когда метаданные обновляются из фоновой задачи (!), тема обновляется через MessageBus.publish("/topic/#{topic.id}", { reload_topic: true, refresh_stream: true }).

Теперь это правильно обновляется в шаблоне:

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

Однако, когда я обращаюсь к нему через геттер

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

то

{{ this.statusMessage }}

не обновляется.

(То же самое относится ко всем геттерам, которые я использую и которые так или иначе зависят от this.args.topic.my_metadata. Это и есть реальная проблема).

Что я делаю не так?

Я уже перепробовал многое, включая определение пользовательских событий, копирование topic.my_metadata в @tracked metadata и последующее использование this.metadata и так далее.

Я считаю, что гарантированная повторная оценка геттера происходит только в том случае, если отслеживается одно из зависимых значений (и оно изменяется).

Поэтому в данном случае, думаю, лучше использовать {{this.args.topic.my_metadata.status}}, хотя, кстати, писать это следует так: {{@topic.my_metadata.status}}.

Что ж, дело не в том, «лучше» это или нет. В JavaScript есть логика оценки метаданных, например, для решения о том, следует ли отображать кнопку, как в этом примере:

get showButton() {
   // выполняем тяжелые операции с this.args.topic.my_metadata.foo и this.args.topic.my_metadata.bar
}

Поэтому мне нужны геттеры, и мне нужно, чтобы они как-то переоценивались.

Кроме того, в документации по Glimmer сказано, что args и их значения отслеживаются автоматически.

Если шаблон может автоматически отслеживать их, почему геттер не может?

Даже это работает — передача метаданных в качестве аргумента:

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

с

  getStatus(arg) {
    return arg.status;
  }

Это неприятная особенность нашего перехода от классической системы реактивности Ember (get/set/computed) к системе Octane с использованием @tracked и нативных геттеров.

Как обсуждалось выше, проблема в вашем примере заключается в том, что вы обращаетесь к свойству, не помеченному как @tracked, из нативного геттера.

Прямой доступ к полному пути из шаблона будет работать, хотя, как вы верно заметили, это ограничивает доступную логику.

В качестве альтернативы использование .get() позволит новой системе автотрекинга Ember работать с классическими (то есть не помеченными как @tracked) свойствами.

Таким образом, в данном случае код будет выглядеть так:

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

Спасибо, Дэвид.

Ты мог бы также решить это с помощью @computed, или это плохая идея?

  @computed('args.topic.my_metadata.status')
  get statusMessage() {
    // здесь логика может быть гораздо сложнее
    return this.args.topic.my_metadata.status;
  }

Полагаю, это дало бы преимущество кэширования результата, если вычисления значительны.

(очевидно, тривиально в этом примере, но то, на что намекал @RGJ)

@computed является частью классической системы реактивности Ember. Она официально не устарела, но мы стараемся избегать её использования в новом коде.

Если ваш геттер достаточно сложен для кэширования, вы можете использовать @cached:

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

...

@cached
get statusMessage() {
  // это может быть гораздо сложнее
  return this.args.topic.my_metadata.status;
}

Это работает как по волшебству. Спасибо, что спасли остаток моей пятницы, @david!!! Очень признателен!!