A tour of how the Widget (Virtual DOM) code in Discourse works

Right, after a lot of headaches I’ve got it working - thanks for the help! :tada:

The two other problems I hit which could possibly do with some re-engineering in the widget code:

  • When setting dirtyKeys, it only re-renders the first widget with that buildKey. I’ve got around that by adding the post-id to the post-avatar’s buildKey so that every buildKey is unique
  • Setting the dirtyKey doesn’t work if the widget you want to re-render is inside another widget. In my case the post-avatar is inside a post. I think this is because of the shadowTree stuff.
    It would be really nice if the widget code could work out that the widget is deep in the tree and re-render its parents. I’ve got around this by setting the post widget as dirty as well.

That’s because keys are supposed to be unique! So adding the post id to the avatar is correct in this case. I might want to raise an error in development mode if two elements use the same keys. If you want to commit the post avatar key to master that would be fine.

That’s exactly what it is. By default I think a widget is supposed to re-render its parents, but the shadowTree is a performance optimization that avoids too much re-rendering and requires you to be more explicit.

Just in case anyone is trying to track down how widgets can use handlebars templates, see the commit message here (can’t find it documented anywhere on meta):

https://github.com/discourse/discourse/commit/dffb1fc4ee8a4d58f48145decb1590a304e8cf7d

如何使用小部件创建导航栏?

就像这样

image

以下指令中的代码已无法正常工作。渲染可以正常显示,但点击时状态并未递增:

const { createWidget } = require('discourse/widgets/widget');

createWidget('increment-button', {
    tagName: 'button',

    defaultState() {
        return { clicks: 0 };
    },

    html(attrs, state) {
        return `Click me! ${state.clicks} clicks`;
    },

    click() {
        this.state.clicks++;
    }
});
{{mount-widget widget="increment-button" }}

有点奇怪,对我来说,当我尝试使用该组件时,它根本没有渲染,因为缺少一个 key。我已在上面的文档中更新了内容,添加了 buildKey 方法,现在对我而言可以正常工作了。请试试看。

感谢 @eviltrout,我成功让它运行起来了!非常感谢!

我该如何将从参数中传来的 HTML 渲染到组件中?目前它正在转义 HTML 并按原样渲染。

此外,以下示例也无法正常工作:

似乎 user 变量为 null。我是否需要做些什么,才能将 user 传递给挂载该组件的 hbs 模板?

找到这个问题的答案了:

答案:

import { createWidget } from 'discourse/widgets/widget';
import RawHtml from "discourse/widgets/raw-html";

createWidget('display-name', {
  tagName: 'div.name',

  html() {
    return new RawHtml({ html: `<div>${this.siteSettings.user_rank_alert_message}</div>` });
  }
});

但我仍然不知道如何在未从出口传入数据的连接器 hbs 中获取用户数据。

看看那三个大括号,兄弟 :smiley:

别急得太快 :wink: :warning: :

与其使用三个花括号,您可以使用以下辅助方法:

{{html-safe result}}

@eviltrout

我注意到你在本文的原帖(OP)中提到:

但在本文的下方,你提到:

你愿意修正原帖吗?

谢谢,我已经做了那个编辑。

@eviltrout 这不应该是 action="deleteThing" 吗?

这取决于您的 widget。在 @eviltrout 的示例中,my-widget 期望接收一个名为“deleteThing”的 action。不同的 widget 将使用不同的名称来命名它们的 action(事实上,它们也可以将它们的 action 命名为“action”)。