Glimmer Component Wijzigingsdetectie Werkt Niet - Muteren van Eigenschappen in @tracked Array

Hallo iedereen!

Ik heb problemen met het detecteren van wijzigingen in een Glimmer-component en zou wat begeleiding kunnen gebruiken. Wanneer ik eigenschappen op objecten binnen een @tracked array muteer, wordt de template niet opnieuw gerenderd.

Mijn opstelling:

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

    }

    // ... meer items

  ];



  @action

  toggleExpanded(item) {

    item.expanded = !item.expanded; // Deze mutatie triggert geen herweergave
    console.log('Toggled:', item.label, item.expanded); // Logt correct

  }



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

}

```

Het probleem:

  1. De klikhandler wordt correct uitgevoerd
  2. De eigenschap item.expanded wordt bijgewerkt (geverifieerd in de console)
  3. Maar de template wordt niet opnieuw gerenderd - geen klassewijzigingen, geen tekstwijzigingen

Wat ik heb geprobeerd:

  1. Array-hertoewijzing - this.items = [...this.items] na mutatie (werkt niet)
  2. Immer voor onveranderlijke updates - Wilde dit proberen, maar kan npm-imports niet werkend krijgen in Discourse-thema’s
  3. Andere sleutelstrategieën - key="id" in plaats van key="@index"

Vragen:

  • Moet het muteren van eigenschappen op objecten binnen @tracked arrays werken in Glimmer?
  • Moet ik individuele objecten op de een of andere manier tracked maken?
  • Is er een aanbevolen patroon voor dit gebruiksscenario?
  • Kan dit verband houden met het draaien in een Discourse-thema-omgeving?
  • Zijn er beperkingen met npm-imports in Discourse-thema’s die reactiviteitsbibliotheken beĂŻnvloeden?

Omgeving:

  • Discourse thema-component
  • Glimmer-componenten met .gjs-bestanden
  • Nieuwste Discourse-versie - vandaag bijgewerkt

Alle inzichten zouden zeer gewaardeerd worden! Mis ik iets fundamenteels over Glimmer’s reactiviteitssysteem?

Bedankt!

2 likes

Hmm, dat is vreemd. De array-toewijzingstruc werkt meestal.

Een alternatief dat ik heb gevonden, is om de objecten in de array te laten komen van een klasse met eigen bijgehouden eigenschappen. Iets als:

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'),
    ...
  ];
  // rest van je code
}

Het kan omslachtiger zijn dan het maken van een hoop platte objecten, maar ik heb gemerkt dat het gemakkelijker uit te breiden en te redeneren is, vooral als je iets moet doen als het doorgeven van de gegevens aan geneste componenten.

4 likes

Hi Alteras,

Thank you for your suggestion !!! It did work as suggested by you approach and really made our day bit easy ::)** :grinning_face:

2 likes

I believe when you track an array like described in OP you’re tracking the array reference and not changes to the individual objects within the array

Another way to handle it is using trackedObject, we use this in a handful of places throughout Discourse

3 likes

Bedankt voor uw input. Ik heb nog een vervolgvraag:

Zou het gebruik van een bibliotheek zoals Immer werken?
Zo ja, hoe moet ik Immer in Discourse opnemen?
Ik heb vermeldingen gezien van ‘kopieer het naar assets vanuit node_modules’, maar ik zou het liefst een manier hebben om dit via npm te doen, of zelfs een CDN-link in de header-tag.

You can use loadScript() to load JS, even if the URL provided is an external URL. Using external services like jsdelivr should work with this.

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

...

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

Though I haven’t tested it extensively, a dynamic import should also work.

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

I should note that it is probably safer to save the NPM files directly into your code instead of relying on an external CDN, depending on how critical the JS is. Similarly, you may need to do a bit of bundling and preprocessing before being able to use it in the browser.


As for whether Immer will work with discourse, I don’t know.

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