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 curtida

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 curtidas

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

Para constar - temos um novo sistema para renderizar componentes Ember dentro de widgets. Os detalhes podem ser encontrados na mensagem do commit aqui:

Procure por RenderGlimmer na base de código principal para encontrar exemplos de uso.

5 curtidas

Isso é ótimo, @David! … mas por que eu poderia estar com dificuldades para importar:

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

ao tentar usar isso em um Componente de Tema?

(estou na versão mais recente tests-passed)

2 curtidas

Ah… é muito possível que a compilação inline ainda não funcione em componentes de tema. Deve funcionar em plugins (a partir desta semana). Vou ver o que posso fazer para adicionar suporte a componentes de tema :eyes:

2 curtidas

Isso deve fazer as coisas funcionarem:

Tentarei fazer com que seja mesclado na próxima semana.

3 curtidas

Excelente progresso. Tenho um uso para isso, então estou ansioso por isso :+1:t2:

2 curtidas

Isso foi mesclado - nos diga como você se sai com isso @merefield

3 curtidas

Bingo! O componente agora está aparecendo no meu widget baseado em TC, até os dados estão sendo preenchidos corretamente, muito bom! :rocket:

Obrigado pela incrível agilidade, mesmo durante o feriado bancário do Reino Unido!

Eu sei que widgets lidam com eventos de clique, mas e se meu componente tiver um evento que dispara uma ação, onde posso lidar com essa ação dentro de um widget?

2 curtidas

Oops David, acabei de ver um exemplo, estou estudando-o!

1 curtida

Sim, essa provavelmente é a melhor referência. Acho que ainda não passamos ações ‘de verdade’ no core, então me avise se encontrar algum problema.

Olhando novamente para esse teste, suspeito que a função actionForComponentToTrigger() precisará de um @bind acima dela para garantir que this funcione corretamente dentro dela.

2 curtidas

OK, isso funciona até certo ponto:

    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 é invocado para uma alteração e actionForClick é invocado ao pressionar o botão.

No entanto, estou com dificuldades para manter o Seletor de Tags preenchido.

Assim que introduzo minha ação personalizada para onChange, o controle falha em manter as tags selecionadas atualizadas na sua interface (mesmo que minha variável personalizada contenha os valores corretos).

E se eu não incluir minha ação personalizada para onChange, a interface do componente funciona novamente, mas não consigo preencher meu conjunto. :thinking:

Tentei adicionar this.scheduleRerender() à função onChangeUpdateTagSet, mas isso está aparecendo como undefined - claramente não está em seu escopo?

Adicionei um evento de clique ao widget e ele então atualiza mostrando os dados corretos.

No entanto, parece que a ligação é apenas em uma direção, então não posso usar o Componente para atualizar os dados externos, apenas para enviar novos dados para o Componente.

Existe uma maneira de tornar a ligação bidirecional?

Suspeito que você precisará usar widget ‘state’ em vez de acessar this.chosen - instâncias de widget são de curta duração e ‘sem estado’. Você obterá uma nova instância em cada re-renderização, o que significa que this.chosen será redefinido.

1 curtida

this.state está indefinido dentro da função de alteração, então isso torna as coisas um pouco complicadas?

(Posso passá-lo, mas isso substitui os dados vindos do componente para me dizer o que foi escolhido

  onChangeUpdateTagSet: this.onChangeUpdateTagSet(this.state),

)

1 curtida

Você consegue compartilhar mais do código do widget? Se não me engano, para o estado funcionar, você precisa definir um método buildKey e um defaultState().

Vou ver se consigo adicionar um exemplo desse tipo nos testes do MountWidget :eyes:

1 curtida