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!

2 Mi Piace

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.

4 Mi Piace

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:

2 Mi Piace

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.

3 Mi Piace

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.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.