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

**URL:** https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347
**Category:** Development
**Created:** [29 februari 2016 om 20:18 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347 "2016-02-29T20:18:39Z")
**Posts on this page:** 16
**Page:** 4

<div class="post-metadata">

### Author: ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)
#### Post date: [4 september 2017 om 21:38 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/61 "2017-09-04T21:38:45Z")

</div>

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

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.

> <https://github.com/discourse/discourse-whos-online/blob/0437d7f2c8f9e396416e66f0a306b4e9377fc389/assets/javascripts/discourse/initializers/start-whos-online.js.es6#L88-L101>

---

<div class="post-metadata">

### Author: ![eviltrout](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/eviltrout/32/5275_2.png) [@eviltrout](https://meta.discourse.org/u/eviltrout)
#### Post date: [5 september 2017 om 14:21 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/62 "2017-09-05T14:21:26Z")

</div>

> [@david](#):
>
> When setting dirtyKeys, it only re-renders the first widget with that buildKey

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.

> [@david](#):
>
> I think this is because of the shadowTree stuff.

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.

---

<div class="post-metadata">

### Author: ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)
#### Post date: [27 september 2017 om 15:40 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/63 "2017-09-27T15:40:09Z")

</div>

> [@eviltrout](#):
>
> In the future we plan on using Glimmer 2’s handlebars compiler.

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](https://github.com/discourse/discourse/commit/dffb1fc4ee8a4d58f48145decb1590a304e8cf7d)

---

<div class="post-metadata">

### Author: ![Deepak\_Kumar4](https://avatars.discourse-cdn.com/v4/letter/d/ba8739/32.png) [@Deepak\_Kumar4](https://meta.discourse.org/u/Deepak_Kumar4)
#### Post date: [25 november 2020 om 12:28 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/64 "2020-11-25T12:28:06Z")

</div>

How to create a navigation bar? using widget.

like this

![image](https://global.discourse-cdn.com/meta/original/3X/e/2/e223dc4940129800694eee658f78b2f23fd442fb.png)

---

<div class="post-metadata">

### Author: ![domdambro](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/domdambro/32/201848_2.png) [@domdambro](https://meta.discourse.org/u/domdambro)
#### Post date: [22 januari 2021 om 21:48 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/65 "2021-01-22T21:48:37Z")

</div>

The following code in the instruction no longer work. The render works but the state is not incrementing on clicks:

````plaintext
<script type="text/javascript">
        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++;
            }
        });
</script>

<script type='text/x-handlebars' data-template-name='/connectors/above-footer/increment-button'>
    {{mount-widget widget="increment-button" }}
</script>```
````

---

<div class="post-metadata">

### Author: ![eviltrout](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/eviltrout/32/5275_2.png) [@eviltrout](https://meta.discourse.org/u/eviltrout)
#### Post date: [25 januari 2021 om 17:37 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/66 "2021-01-25T17:37:33Z")

</div>

Weird, for me when I try that component it doesn’t render at all because it is missing a `key`. I’ve updated the documentation above to add a `buildKey` method and it’s working for me. Try that out.

---

<div class="post-metadata">

### Author: ![domdambro](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/domdambro/32/201848_2.png) [@domdambro](https://meta.discourse.org/u/domdambro)
#### Post date: [25 januari 2021 om 19:11 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/67 "2021-01-25T19:11:10Z")

</div>

Thanks @eviltrout I was able to get that to work! Much appreciated

---

<div class="post-metadata">

### Author: ![hyd504](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/hyd504/32/177122_2.png) [@hyd504](https://meta.discourse.org/u/hyd504)
#### Post date: [21 februari 2021 om 05:48 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/68 "2021-02-21T05:48:22Z")

</div>

How can I render html coming from an argument into the widget? Right now it is escaping the HTML and rendering it as it is.

Also the following example is not working:

> [@eviltrout](#):
>
> `{{mount-widget widget="display-name" args=user}}`

It seems like the user variable is null. Do I have to do something to pass user into the hbs from where the widget is mounted?

---

<div class="post-metadata">

### Author: ![hyd504](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/hyd504/32/177122_2.png) [@hyd504](https://meta.discourse.org/u/hyd504)
#### Post date: [21 februari 2021 om 07:10 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/69 "2021-02-21T07:10:36Z")

</div>

Found answer to this one:

> [@hyd504](#):
>
> How can I render html coming from an argument into the widget? Right now it is escaping the HTML and rendering it as it is.

Answer:

```
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>` });
  }
});

```

But I still don’t know how to get user data in a connector hbs where data is not passed in from the outlet.

---

<div class="post-metadata">

### Author: ![spirobel](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/spirobel/32/170908_2.png) [@spirobel](https://meta.discourse.org/u/spirobel)
#### Post date: [22 februari 2021 om 06:36 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/70 "2021-02-22T06:36:09Z")

</div>

> <https://github.com/discourse/discourse/blob/a0bbc346cb5d5b89d1a3efdfa89869349a8b067f/app/assets/javascripts/discourse/app/widgets/widget-dropdown.js#L148>

check out the triple curlys, bro 😃

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [24 februari 2021 om 17:26 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/71 "2021-02-24T17:26:02Z")

</div>

Not so fast 😉 ⚠ :

> <https://github.com/ember-template-lint/ember-template-lint/blob/main/docs/rule/no-triple-curlies.md>

---

<div class="post-metadata">

### Author: ![awesomerobot](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/awesomerobot/32/142900_2.png) [@awesomerobot](https://meta.discourse.org/u/awesomerobot)
#### Post date: [24 februari 2021 om 19:25 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/72 "2021-02-24T19:25:11Z")

</div>

Instead of triple curlies we have a helper you can use instead:

```hbs
{{html-safe result}}

```

---

<div class="post-metadata">

### Author: ![Grayden\_Shand](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/grayden_shand/32/166586_2.png) [@Grayden\_Shand](https://meta.discourse.org/u/Grayden_Shand)
#### Post date: [27 oktober 2021 om 22:30 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/73 "2021-10-27T22:30:04Z")

</div>

Hey @eviltrout,

I noticed in the OP for this you say:

> [@eviltrout](#):
>
> However, if you have an edge case, a widget can rerender itself by calling `this.queueRerender()` .
> 
> Render calls are coalesced on the [Ember Run Loop](https://guides.emberjs.com/v1.12.0/understanding-ember/run-loop/), so don’t be afraid to call `this.queueRerender()` multiple times in the same event loop – the widget will only render one time for all the changes you’ve queued.

But lower down in this topic, you mention

> [@eviltrout](#):
>
> Oops, not sure if I wrote that before ☕ but I did get it backwards. I’ll edit that post. It’s `scheduleRerender` within a widget. It calls `queueRerender` on the component that embedded it.

Would you be willing to correct the OP?

---

<div class="post-metadata">

### Author: ![eviltrout](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/eviltrout/32/5275_2.png) [@eviltrout](https://meta.discourse.org/u/eviltrout)
#### Post date: [28 oktober 2021 om 17:39 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/74 "2021-10-28T17:39:56Z")

</div>

Thanks, I’ve made that edit.

---

<div class="post-metadata">

### Author: ![pipkin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pipkin/32/134643_2.png) [@pipkin](https://meta.discourse.org/u/pipkin)
#### Post date: [10 juli 2023 om 11:54 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/75 "2023-07-10T11:54:35Z")

</div>

> [@eviltrout](#):
>
> `{{mount-widget widget="my-widget" deleteThing="deleteThing"}}`

@eviltrout Shouldn’t this be `action="deleteThing"` ?

---

<div class="post-metadata">

### Author: ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)
#### Post date: [10 juli 2023 om 12:12 UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/76 "2023-07-10T12:12:29Z")

</div>

It depends on your widget. In `@eviltrout`’s example, the my-widget expects to be passed an action named “deleteThing”. Different widgets will use different names for their actions (and indeed, it’s possible for them to call their action “action”)

[Vorige pagina](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347.md?page=3)
