Rilevamento modifiche componente Glimmer non funzionante - Mutazione proprietà in array @tracked

Ciao a tutti!

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:

  1. L’handler del click viene eseguito correttamente
  2. La proprietà \item.expanded\\ viene aggiornata (verificato nella console)
  3. Ma il template non si ri-renderizza: nessuna modifica alla classe, nessun cambiamento di testo

Cosa ho provato:

  1. Ri-assegnazione dell’array: \this.items = [...this.items]\\ dopo la mutazione (non funziona)
  2. Immer per aggiornamenti immutabili: Volevo provare questo, ma non riesco a far funzionare le importazioni npm nei temi di Discourse
  3. 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?

Grazie!

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.

Ciao Alteras,

Grazie per il tuo suggerimento!!! Ha funzionato come suggerito dal tuo approccio e ci ha davvero reso la giornata un po’ più facile ::)** :grinning_face:

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.