# How to use api.onPageChange with api.createWidget?

**URL:** https://meta.discourse.org/t/how-to-use-api-onpagechange-with-api-createwidget/242744
**Category:** Development
**Tags:** ember
**Created:** [October 24, 2022, 2:55am UTC](https://meta.discourse.org/t/how-to-use-api-onpagechange-with-api-createwidget/242744 "2022-10-24T02:55:24Z")
**Posts on this page:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![rahim123](https://avatars.discourse-cdn.com/v4/letter/r/df705f/32.png) [@rahim123](https://meta.discourse.org/u/rahim123)
#### Post date: [October 24, 2022, 2:55am UTC](https://meta.discourse.org/t/how-to-use-api-onpagechange-with-api-createwidget/242744/1 "2022-10-24T02:55:24Z")

</div>

Hi there, I’m trying to add an image banner to all pages that picks randomly from two random images. It works when I add it to my theme’s **Head** section and hard reload the homepage:

_ **EDIT:** _ _My attempt that gets closer to the solution is here:_  
[How to use api.onPageChange with api.createWidget? - #2 by rahim123](https://meta.discourse.org/t/how-to-use-api-onpagechange-with-api-createwidget/242744/2)

Any idea what I’m doing wrong? Thanks!

---

<div class="post-metadata">

### Author: ![rahim123](https://avatars.discourse-cdn.com/v4/letter/r/df705f/32.png) [@rahim123](https://meta.discourse.org/u/rahim123)
#### Post date: [October 25, 2022, 3:52pm UTC](https://meta.discourse.org/t/how-to-use-api-onpagechange-with-api-createwidget/242744/2 "2022-10-25T15:52:36Z")

</div>

This is my more proper attempt. When I run it without the `api.onPageChange(()` function it works, but doesn’t randomize the image until I do a hard page refresh. When I add the `api.onPageChange(()` function it doesn’t show any image at all. Any tips on what I’m doing wrong?

```plaintext
<script type="text/discourse-plugin" version="0.8">

api.onPageChange(() => {
        doStuff();
});

function doStuff() {

const h = require("virtual-dom").h;

let banners = new Array();
banners[0]="https://example.com/uploads/default/original/1X/9d9762a4129c78c478d14ff857c3bd1f2be0322a.jpg";
banners[1]="https://example.com/uploads/default/original/1X/04ede2abd9ac117c64988a5b0bcf033474381527.jpg";

let GoTo = new Array();
GoTo[0]="https://google.com";
GoTo[1]="https://brave.com";

let Number = Math.round(1 * Math.random());
let TheLink = GoTo[Number];
let TheImage = banners[Number];

  api.createWidget("top-banner-widget", {
    tagName: "div.top-banner",
    html() {
    return h("div#top-banner", [
      h(
        "a.custom-ad-link",
        { href: TheLink },
        h("img", { src: TheImage})
      )
    ]);
    }
  });
}
</script>

<script type="text/x-handlebars" data-template-name="/connectors/above-main-container/inject-widget">
  {{mount-widget widget="top-banner-widget"}}
</script>
```

---

<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: [October 25, 2022, 5:52pm UTC](https://meta.discourse.org/t/how-to-use-api-onpagechange-with-api-createwidget/242744/3 "2022-10-25T17:52:57Z")

</div>

I think the missing piece was to trigger a widget rerender… if I recall correctly widgets rerender on interaction, so I’ve added `this.scheduleRerender();` to make it rerender on page change.

This works for me locally:

```js
api.createWidget("top-banner-widget", {
    tagName: "div.top-banner",

    html() {
        let Number = Math.round(1 * Math.random());
        
        let banners = new Array();
        banners[0]="https://placekitten.com/500/500";
        banners[1]="https://placekitten.com/500/300";

        let GoTo = new Array();
        GoTo[0]="https://google.com";
        GoTo[1]="https://brave.com";
  
        let TheLink = GoTo[Number];
        let TheImage = banners[Number];
        
        api.onPageChange(() => {
            this.scheduleRerender();
        });
  
        return h("div#top-banner", [
          h(
            "a.custom-ad-link",
            { href: TheLink },
            h("img", { src: TheImage})
          )
        ]);
    }
});

```

---

<div class="post-metadata">

### Author: ![rahim123](https://avatars.discourse-cdn.com/v4/letter/r/df705f/32.png) [@rahim123](https://meta.discourse.org/u/rahim123)
#### Post date: [October 25, 2022, 6:04pm UTC](https://meta.discourse.org/t/how-to-use-api-onpagechange-with-api-createwidget/242744/4 "2022-10-25T18:04:02Z")

</div>

Brilliant! Thanks so much, I never would have figured that out!

Here’s the complete **Head** code for the theme component:

```plaintext
<script type="text/discourse-plugin" version="0.8">

const h = require("virtual-dom").h;

api.createWidget("top-banner-widget", {
    tagName: "div.top-banner",

    html() {
        let Number = Math.round(1 * Math.random());
        
        let banners = new Array();
        banners[0]="https://example.com/uploads/default/original/1X/9d9762a4129c78c478d14ff857c3bd1f2be0322a.jpg";
        banners[1]="https://example.com/uploads/default/original/1X/04ede2abd9ac117c64988a5b0bcf033474381527.jpg";

        let GoTo = new Array();
        GoTo[0]="https://google.com";
        GoTo[1]="https://brave.com";
  
        let TheLink = GoTo[Number];
        let TheImage = banners[Number];
        
        api.onPageChange(() => {
            this.scheduleRerender();
        });
  
        return h("div#top-banner", [
          h(
            "a.custom-ad-link",
            { href: TheLink },
            h("img", { src: TheImage})
          )
        ]);
    }
});
</script>

<script type="text/x-handlebars" data-template-name="/connectors/above-main-container/inject-widget">
  {{mount-widget widget="top-banner-widget"}}
</script>
```

---

<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: [October 25, 2022, 6:34pm UTC](https://meta.discourse.org/t/how-to-use-api-onpagechange-with-api-createwidget/242744/5 "2022-10-25T18:34:00Z")

</div>

It’s also worth mentioning that this could be an Ember component. In the long-term we’re going to start relying on Ember more than our custom widget system. We’re still in the process of working on Ember updates, so our documentation on customizing Discourse is a bit behind on this.

I’ve put together a theme component that shows an example of how this can work as an Ember component: [GitHub - awesomerobot/discourse-component-example · GitHub](https://github.com/awesomerobot/discourse-component-example/)

There are 3 important files here:

1. The connector ([/javascripts/discourse/connectors/custom-header-banner-connector.hbs](https://github.com/awesomerobot/discourse-component-example/blob/main/javascripts/discourse/connectors/above-main-container/custom-header-banner-connector.hbs))

2. The component ([/javascripts/discourse/components/custom-header-banner.js](https://github.com/awesomerobot/discourse-component-example/blob/main/javascripts/discourse/components/custom-header-banner.js))

3. The component’s template ([/javascripts/discourse/templates/components/custom-header-banner.hbs](https://github.com/awesomerobot/discourse-component-example/tree/main/javascripts/discourse/templates/components))

The connector is where the component is added, so this only needs to contain `<CustomHeaderBanner />`. The component is where we detect a page changes (route changes) and setup the logic for which image/link randomly appears. The component’s template is how the data is presented in HTML.

---

<div class="post-metadata">

### Author: ![rahim123](https://avatars.discourse-cdn.com/v4/letter/r/df705f/32.png) [@rahim123](https://meta.discourse.org/u/rahim123)
#### Post date: [October 25, 2022, 6:48pm UTC](https://meta.discourse.org/t/how-to-use-api-onpagechange-with-api-createwidget/242744/6 "2022-10-25T18:48:31Z")

</div>

Ah, good to know, many thanks for that too. I confirm that it works when installed from your repo as a theme component on my site.

Alternatively, could I manually add it as an Ember theme component via the **Customize \> Install \> Create New** interface and then **Edit CSS / HTML**? That would make it easier to quickly change the images and links as needed. I assume the contents of `custom-header-banner.js` would go under **Head** , and then `<CustomHeaderBanner />` in **After Header** or **Body** , but not sure where to add the contents of `custom-header-banner.hbs` .

---

<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: [October 25, 2022, 7:04pm UTC](https://meta.discourse.org/t/how-to-use-api-onpagechange-with-api-createwidget/242744/7 "2022-10-25T19:04:03Z")

</div>

It’s a little more complicated than that to translate to a locally installed theme component, but one way to add some flexibility to a remote theme would be using theme settings ([Add settings to your Discourse theme](https://meta.discourse.org/t/add-settings-to-your-discourse-theme/82557)).

That involves adding a [settings.yml](https://github.com/awesomerobot/discourse-component-example/blob/main/settings.yml) file and updating a few values in the component and/or template. Then you get settings on your theme component admin page like this:

 ![Screen Shot 2022-10-25 at 2.56.54 PM](https://global.discourse-cdn.com/meta/original/4X/8/1/8/818e6881ef1a6ff6ec83303d1514677bb109f9e6.png)

It’s also fine to stick with the widget implementation above and update via the admin panel, but we tend to recommend using git when possible; it’s easier to share if you need to troubleshoot, and it makes it easy to track your changes.

---

<div class="post-metadata">

### Author: ![rahim123](https://avatars.discourse-cdn.com/v4/letter/r/df705f/32.png) [@rahim123](https://meta.discourse.org/u/rahim123)
#### Post date: [October 25, 2022, 7:05pm UTC](https://meta.discourse.org/t/how-to-use-api-onpagechange-with-api-createwidget/242744/8 "2022-10-25T19:05:06Z")

</div>

Excellent, really appreciate your patient and clear explanation, great to have that option too.

---

<div class="post-metadata">

### Author: ![rahim123](https://avatars.discourse-cdn.com/v4/letter/r/df705f/32.png) [@rahim123](https://meta.discourse.org/u/rahim123)
#### Post date: [November 6, 2022, 2:29am UTC](https://meta.discourse.org/t/how-to-use-api-onpagechange-with-api-createwidget/242744/10 "2022-11-06T02:29:10Z")

</div>

Hi again, I’m trying to figure out the best way to only make the widget show on a specific URL, such as the homepage.

The easy way is simply to use a plugin outlet that only exists on the homepage, which works for what I need for now (specifically, the `discovery-navigation-bar-above`). But I’m still curious about how to do this programmatically in a way that is sensitive to the specific page URL.

I found this very helpful topic, also by @awesomerobot :

> [@Add custom content that only appears on your homepage](https://meta.discourse.org/t/add-custom-content-that-only-appears-on-your-homepage/131415):
>
> A very common situation you’ll find yourself in as a theme developer is the need to create content that only shows on the homepage of your community. You might add some HTML to the “After Header” section of your theme, which will then appear on every page. You can jump through some hoops in CSS to hide this everywhere except the homepage… but instead let’s use a Discourse theme to create a component with content that is only visible on your homepage. If you’re unfamiliar with Discourse themes …

I tried to adapt this into the solution from earlier in this post:

```plaintext
        api.onPageChange((url) => {
            if (url === "/" || url === homeRoute ){
               this.scheduleRerender();
            }
        });

```

But this still makes the image appear on all pages. I also tried to put my variables and random selection code inside the `if` clause, but that doesn’t work at all.

There’s also the `<script type="text/x-handlebars" ...` section of the example, but it seems to only allow for HTML, and I don’t know how to get the variables into it from the previous script.

---

<div class="post-metadata">

### Author: ![system](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/system/32/443519_2.png) [@system](https://meta.discourse.org/u/system)
#### Post date: [December 6, 2022, 2:30am UTC](https://meta.discourse.org/t/how-to-use-api-onpagechange-with-api-createwidget/242744/11 "2022-12-06T02:30:01Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
