Détection de changement de composant Glimmer ne fonctionne pas - Modification des propriétés dans le tableau @tracked

Salut à tous !

J’ai des problèmes avec la détection de changement dans un composant Glimmer et j’aurais besoin de quelques conseils. Lorsque je mute des propriétés sur des objets dans un tableau \@tracked, le template ne se ré-affiche pas.

Mon Setup :

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
    }
    // ... more items
  ];

  @action
  toggleExpanded(item) {
    item.expanded = !item.expanded; // Cette mutation ne déclenche pas de ré-affichage
    console.log('Toggled:', item.label, item.expanded); // S'affiche correctement
  }

  <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>
}

Le Problème :

  1. Le gestionnaire de clic s’exécute correctement
  2. La propriété item.expanded est mise à jour (vérifié dans la console)
  3. Mais le template ne se ré-affiche pas - aucun changement de classe, aucun changement de texte

Ce que j’ai essayé :

  1. Réaffectation du tableau - this.items = [...this.items] après la mutation (ne fonctionne pas)
  2. Immer pour les mises à jour immuables - Je voulais essayer cela mais je n’arrive pas à faire fonctionner les imports npm dans les thèmes Discourse
  3. Stratégies de clé différentes - key="id" au lieu de key="@index"

Questions :

  • La mutation des propriétés sur les objets dans les tableaux @tracked est-elle censée fonctionner dans Glimmer ?
  • Dois-je rendre les objets individuels traçables d’une manière ou d’une autre ?
  • Existe-t-il un modèle recommandé pour ce cas d’utilisation ?
  • Cela pourrait-il être lié à l’exécution dans un environnement de thème Discourse ?
  • Y a-t-il des limitations avec les imports npm dans les thèmes Discourse qui affectent les bibliothèques de réactivité ?

Environnement :

  • Composant de thème Discourse
  • Composants Glimmer avec des fichiers .gjs
  • Dernière version de Discourse - mise à jour aujourd’hui

Toute aide serait grandement appréciée ! Y a-t-il quelque chose de fondamental que je manque dans le système de réactivité de Glimmer ?

Merci !

2 « J'aime »

Hmm, c’est étrange. L’astuce d’assignation de tableau fonctionne généralement.

Une alternative que j’ai trouvée consiste à faire en sorte que les objets du tableau proviennent d’une classe avec ses propres propriétés suivies. Quelque chose comme :

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'),
    ...
  ];
  // reste de votre code
}

C’est peut-être plus verbeux que de créer un tas d’objets simples, mais j’ai constaté que cela rendait l’extension et le raisonnement plus faciles, surtout si vous avez besoin de faire quelque chose comme passer les données à des composants imbriqués.

4 « J'aime »

Salut Alteras,

Merci pour votre suggestion !!! Cela a fonctionné comme vous l’avez suggéré et nous a vraiment facilité la tâche ::)** :grinning_face:

2 « J'aime »

Je pense que lorsque vous suivez un tableau comme décrit dans OP, vous suivez la référence du tableau et non les modifications apportées aux objets individuels dans le tableau.

Une autre façon de le gérer est d’utiliser trackedObject, nous l’utilisons dans plusieurs endroits de Discourse.

3 « J'aime »

Merci pour votre contribution. J’ai une autre question de suivi :

Est-ce que l’utilisation d’une bibliothèque comme immer fonctionnerait ?
Si oui, comment devrais-je inclure immer dans discourse ?
J’ai vu des mentions de « le copier dans les assets depuis node_modules », mais je préférerais s’il existe un moyen de le faire via npm, ou même un lien CDN dans la balise d’en-tête.

Vous pouvez utiliser loadScript() pour charger du JS, même si l’URL fournie est une URL externe. L’utilisation de services externes comme jsdelivr devrait fonctionner avec cela.

import loadScript from "discourse/lib/load-script";

...

loadScript(URL).then(() => {
   // votre code
})

Bien que je ne l’aie pas testé de manière exhaustive, un import dynamique devrait également fonctionner.

async function load() {
  const {
    default: myDefault,
    foo,
    bar,
  } = await import(URL);
  // reste du code
}

Je dois noter qu’il est probablement plus sûr de sauvegarder les fichiers NPM directement dans votre code au lieu de s’appuyer sur un CDN externe, selon l’importance critique du JS. De même, vous pourriez avoir besoin de faire un peu de regroupement et de prétraitement avant de pouvoir l’utiliser dans le navigateur.


Quant à savoir si Immer fonctionnera avec Discourse, je ne sais pas.

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