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 个赞

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 个赞

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

供记录——我们有一个在 widget 中渲染 Ember 组件的新系统。详细信息可以在此提交消息中找到:

在核心代码库中搜索 RenderGlimmer 以查找示例用法。

5 个赞

太棒了,@David!……但为什么我在使用这个时可能会难以导入:

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

在尝试于主题组件中使用它时?

(我使用的是最新的 tests-passed

2 个赞

啊……主题组件(theme components)可能(还)不支持内联编译。插件(自本周起)应该支持。我会看看我能做什么来添加主题组件支持 :eyes:

2 个赞

这应该能让它正常工作:

下周会尝试合并它。

3 个赞

进展非常顺利。我有一个用途,期待它 :+1:t2:

2 个赞

已合并 - 请告知我们您在使用它时的情况 @merefield

3 个赞

太棒了!组件现在显示在我的 TC 小部件中,数据也已正确填充,真是太棒了! :rocket:

感谢您在英国银行假日期间如此迅速地完成工作!

我知道小部件可以处理点击事件,但如果我的组件有一个触发操作的事件,我可以在小部件的什么位置处理该操作?

2 个赞

哎呀 David,我刚看到一个例子,我正在学习它!

1 个赞

是的,这可能是最好的参考。我认为我们还没有在核心中真正地“通过”actions,所以如果你遇到任何问题,请告诉我。

再看看那个测试,我怀疑 actionForComponentToTrigger() 函数需要一个 @bind 在它上面,以确保 this 在其中正常工作。

2 个赞

OK,这在一定程度上有效:

    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 在更改时被调用,actionForClick 在按钮按下时被调用。

但是,我难以保持 Tag Chooser 被填充。

一旦我引入了自定义的 onChange 操作,该控件就无法在其 UI 中保持选定的标签更新(即使我的自定义变量包含正确的值)。

如果我不包含自定义的 onChange 操作,组件的 UI 就可以再次工作,但我无法填充我的集合。:thinking:

我尝试在 onChangeUpdateTagSet 函数中添加 this.scheduleRerender(),但这显示为 undefined - 显然不在其范围内?

我向小部件添加了一个点击事件,它确实刷新并显示了正确的数据。

但是,看起来绑定只支持单向,所以我不能使用该组件来更新外部数据,只能将新数据推送到该组件。

有没有办法使绑定成为双向的?

我怀疑您需要使用 widget ‘state’ 而不是访问 this.chosen - widget 实例是短暂且“无状态”的。每次重新渲染时都会获得一个新实例,这意味着 this.chosen 将被重置。

1 个赞

this.state 在 change 函数中是未定义的,这使得事情有点棘手?

(我可以传入它,但这会覆盖来自组件的数据,告诉我选择了什么

  onChangeUpdateTagSet: this.onChangeUpdateTagSet(this.state),

1 个赞

您能否分享更多小部件代码?如果我没记错的话,要使状态正常工作,您需要定义一个 buildKey 和一个 defaultState() 方法。

我会看看是否能在 MountWidget 测试中添加此类示例 :eyes:

1 个赞