Как использовать api.onPageChange с api.createWidget?

Привет! Я пытаюсь добавить баннер с изображением на все страницы, который случайным образом выбирает одно из двух изображений. Это работает, когда я добавляю его в раздел Head моей темы и делаю полную перезагрузку главной страницы:

РЕДАКТИРОВАНИЕ: Моя попытка, которая ближе к решению, находится здесь:
How to use api.onPageChange with api.createWidget? - #2 by rahim123

Есть ли какие-то идеи, что я делаю не так? Спасибо!

Это моя более корректная попытка. Когда я запускаю код без функции api.onPageChange((), он работает, но изображение не меняется случайно, пока я не обновлю страницу полностью (hard refresh). Когда я добавляю функцию api.onPageChange((), изображение вообще не отображается. Есть какие-то советы, что я делаю не так?

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

Кажется, не хватало именно вызова перерисовки виджета… Если я правильно помню, виджеты перерисовываются при взаимодействии, поэтому я добавил this.scheduleRerender();, чтобы обеспечить перерисовку при смене страницы.

У меня это работает локально:

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

Великолепно! Большое спасибо, я бы никогда сам этого не понял!

Вот полный код Head для компонента темы:

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

Стоит также упомянуть, что это может быть компонент Ember. В долгосрочной перспективе мы начнем больше полагаться на Ember, чем на нашу собственную систему виджетов. Мы все еще работаем над обновлениями Ember, поэтому наша документация по кастомизации Discourse в этом аспекте немного отстает.

Я подготовил компонент темы, который показывает пример того, как это может работать как компонент Ember: GitHub - awesomerobot/discourse-component-example · GitHub

Здесь есть три важных файла:

  1. Коннектор (/javascripts/discourse/connectors/custom-header-banner-connector.hbs)

  2. Компонент (/javascripts/discourse/components/custom-header-banner.js)

  3. Шаблон компонента (/javascripts/discourse/templates/components/custom-header-banner.hbs)

В коннекторе добавляется компонент, поэтому здесь достаточно указать только <CustomHeaderBanner />. В компоненте мы отслеживаем изменения страницы (смену маршрута) и настраиваем логику для случайного отображения изображения или ссылки. Шаблон компонента определяет, как данные отображаются в HTML.

Ах, хорошо знать, большое спасибо и за это. Подтверждаю, что всё работает, когда я устанавливаю это из вашего репозитория как компонент темы на своём сайте.

Альтернативно, могу ли я вручную добавить его как компонент темы Ember через интерфейс Настроить > Установить > Создать новый, а затем Изменить CSS / HTML? Это упростило бы быстрое изменение изображений и ссылок по мере необходимости. Я предполагаю, что содержимое custom-header-banner.js должно находиться в разделе Head, а тег <CustomHeaderBanner /> — в разделе После заголовка или Body, но не уверен, куда добавлять содержимое custom-header-banner.hbs.

Перевод на локально установленный компонент темы немного сложнее, но один из способов добавить гибкости удалённой теме — использовать настройки темы (Add settings to your Discourse theme).

Это требует добавления файла settings.yml и обновления нескольких значений в компоненте и/или шаблоне. После этого на странице администрирования компонента темы появятся настройки, как показано ниже:

Также допустимо использовать реализацию виджета, описанную выше, и обновлять её через панель администратора, но мы, как правило, рекомендуем по возможности использовать Git: это упрощает обмен при необходимости устранения неполадок и позволяет легко отслеживать внесённые изменения.

Отлично, очень ценю ваше терпение и понятное объяснение. Приятно, что есть такая возможность.

Снова привет! Я пытаюсь понять, как лучше всего настроить виджет так, чтобы он отображался только на определённом URL, например, на главной странице.

Самый простой способ — использовать выходной канал плагина, который существует только на главной странице. Это работает для моих текущих задач (в частности, для discovery-navigation-bar-above). Однако меня всё ещё интересует, как сделать это программно с учётом конкретного URL страницы.

Я нашёл очень полезную тему, также от @awesomerobot:

Я попытался адаптировать это решение к коду, упомянутому ранее в этом посте:

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

Но это всё равно заставляет изображение появляться на всех страницах. Я также пробовал поместить свои переменные и код случайного выбора внутрь блока if, но это вообще не работает.

Также в примере есть секция <script type="text/x-handlebars" ..., но, похоже, она допускает только HTML, и я не знаю, как передать туда переменные из предыдущего скрипта.