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

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

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

Per la cronaca, abbiamo un nuovo sistema per il rendering dei componenti Ember all’interno dei widget. I dettagli sono disponibili nel messaggio di commit qui:

Cerca RenderGlimmer nel codebase principale per trovare esempi di utilizzo.

5 Mi Piace

Questo è fantastico @David! … ma perché potrei avere difficoltà a importare:

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

quando provo a usarlo in un componente tema?

(sono sull’ultima versione tests-passed)

2 Mi Piace

Ah… è molto probabile che la compilazione inline non funzioni (ancora) nei componenti del tema. Dovrebbe funzionare nei plugin (da questa settimana). Vedrò cosa posso fare per aggiungere il supporto ai componenti del tema :occhi:

2 Mi Piace

Questo dovrebbe far funzionare le cose:

Cercherò di farlo unire la prossima settimana.

3 Mi Piace

Ottimo progresso. Ho un utilizzo per questo, quindi non vedo l’ora :+1:t2:

2 Mi Piace

È stato unito: facci sapere come ti trovi con esso @merefield

3 Mi Piace

Bingo! Il componente ora appare nel mio widget basato su TC, anche i dati sono popolati correttamente, fantastico! :rocket:

Grazie per la straordinaria rapiditĂ  nonostante il giorno festivo nel Regno Unito!

So che i widget gestiscono gli eventi di clic, ma cosa succede se il mio componente ha un evento che attiva un’azione, dove posso gestire quell’azione all’interno di un widget?

2 Mi Piace

Oops David, ho appena visto un esempio, lo sto studiando!

1 Mi Piace

Sì, probabilmente è il riferimento migliore. Non credo che abbiamo ancora passato azioni ‘per davvero’ nel core, quindi fammi sapere se incontri problemi.

Guardando di nuovo quel test, sospetto che la funzione actionForComponentToTrigger() avrĂ  bisogno di un @bind sopra di essa per assicurarsi che this funzioni correttamente al suo interno.

2 Mi Piace

OK, questo funziona in parte:

    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 viene invocato per una modifica e actionForClick viene invocato alla pressione del pulsante.

Tuttavia, ho difficoltĂ  a mantenere popolato il Tag Chooser.

Non appena introduco la mia azione personalizzata per onChange, il controllo non riesce a mantenere aggiornati i tag selezionati nella sua interfaccia utente (anche se la mia variabile personalizzata contiene i valori corretti).

E se non includo la mia azione personalizzata per onChange, l’interfaccia utente del componente funziona di nuovo, ma non riesco a popolare il mio set. :thinking:

Ho provato ad aggiungere this.scheduleRerender() alla funzione onChangeUpdateTagSet, ma questo risulta undefined - chiaramente non nel suo scope?

Ho aggiunto un evento click al widget e questo si aggiorna mostrando i dati corretti.

Tuttavia, sembra che il binding sia unidirezionale, quindi non posso usare il Component per aggiornare i dati esterni, solo per inserire nuovi dati nel Component.

C’è un modo per rendere il binding bidirezionale?

Sospetto che dovrai usare widget ‘state’ invece di accedere a this.chosen - le istanze dei widget sono di breve durata e ‘stateless’. Otterrai una nuova istanza ad ogni re-render, il che significa che this.chosen verrà resettato.

1 Mi Piace

this.state è indefinito all’interno della funzione change, quindi questo rende le cose un po’ complicate?

(Posso passarlo, ma questo sovrascrive i dati provenienti dal componente per dirmi cosa è stato scelto

  onChangeUpdateTagSet: this.onChangeUpdateTagSet(this.state),

)

1 Mi Piace

Riesci a condividere altro codice del widget? Se non ricordo male, affinchĂŠ lo stato funzioni, devi definire un metodo buildKey e un metodo defaultState().

Vedrò se riesco ad aggiungere un esempio di questo tipo nei test di MountWidget :occhi:

1 Mi Piace