如何使用 api.onPageChange 和 api.createWidget?

您好,我正在尝试将一个图片横幅添加到所有页面,该横幅会从两张随机图片中随机选择一张。当我将其添加到主题的Head部分并强制重新加载主页时,它就能正常工作:

编辑: 我更接近解决方案的尝试在这里:

您知道我哪里做错了吗?谢谢!

这是我更正过的尝试。当我运行它而不带 api.onPageChange(() 函数时,它可以工作,但直到我进行硬页面刷新后才随机化图像。当我添加 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>
1 个赞

我认为缺失的一环是触发小部件重新渲染……如果我没记错的话,小部件会在交互时重新渲染,所以我添加了 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 })
          )
        ]);
    }
});
5 个赞

太棒了!非常感谢,我绝对想不到这一点!

这是主题组件完整的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>
1 个赞

值得一提的是,这也可以是一个 Ember 组件。从长远来看,我们将更多地依赖 Ember 而不是我们自定义的小部件系统。我们仍在处理 Ember 更新,因此我们关于自定义 Discourse 的文档在这方面有点滞后。

我整理了一个主题组件,展示了这如何作为 Ember 组件工作的示例:GitHub - awesomerobot/discourse-component-example

这里有 3 个重要的文件:

  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 中呈现数据。

3 个赞

好的,很高兴知道,也非常感谢。我确认在我的网站上将其作为主题组件从您的存储库安装时,它确实有效。

另外,我是否可以通过“自定义 > 安装 > 创建新”界面,然后“编辑 CSS / HTML”手动将其添加为 Ember 主题组件?这样可以更轻松地根据需要快速更改图像和链接。我假设 custom-header-banner.js 的内容会放在“Head”下,然后 \u003cCustomHeaderBanner /\u003e 放在“After Header”或“Body”下,但不确定在哪里添加 custom-header-banner.hbs 的内容。

1 个赞

将其翻译成本地安装的主题组件要复杂一些,但为远程主题添加一些灵活性的一种方法是使用主题设置(https://meta.discourse.org/t/add-settings-to-your-discourse-theme/82557)。

这涉及到添加一个 settings.yml 文件(https://github.com/awesomerobot/discourse-component-example/blob/main/settings.yml`),并更新组件和/或模板中的一些值。然后,您将在主题组件管理页面上看到如下设置:

使用上面的 widget 实现并通过管理面板进行更新也是可以的,但我们倾向于建议尽可能使用 git;如果需要进行故障排除,它更容易共享,并且可以轻松跟踪您的更改。

1 个赞

太好了,非常感谢您耐心清晰的解释,很高兴也有这个选项。

1 个赞

你好,我又来了,我正在尝试找出一种最佳方法,只在特定 URL(例如主页)上显示小部件。

简单的方法是使用仅存在于主页上的插件出口,这对我目前的需求来说是有效的(特别是 discovery-navigation-bar-above)。但我仍然想知道如何以一种能够识别特定页面 URL 的编程方式来实现这一点。

我找到了一个非常有用的主题,也是由 @awesomerobot 提出的:

我尝试将其改编成此帖子前面提供的解决方案:

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

但这仍然会导致图片出现在所有页面上。我还尝试将我的变量和随机选择代码放在 if 子句中,但这根本不起作用。

还有一个示例中的 <script type="text/x-handlebars" ... 部分,但它似乎只允许使用 HTML,而且我不知道如何从前面的脚本中将变量传入其中。

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