# 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:** [February 29, 2016, 8:18pm 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:** 20
**Page:** 2

<div class="post-metadata">

### Author: ![piratdavid](https://avatars.discourse-cdn.com/v4/letter/p/ba9def/32.png) [@piratdavid](https://meta.discourse.org/u/piratdavid)
#### Post date: [May 3, 2016, 9:03am UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/21 "2016-05-03T09:03:03Z")

</div>

I can see the plugin-outlets in the header are gone. Any tips on how to add stuff to the header now?

---

<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: [May 3, 2016, 2:57pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/22 "2016-05-03T14:57:23Z")

</div>

The header uses the same apis as the new post stream, so you can just use `decorateWidget` to add code before or after any widget in the header for your needs.

---

<div class="post-metadata">

### Author: ![angus](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/angus/32/341715_2.png) [@angus](https://meta.discourse.org/u/angus)
#### Post date: [May 7, 2016, 5:11pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/23 "2016-05-07T17:11:13Z")

</div>

> [@eviltrout](#):
>
> Render calls are coalesced on the 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.

It seems that `this.queueRerender()` is now `this.scheduleRerender()` ?

---

<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: [May 9, 2016, 2:52pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/24 "2016-05-09T14:52:04Z")

</div>

Sorry, my mistake. Within a widget you call `queueRerender()` which triggers a `scheduleRerender()` on the ember component that embedded it.

So yes, you should be using `queueRerender`

* * *

edit: No, this post was wrong and I was right before. `scheduleRerender` is what you call within a widget to rerender. It calls `queueRerender` on the component.

---

<div class="post-metadata">

### Author: ![stevenpslade](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/stevenpslade/32/53550_2.png) [@stevenpslade](https://meta.discourse.org/u/stevenpslade)
#### Post date: [May 9, 2016, 8:31pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/25 "2016-05-09T20:31:35Z")

</div>

I want to `createWidget` that has the `attrs` of an exisitng widget (specfically the `topic-map` widget’s `attrs`). When creating a widget, how do you pass it the attrs of another widget to use?

---

<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: [May 9, 2016, 8:54pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/26 "2016-05-09T20:54:04Z")

</div>

You can pass anything as `attrs` to a widget when you attach it using `this.attach('widget-name', attrs)`.

It looks like `topic-map` is `attach`ed with the attrs from the post it’s inside.

---

<div class="post-metadata">

### Author: ![stevenpslade](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/stevenpslade/32/53550_2.png) [@stevenpslade](https://meta.discourse.org/u/stevenpslade)
#### Post date: [May 9, 2016, 9:00pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/27 "2016-05-09T21:00:30Z")

</div>

> [@eviltrout](#):
>
> It looks like topic-map is attached with the attrs from the post it’s inside.

Right, that’s what I thought.

`attrs.topicViews` is inside the widget `topic-map`, which is inside the `post` widget (attached through a few other widgets of course). However, in the `post` widget, `attrs.topicViews` comes up undefined.

---

<div class="post-metadata">

### Author: ![stevenpslade](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/stevenpslade/32/53550_2.png) [@stevenpslade](https://meta.discourse.org/u/stevenpslade)
#### Post date: [May 9, 2016, 9:36pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/28 "2016-05-09T21:36:04Z")

</div>

I just realized that a topic with one post will not show the topic views in the `attrs`. I am able to see it now on a topic with multiple posts.

---

<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: [May 10, 2016, 2:19pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/29 "2016-05-10T14:19:43Z")

</div>

Yes the topic map is only shown if certain criteria is met:

[https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/lib/transform-post.js.es6#L123](https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/lib/transform-post.js.es6#L123)

Otherwise we don’t bother to include that information because it’s not displayed.

---

<div class="post-metadata">

### Author: ![stevenpslade](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/stevenpslade/32/53550_2.png) [@stevenpslade](https://meta.discourse.org/u/stevenpslade)
#### Post date: [May 10, 2016, 9:44pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/30 "2016-05-10T21:44:24Z")

</div>

Is is possible to render a component inside a created widget?

---

<div class="post-metadata">

### Author: ![angus](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/angus/32/341715_2.png) [@angus](https://meta.discourse.org/u/angus)
#### Post date: [May 11, 2016, 12:58pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/31 "2016-05-11T12:58:39Z")

</div>

> [@eviltrout](#):
>
> Sorry, my mistake. Within a widget you call queueRerender() which triggers a scheduleRerender() on the ember component that embedded it.
> 
> So yes, you should be using queueRerender

😕 hm, maybe it’s my own confusion, but I’ve found the opposite is true.

Within the [message-list widget](https://github.com/angusmcleod/discourse-quick-messages/blob/master/assets/javascripts/discourse/widgets/message-list.js.es6#L32) I’ve created for Quick Messages, if I replace `this.scheduleRerender()` with `this.queueRerender()` it doesn’t work. It tells me `this.queueRerender()` is not a function:

 ![](https://global.discourse-cdn.com/meta/original/3X/9/b/9ba258eed1e8f45352f3a6b974cebf020ed9ae6c.png)

Likewise, if I use `this.scheduleRerender()` in the `site-header` component I get an error.

![](https://global.discourse-cdn.com/meta/original/3X/0/c/0c625d1be117d1b5bada13940b0f39be88fa654a.png)

Using `this.scheduleRerender()` in widgets and `this.queueRerender()` in components works fine however.

---

<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: [May 11, 2016, 2:27pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/32 "2016-05-11T14:27:55Z")

</div>

> [@stevenpslade](#):
>
> Is is possible to render a component inside a created widget?

Yes but it’s slow so you should avoid doing it whenever possible. Create a decorator, then use [connect](https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/widgets/decorator-helper.js.es6#L104) on it.

> [@angus](#):
>
> hm, maybe it’s my own confusion, but I’ve found the opposite is true.

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.

---

<div class="post-metadata">

### Author: ![tgxworld](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/tgxworld/32/106117_2.png) [@tgxworld](https://meta.discourse.org/u/tgxworld)
#### Post date: [May 11, 2016, 3:03pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/33 "2016-05-11T15:03:48Z")

</div>

@eviltrout I’m adding a link in the hamburger menu but I’m not sure if this is the right way. Also, do you think we can expose this in the pluginAPI?

```plaintext
api.decorateWidget("hamburger-menu:generalLinks", _ => {
   return { route: 'birthdays', label: 'birthdays.title' };
 });

```

---

<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: [May 11, 2016, 3:06pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/34 "2016-05-11T15:06:47Z")

</div>

That is the correct way. What do you mean in the plugin api? It looks like you are already calling `api.decorateWidget`. Unless you mean a shortcut for adding links like `api.addHamburgerGeneral` or something perhaps better named?

---

<div class="post-metadata">

### Author: ![tgxworld](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/tgxworld/32/106117_2.png) [@tgxworld](https://meta.discourse.org/u/tgxworld)
#### Post date: [May 11, 2016, 3:08pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/35 "2016-05-11T15:08:33Z")

</div>

Yea I was thinking of `addGeneralLink` like `addPosterIcon` since adding a link to the hamburger menu should be pretty common for plugin developers.

---

<div class="post-metadata">

### Author: ![stevenpslade](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/stevenpslade/32/53550_2.png) [@stevenpslade](https://meta.discourse.org/u/stevenpslade)
#### Post date: [May 11, 2016, 5:00pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/36 "2016-05-11T17:00:08Z")

</div>

I’m getting this error:

`Uncaught Error: Assertion Failed: You specified the templateName discourse/components/topic-notifications-button for <Ember.View:ember1314>, but it did not exist.`

I tested with `admin/templates/modal/admin_delete_flag` and it did indeed work.

---

<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: [May 11, 2016, 5:41pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/37 "2016-05-11T17:41:03Z")

</div>

Sure, go ahead and add it!

---

<div class="post-metadata">

### Author: ![joebuhlig](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/joebuhlig/32/193054_2.png) [@joebuhlig](https://meta.discourse.org/u/joebuhlig)
#### Post date: [May 12, 2016, 8:35pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/38 "2016-05-12T20:35:54Z")

</div>

Here’s another one for you, Robin. Mounting a widget in `raw.hbs`.

I’m attempting to use `mount-widget` in through a plugin-outlet (until the topic-list is ported over to the widget structure) here:

[https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/templates/list/topic-list-item.raw.hbs#L21](https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/templates/list/topic-list-item.raw.hbs#L21)

It fails and I’m unable to find an error anywhere. It’s as if it never tries to mount it.

---

<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: [May 12, 2016, 9:58pm UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/39 "2016-05-12T21:58:54Z")

</div>

You can’t embed a widget in a raw template. But don’t worry, we aren’t going to convert the topic list over. The gains over raw rendering for that part are negligible.

---

<div class="post-metadata">

### Author: ![joebuhlig](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/joebuhlig/32/193054_2.png) [@joebuhlig](https://meta.discourse.org/u/joebuhlig)
#### Post date: [May 13, 2016, 11:19am UTC](https://meta.discourse.org/t/a-tour-of-how-the-widget-virtual-dom-code-in-discourse-works/40347/40 "2016-05-13T11:19:46Z")

</div>

> [@eviltrout](#):
>
> we aren’t going to convert the topic list over.

That’s a relief. I’ve been working on a few things that this would impact and I was worried I’d end up repeating everything.

So if I can’t mount a widget (and I assume I can’t assign actions or helpers as well), is best practice to go this route:

```javascript
import TopicListItemView from `discourse/views/topic-list-item`;

TopicListItemView.reopen(click: function(e){
   // my stuff
   this._super(e);
});

```

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

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