У меня возникли проблемы с обнаружением изменений в компоненте Glimmer, и я был бы признателен за помощь. При изменении свойств объектов внутри массива, помеченного как @tracked, шаблон не перерисовывается.
Моя конфигурация:
```javascript
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { fn } from '@ember/helper';
import { on } from '@ember/modifier';
import { action } from '@ember/object';
export default class CustomSidebarComponent extends Component {
@tracked items = [
{
id: 'home',
label: 'Home',
expanded: false
},
{
id: 'my-posts',
label: 'My Posts',
expanded: false
}
// ... больше элементов
];
@action
toggleExpanded(item) {
item.expanded = !item.expanded; // Это изменение не вызывает перерисовку
console.log('Переключено:', item.label, item.expanded); // Выводится корректно
}
<template>
<div id="custom-sidebar">
<ul>
{{#each this.items key="@index" as |item|}}
<li class="menu-item {{if item.expanded 'expanded'}}" {{on "click" (fn this.toggleExpanded item)}}>
{{item.label}} {{if item.expanded "(expanded)" ""}}
</li>
{{/each}}
</ul>
</div>
</template>
}
```
Проблема:
Обработчик клика выполняется корректно
Свойство item.expanded обновляется (проверено в консоли)
Но шаблон не перерисовывается — классы и текст не меняются
Что я уже пробовал:
Переназначение массива — this.items = [...this.items] после изменения (не помогло)
Использование Immer для неизменяемых обновлений — хотел попробовать, но не могу настроить импорты npm в темах Discourse
Разные стратегии ключей — key="id" вместо key="@index"
Вопросы:
Должно ли изменение свойств объектов внутри массивов, помеченных как @tracked, работать в Glimmer?
Нужно ли каким-то образом помечать отдельные объекты как отслеживаемые?
Есть ли рекомендуемый паттерн для этого случая?
Может ли это быть связано с запуском в среде темы Discourse?
Существуют ли ограничения импортов npm в темах Discourse, влияющие на реактивные библиотеки?
Окружение:
Компонент темы Discourse
Компоненты Glimmer с файлами .gjs
Последняя версия Discourse — обновлена сегодня
Буду очень признателен за любые подсказки! Не упускаю ли я что-то фундаментальное в системе реактивности Glimmer?
Хм, это странно. Обычно трюк с присваиванием массива работает.
Альтернатива, которую я нашел, — сделать объекты в массиве экземплярами класса с собственными отслеживаемыми свойствами. Например:
class CustomSidebarItem {
@tracked expanded = false;
constructor(id, label) {
this.id = id;
this.label = label;
}
}
export default class CustomSidebarComponent extends Component {
@tracked items = [
new CustomSidebarItem('home', 'Home'),
new CustomSidebarItem('my-posts', 'My Posts'),
...
];
// остальной код
}
Это может быть более многословно, чем создание множества простых объектов, но, как я заметил, так проще расширять функционал и понимать логику, особенно если нужно передавать данные во вложенные компоненты.
Я полагаю, что когда вы отслеживаете массив, как описано в первом сообщении, вы отслеживаете ссылку на массив, а не изменения отдельных объектов внутри него.
Другой способ решения — использовать trackedObject. Мы применяем его в нескольких местах в Discourse:
Спасибо за ваш вклад. У меня есть ещё один уточняющий вопрос:
Сработает ли использование библиотеки вроде Immer?
Если да, то как следует подключить Immer в Discourse?
Я видел упоминания о том, чтобы «скопировать её из node_modules в assets», но я бы предпочёл вариант с использованием npm или даже ссылки на CDN в теге header.
Вы можете использовать loadScript() для загрузки JS, даже если предоставленный URL является внешним. Использование внешних сервисов, таких как jsdelivr, должно работать с этим.
import loadScript from "discourse/lib/load-script";
...
loadScript(URL).then(() => {
// ваш код
})
Хотя я не тестировал это extensively, динамический импорт также должен работать.
async function load() {
const {
default: myDefault,
foo,
bar,
} = await import(URL);
// остальной код
}
Стоит отметить, что, вероятно, безопаснее сохранять файлы NPM напрямую в ваш код, вместо того чтобы полагаться на внешний CDN, в зависимости от того, насколько критичен JS. Аналогично, вам может потребоваться выполнить некоторую сборку и предварительную обработку, прежде чем использовать его в браузере.
Что касается того, будет ли Immer работать с Discourse, я не знаю.