Estou tendo problemas com a detecção de alterações em um componente Glimmer e gostaria de alguma orientação. Quando eu muto propriedades em objetos dentro de um array \@tracked\\, o template não é renderizado novamente.
\\Minha Configuração:\\
```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
}
// ... mais itens
];
@action
toggleExpanded(item) {
item.expanded = !item.expanded; // Esta mutação não aciona a nova renderização
console.log('Toggled:', item.label, item.expanded); // Registra corretamente
}
<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>
}
```
\\O Problema:\\
O manipulador de clique executa corretamente
A propriedade \item.expanded\\ é atualizada (verificada no console)
Mas o template não é renderizado novamente - nenhuma alteração de classe, nenhuma alteração de texto
\\O que eu Tentei:\\
\\Reatribuição de array\\ - \this.items = \\[...this.items\\]\\ após a mutação (não funciona)
\\Immer para atualizações imutáveis\\ - Queria tentar isso, mas não consigo fazer as importações npm funcionarem em temas do Discourse
\\Diferentes estratégias de chave\\ - \key="id"\\ em vez de \key="@index"\\
\\Perguntas:\\
Mutar propriedades em objetos dentro de arrays \@tracked\\ deveria funcionar no Glimmer?
Preciso rastrear objetos individuais de alguma forma?
Existe um padrão recomendado para este caso de uso?
Isso pode estar relacionado à execução em um ambiente de tema do Discourse?
Existem limitações com importações npm em temas do Discourse que afetam bibliotecas de reatividade?
\\Ambiente:\\
Componente de tema do Discourse
Componentes Glimmer com arquivos \.gjs\\
Versão mais recente do Discourse - atualizada hoje
Qualquer insight seria muito apreciado! Há algo fundamental que estou perdendo sobre o sistema de reatividade do Glimmer?
Hmm, isso é estranho. Fazer o truque de atribuição de array geralmente funciona.
Uma alternativa que encontrei é fazer com que os objetos no array sejam de uma classe com suas próprias propriedades rastreadas. Algo como:
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'),
...
];
// resto do seu código
}
Pode ser mais verboso do que criar um monte de objetos simples, mas descobri que facilita a extensão e a compreensão, especialmente se você precisar fazer algo como passar os dados para componentes aninhados.
Acredito que, ao rastrear um array como descrito no OP, você está rastreando a referência do array e não as alterações nos objetos individuais dentro dele.
Outra maneira de lidar com isso é usar trackedObject. Usamos isso em alguns lugares no Discourse.
Obrigado pela sua contribuição. Tenho mais algumas perguntas:
Usar uma biblioteca como o Immer funcionaria?
Se sim, como devo incluir o Immer no Discourse?
Vi menções de ‘copiá-lo para os assets a partir do node_modules’, mas eu preferiria se houvesse uma maneira de fazer isso via npm, ou mesmo um link CDN na tag do cabeçalho.
Você pode usar loadScript() para carregar JS, mesmo que a URL fornecida seja uma URL externa. Usar serviços externos como jsdelivr deve funcionar com isso.
import loadScript from "discourse/lib/load-script";
...
loadScript(URL).then(() => {
// seu código
})
Embora eu não tenha testado extensivamente, um import dinâmico também deve funcionar.
async function load() {
const {
default: myDefault,
foo,
bar,
} = await import(URL);
// resto do código
}
Devo notar que é provavelmente mais seguro salvar os arquivos NPM diretamente em seu código em vez de depender de uma CDN externa, dependendo de quão crítico o JS é. Da mesma forma, você pode precisar fazer um pouco de empacotamento e pré-processamento antes de poder usá-lo no navegador.
Quanto a saber se o Immer funcionará com o discourse, eu não sei.