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 „Gefällt mir“

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 „Gefällt mir“

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

Zur Protokollierung – wir haben ein neues System zum Rendern von Ember-Komponenten innerhalb von Widgets. Details finden Sie in der Commit-Nachricht hier:

Suchen Sie im Kern-Codebestand nach RenderGlimmer, um Beispiele für die Verwendung zu finden.

5 „Gefällt mir“

Das ist großartig, @David! … aber warum könnte ich Schwierigkeiten beim Importieren haben:

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

wenn ich versuche, dies in einer Theme Component zu verwenden?

(Ich bin auf der neuesten Version tests-passed)

2 „Gefällt mir“

Ah… es ist sehr gut möglich, dass die Inline-Kompilierung in Theme-Komponenten (noch) nicht funktioniert. Sie sollte in Plugins funktionieren (seit dieser Woche). Ich werde sehen, was ich tun kann, um die Unterstützung für Theme-Komponenten hinzuzufügen :eyes:

2 „Gefällt mir“

Das sollte die Dinge zum Laufen bringen:

Ich werde versuchen, es nächste Woche zusammenzuführen.

3 „Gefällt mir“

Ausgezeichnete Fortschritte. Ich habe eine Verwendung dafür und freue mich darauf :+1:t2:

2 „Gefällt mir“

Das ist zusammengeführt – lass uns wissen, wie es dir damit geht @merefield

3 „Gefällt mir“

Bingo! Die Komponente wird jetzt in meinem TC-basierten Widget angezeigt, sogar die Daten werden korrekt befüllt, super Sache! :rocket:

Vielen Dank für die erstaunliche Bearbeitungszeit über die britischen Feiertage hinweg!

Ich weiß, dass Widgets Klick-Ereignisse verarbeiten, aber was ist, wenn meine Komponente ein Ereignis auslöst, das eine Aktion auslöst? Wo kann ich diese Aktion innerhalb eines Widgets verarbeiten?

2 „Gefällt mir“

Ups David, habe gerade ein Beispiel gesehen, ich studiere es!

1 „Gefällt mir“

Ja, das ist wahrscheinlich die beste Referenz. Ich glaube nicht, dass wir im Kern bisher ‘echte’ Aktionen durchgeführt haben. Melden Sie sich also, wenn Sie auf Probleme stoßen.

Wenn ich mir diesen Test noch einmal ansehe, vermute ich, dass die Funktion actionForComponentToTrigger() eine @bind-Anweisung darüber benötigt, um sicherzustellen, dass this darin korrekt funktioniert.

2 „Gefällt mir“

OK, das funktioniert bis zu einem gewissen Grad:

    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 wird für eine Änderung aufgerufen und actionForClick wird beim Drücken der Schaltfläche aufgerufen.

Ich habe jedoch Schwierigkeiten, den Tag Chooser gefüllt zu halten.

Sobald ich meine benutzerdefinierte Aktion für onChange einführe, schlägt die Steuerung fehl, die ausgewählten Tags in ihrer Benutzeroberfläche auf dem neuesten Stand zu halten (obwohl meine benutzerdefinierte Variable die richtigen Werte enthält).

Und wenn ich meine benutzerdefinierte Aktion für onChange nicht einschließe, funktioniert die Benutzeroberfläche der Komponente wieder, aber ich kann mein Set nicht füllen. :thinking:

Ich habe versucht, this.scheduleRerender() zur Funktion onChangeUpdateTagSet hinzuzufügen, aber dies wird als undefined angezeigt - eindeutig nicht in seinem Gültigkeitsbereich?

Ich habe ein Klickereignis zum Widget hinzugefügt und es wird dann aktualisiert und zeigt die richtigen Daten an.

Es sieht jedoch so aus, als ob die Bindung nur in eine Richtung geht, sodass ich die Komponente nicht verwenden kann, um die externen Daten zu aktualisieren, sondern nur neue Daten in die Komponente zu pushen kann.

Gibt es eine Möglichkeit, die Bindung bidirektional zu gestalten?

Ich vermute, Sie müssen widget ‘state’ anstelle des Zugriffs auf this.chosen verwenden – Widget-Instanzen sind kurzlebig und „zustandslos“. Sie erhalten bei jeder Neu-Renderung eine neue Instanz, was bedeutet, dass this.chosen zurückgesetzt wird.

1 „Gefällt mir“

this.state ist innerhalb der change-Funktion undefiniert, was die Sache etwas knifflig macht?

(Ich kann es übergeben, aber das überschreibt die Daten, die von der Komponente kommen, um mir mitzuteilen, was ausgewählt wurde

  onChangeUpdateTagSet: this.onChangeUpdateTagSet(this.state),

)

1 „Gefällt mir“

Können Sie mehr vom Widget-Code teilen? Soweit ich mich erinnere, müssen Sie für den Status eine buildKey- und eine defaultState()-Methode definieren.

Ich werde sehen, ob ich ein Beispiel für diese Art von Dingen in den MountWidget-Tests hinzufügen kann :eyes:

1 „Gefällt mir“