Ho problemi con il rilevamento delle modifiche in un componente Glimmer e avrei bisogno di qualche consiglio. Quando modifico le proprietà sugli oggetti all’interno di un array \@tracked\\, il template non si ri-renderizza.
Il mio setup:
```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
}
// ... altri elementi
];
@action
toggleExpanded(item) {
item.expanded = !item.expanded; // Questa mutazione non innesca il re-render
console.log('Toggled:', item.label, item.expanded); // Registra correttamente
}
<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>
}
```
Il problema:
L’handler del click viene eseguito correttamente
La proprietà \item.expanded\\ viene aggiornata (verificato nella console)
Ma il template non si ri-renderizza: nessuna modifica alla classe, nessun cambiamento di testo
Cosa ho provato:
Ri-assegnazione dell’array: \this.items = [...this.items]\\ dopo la mutazione (non funziona)
Immer per aggiornamenti immutabili: Volevo provare questo, ma non riesco a far funzionare le importazioni npm nei temi di Discourse
Diverse strategie di chiave: \key="id"\\ invece di \key="@index"\\
Domande:
La mutazione delle proprietà sugli oggetti all’interno degli array \@tracked\\ dovrebbe funzionare in Glimmer?
Devo rendere tracciabili i singoli oggetti in qualche modo?
Esiste un pattern consigliato per questo caso d’uso?
Potrebbe essere correlato all’esecuzione in un ambiente a tema Discourse?
Ci sono limitazioni con le importazioni npm nei temi Discourse che influiscono sulle librerie di reattività?
Ambiente:
Componente del tema Discourse
Componenti Glimmer con file \.gjs\\
Ultima versione di Discourse - aggiornata oggi
Qualsiasi suggerimento sarebbe molto apprezzato! C’è qualcosa di fondamentale che mi sfugge nel sistema di reattività di Glimmer?
Hmm, questo è strano. Fare il trucco dell’assegnazione dell’array di solito funziona.
Un’alternativa che ho trovato è fare in modo che gli oggetti nell’array provengano da una classe con le proprie proprietà tracciate. Qualcosa come:
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 del tuo codice
}
Può essere più verboso che creare un mucchio di oggetti semplici, ma ho scoperto che rende più facile estendere e ragionare attorno, specialmente se hai bisogno di fare qualcosa come passare i dati a componenti annidati.
Credo che quando si traccia un array come descritto nell’OP, si stia tracciando il riferimento all’array e non le modifiche ai singoli oggetti al suo interno.
Un altro modo per gestirlo è usare trackedObject, lo usiamo in diversi punti di Discourse.
Grazie per il tuo contributo. Ho altre domande di follow-up:
L’utilizzo di una libreria come immer funzionerebbe?
Se sì, come dovrei includere immer in discourse?
Ho visto menzioni di “copiarlo in assets da node_modules”, ma preferirei se ci fosse un modo per farlo tramite npm, o anche un link CDN nel tag header.
Puoi usare loadScript() per caricare JS, anche se l’URL fornito è un URL esterno. L’uso di servizi esterni come jsdelivr dovrebbe funzionare con questo.
import loadScript from "discourse/lib/load-script";
...
loadScript(URL).then(() => {
// il tuo codice
})
Anche se non l’ho testato approfonditamente, anche un import dinamico dovrebbe funzionare.
async function load() {
const {
default: myDefault,
foo,
bar,
} = await import(URL);
// resto del codice
}
Devo notare che è probabilmente più sicuro salvare i file NPM direttamente nel tuo codice invece di fare affidamento su una CDN esterna, a seconda di quanto sia critico il JS. Allo stesso modo, potresti dover fare un po’ di bundling e pre-elaborazione prima di poterlo utilizzare nel browser.
Per quanto riguarda se Immer funzionerà con discourse, non lo so.