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

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

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

Para que conste, tenemos un nuevo sistema para renderizar componentes de Ember dentro de widgets. Los detalles se pueden encontrar en el mensaje de confirmación aquí:

Busca RenderGlimmer en la base de código principal para encontrar ejemplos de uso.

5 Me gusta

¡Esto es genial @David! … pero ¿por qué podría tener problemas para importar:

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

cuando intento usar esto en un componente de tema?

(estoy en la última versión tests-passed)

2 Me gusta

Ah… es muy posible que la compilación en línea aún no funcione en los componentes del tema. Debería funcionar en los plugins (desde esta semana). Veré qué puedo hacer para agregar soporte para componentes de temas :eyes:

2 Me gusta

Esto debería hacer que las cosas funcionen:

Intentaré que se fusione la próxima semana.

3 Me gusta

Excelente progreso. Tengo un uso para esto, así que estoy deseando que llegue :+1:t2:

2 Me gusta

Eso está fusionado. Cuéntanos qué tal te va con ello @merefield.

3 Me gusta

¡Bingo! El componente ahora aparece en mi widget basado en TC, ¡incluso los datos se rellenan correctamente, genial! :rocket:

Gracias por la increíble respuesta, ¡a pesar del feriado bancario del Reino Unido!

Sé que los widgets manejan eventos de clic, pero ¿qué pasa si mi componente tiene un evento que dispara una acción? ¿Dónde puedo manejar esa acción dentro de un widget?

2 Me gusta

¡Uy David, acabo de ver un ejemplo, lo estoy estudiando!

1 me gusta

Sí, esa es probablemente la mejor referencia. No creo que todavía hayamos pasado acciones ‘de verdad’ en el núcleo, así que avisa si te encuentras con algún problema.

Mirando de nuevo esa prueba, sospecho que la función actionForComponentToTrigger() necesitará un @bind encima para asegurarse de que this funcione correctamente dentro de ella.

2 Me gusta

OK, esto funciona hasta cierto punto:

    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 se invoca para un cambio, y actionForClick se invoca al presionar el botón.

Sin embargo, tengo problemas para mantener el Selector de Etiquetas poblado.

Tan pronto como introduzco mi acción personalizada para onChange, el control no logra mantener las etiquetas seleccionadas actualizadas en su interfaz de usuario (aunque mi variable personalizada contenga los valores correctos).

Y si no incluyo mi acción personalizada para onChange, la interfaz de usuario de los componentes vuelve a funcionar, pero no puedo poblar mi conjunto. :thinking:

Intenté agregar this.scheduleRerender() a la función onChangeUpdateTagSet, pero esto aparece como undefined, ¿claramente no está en su ámbito?

Agregué un evento de clic al widget y luego se actualiza mostrando los datos correctos.

Sin embargo, parece que el enlace solo va en una dirección, por lo que no puedo usar el Componente para actualizar los datos externos, solo para enviar nuevos datos al Componente.

¿Hay alguna forma de hacer que el enlace sea bidireccional?

Sospecho que necesitarás usar widget ‘state’ en lugar de acceder a this.chosen, ya que las instancias de widget son de corta duración y “sin estado”. Obtendrás una nueva instancia en cada renderizado, lo que significa que this.chosen se restablecerá.

1 me gusta

this.state no está definido dentro de la función de cambio, ¿así que eso hace las cosas un poco complicadas?

(Puedo pasarlo, pero eso sobrescribe los datos que vienen del componente para decirme qué se ha elegido

  onChangeUpdateTagSet: this.onChangeUpdateTagSet(this.state),

)

1 me gusta

¿Puedes compartir más del código del widget? Si mal no recuerdo, para que el estado funcione, necesitas definir un método buildKey y un método defaultState().

Veré si puedo añadir un ejemplo de este tipo de cosas en las pruebas de MountWidget :eyes:

1 me gusta