Render a component within a Widget. (Using select-kit components within plugin code)

If we successfully define a select-kit component within a plugin we are easily able to use it within a *.hbs template file using its pluginApiIdentifiers however, in order to attach that component within a widget using JS code I am not able to make it work. For instance:

I have:

import DropdownSelectBoxComponent from 'select-kit/components/dropdown-select-box';

export default DropdownSelectBoxComponent.extend({
  pluginApiIdentifiers: ['header-profile-dropdown'],
  ...
  
  autoHighlight() { ... },

  computeContent() {
    ...
    return items;
  },

  mutateValue(id) { ... }
});

Then I can go to any template I want to overwrite and write like:

(taking as an example the discovery template)

...
<div id="list-area">
        {{header-profile-dropdown}}
...

And the snippet above will work flawlessly, BUT how I will include that component into a widget like:

    api.reopenWidget('user-dropdown', {
        html(attrs, state) {
          if (this.site.mobileView) {
            return h('div.profile-dropdown-container', [
              this.attach('header-notifications', attrs),
              --> I NEED ATTACH THE COMPONENT HERE <--
            ]);
          }

          ...
        }
      });

NOTE: much of the code has been skipped to keep the post brief. Feel free to ask for the full code if needed.

PD: I have already tried to describe this issue at How to attach a component to a widget with just markup? but looks like it was a too complicated explanation so I am trying to reduce the information here to make it clear and try to find an answer.

1 « J'aime »

I think it was a matter of ask myself the right question: “How to render a component within a widget?” that’s why I updated the topic title. I have found few results already such as: How to render Component inside Widget? I will take a look and post any useful finding if it is needed.

Sadly enough looks like it is not possible, simply put we need to find a way to create a widget instead to render what we need to and render a widget within a widget, cause using the connector there are a lot of limitations. Check also: Rendering Component in decorativeWidget API

same issue here.
this is really terrible frontend code that uses widgets and ember components in the same page, it’s really hard to revamp and maintain.
can’t understand what’s the logic

An example of this is available in Discourse itself:

https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6#L362

I wasn’t there when widgets were created, but as far as I can tell, it was created for performance reasons at a time where frontend and especially mobile frontend had huge performance issues. And for dev simplicity it was added the possibility to inject components into widgets.

You got to understand that Discourse carries some history and we can’t start from scratch every year.

6 « J'aime »

This topic was automatically closed after 32 hours. New replies are no longer allowed.

Pour mémoire, nous avons un nouveau système pour le rendu des composants Ember dans les widgets. Les détails se trouvent dans le message de commit ici :

Recherchez RenderGlimmer dans la base de code principale pour trouver des exemples d’utilisation.

5 « J'aime »

C’est super @David ! … mais pourquoi pourrais-je avoir du mal à importer :

import { hbs } from "ember-cli-htmlbars";
Error: Could not find module `ember-cli-htmlbars`

quand j’essaie de l’utiliser dans un Theme Component ?

(je suis sur la dernière version tests-passed)

2 « J'aime »

Ah… il est très possible que la compilation inline ne fonctionne pas (encore) dans les composants de thème. Elle devrait fonctionner dans les plugins (depuis cette semaine). Je vais voir ce que je peux faire pour ajouter la prise en charge des composants de thème :eyes:

2 « J'aime »

Cela devrait permettre de faire fonctionner les choses :

J’essaierai de le faire fusionner la semaine prochaine.

3 « J'aime »

Excellent progrès. J’ai une utilité pour cela, j’ai donc hâte :+1:t2:

2 « J'aime »

C’est fusionné - faites-nous savoir comment vous vous en sortez avec @merefield

3 « J'aime »

Bingo ! Le composant apparaît maintenant dans mon widget basé sur TC, même les données sont correctement remplies, c’est génial ! :rocket:

Merci pour la incroyable rapidité de traitement en plein pont bancaire britannique !

Je sais que les widgets gèrent les événements de clic, mais que se passe-t-il si mon composant a un événement qui déclenche une action, où puis-je gérer cette action dans un widget ?

2 « J'aime »

Oups David, je viens de voir un exemple, je l’étudie !

1 « J'aime »

Oui, c’est probablement la meilleure référence. Je ne pense pas que nous ayons encore passé d’actions « pour de vrai » dans le cœur, alors crie si tu rencontres des problèmes.

En regardant à nouveau ce test, je soupçonne que la fonction actionForComponentToTrigger() aura besoin d’un @bind au-dessus d’elle pour s’assurer que this fonctionne correctement à l’intérieur.

2 « J'aime »

OK, cela fonctionne dans une certaine mesure :

    contents.push(
      new RenderGlimmer(
        this,
        "div.tag-chooser-component",
        hbs`<TagChooser @id="list-with-tags"  @tags={{@data.chosen}} @onChange={{action @data.onChangeUpdateTagSet}}/>
        <button onClick={{@data.actionForClick}} label="blah"/>`,
        {
          chosen: this.chosen,
          onChangeUpdateTagSet: this.onChangeUpdateTagSet,
          actionForClick: this.actionForClick
        }
      ),
    );
    return contents;
  },

  @bind
  onChangeUpdateTagSet(chosen) {
    this.chosen.push(chosen.lastObject);
  },

  @bind
  actionForClick () {
    console.log(this.chosen)
  }
});

onChangeUpdateTagSet est appelé pour un changement, et actionForClick est appelé lors de la pression sur le bouton.

Cependant, j’ai du mal à maintenir le sélecteur de tags rempli.

Dès que j’introduis mon action personnalisée pour onChange, le contrôle ne parvient pas à maintenir les tags sélectionnés à jour dans son interface utilisateur (même si ma variable personnalisée contient les bonnes valeurs).

Et si je n’inclus pas mon action personnalisée pour onChange, l’interface utilisateur des composants fonctionne à nouveau, mais je ne peux pas remplir mon ensemble. :thinking:

J’ai essayé d’ajouter this.scheduleRerender() à la fonction onChangeUpdateTagSet mais cela s’affiche comme undefined - clairement pas dans sa portée ?

J’ai ajouté un événement de clic au widget et il se rafraîchit alors, affichant les bonnes données.

Cependant, il semble que la liaison ne soit que dans un sens, je ne peux donc pas utiliser le composant pour mettre à jour les données externes, seulement pour pousser de nouvelles données dans le composant.

Existe-t-il un moyen de rendre la liaison bidirectionnelle ?

Je soupçonne que vous devrez utiliser widget ‘state’ plutôt que d’accéder à this.chosen - les instances de widget sont éphémères et « sans état ». Vous obtiendrez une nouvelle instance à chaque nouveau rendu, ce qui signifie que this.chosen sera réinitialisé.

1 « J'aime »

this.state est indéfini dans la fonction change, ce qui rend les choses un peu délicates ?

(Je peux le passer, mais cela écrase les données provenant du composant pour me dire ce qui a été choisi

  onChangeUpdateTagSet: this.onChangeUpdateTagSet(this.state),

)

1 « J'aime »

Êtes-vous en mesure de partager davantage de code du widget ? Si ma mémoire est bonne, pour que l’état fonctionne, vous devez définir une méthode buildKey et une méthode defaultState().

Je vais voir si je peux ajouter un exemple de ce genre dans les tests de MountWidget :eyes:

1 « J'aime »