如何在每次页脚加载(或页面加载?)时触发

我正在尝试在页脚加载一个 JavaScript 表单。

我在头部有如下代码:

<script type="text/discourse-plugin" version="0.8">
  let loadScript = require("discourse/lib/load-script").default;

  api.onPageChange(() => {
    loadScript("//js.hsforms.net/forms/current.js").then(() => {
      console.log("doing the thing");
      hbspt.forms.create({
        portalId: "229276",
        formId: "a86ca9cc",
        submitButtonClass: "button orange-button hubspot-button",
        target: ".subscription-form"
      });
    });
  });
</script>

而在页脚中则有:

<div class="subscription-form clearfix">
  <h5>订阅我们的博客</h5>
</div>

页面首次加载时一切正常。但在后续页面(至少大多数情况下),表单无法加载,并报错:

Couldn't find target container .subscription-form for HubSpot Form a86ca9cc. Not rendering form onto the page

我怀疑可能是页脚在脚本执行之后才加载?

因此,我需要以某种方式延迟脚本的执行,直到页脚加载完成。

plugin-api.js 中提到:

// 监听来自 Discourse 的触发的 `AppEvent`。

api.onAppEvent("inserted-custom-html", () => {
  console.log("a custom footer was rendered");
});

也许我只需要知道应该监听哪个 AppEvent

这里有一些相关信息。

Javascripts targeting the footer doesn't work after page transitions - #4 by Johani

当你使用以下代码时:

api.onAppEvent("inserted-custom-html", () => {
  // 一些代码
});

你需要指定要 targeting 的自定义 HTML 内容,不同的事件会据此触发。

在这种情况下,你需要 targeting 页脚。你可以试试这个,看看是否对你有效吗?

api.onAppEvent("inserted-custom-html:footer", () => {
  // 一些代码
});

哈!这就对了!(当然,那是在我说服他们,他们原本想在页脚做的事情其实根本不值得做之后。)我很高兴终于搞明白了这个问题,非常感谢你的帮助;我正慢慢弄懂这些内容。(而且我想,他们现在可能会直接把页脚删掉。)

啊哈!我不确定为什么我之前没想到搜索“footer”!:man_shrugging:

那么 inserted-custom-html 指的是主题中的任何内容吗?然后我可以附加 :footer:header,或者也许是 :head_tag?这就是关键所在吗?是不是只有我这么觉得,还是说

https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/app/lib/plugin-api.js#L516

如果写成

   api.onAppEvent('inserted-custom-html:footer', () => {

会不会更有帮助?

JavaScript 和 Ember 对我来说是最难弄明白的东西,毕竟……也许自从 20 世纪 80 年代中期学习 Lisp 之后,我就一直无法完全判断什么算是“显而易见”。

不完全是。该事件仅在渲染 custom-html ember 组件时触发。

那么,我们在哪里使用这个 ember 组件,它的作用是什么?

我们主要在 应用模板 中使用它来渲染两个主题字段。

主题 after_header 选项卡

主题页脚选项卡

请注意,我们在其他地方也使用它,但在当前上下文中这些并不重要。

注意页脚也有 triggerAppEvent="true"

这就是为什么你可以使用:

api.onAppEvent("inserted-custom-html:footer", () => {
  // 一些代码
});

但你无法对 after_header 选项卡做同样的事情。Discourse 不会为该选项卡触发事件。因此,这将无法工作。

api.onAppEvent("inserted-custom-html:top", () => {
  // 一些代码
});

那么,为什么我们要使用这个 ember 组件来渲染某些主题字段呢?

简单的答案是,它让我们对渲染时机拥有更多的控制权。

举两个例子……话题列表和管理页面。

我们不想在话题列表通过无限滚动加载话题时渲染页脚。

我们也不想在 after_header 选项卡中添加的任何横幅或网站品牌标记渲染在管理页面上。

因此,使用 custom-html ember 组件让我们能够更精细地控制这类事情。

现在,回到你的问题。如果你需要在页脚渲染时执行某些操作,这就是你需要的包装器:

api.onAppEvent("inserted-custom-html:footer", () => {
  // 一些代码
});

该 API 方法适用于所有应用事件。因此,即使其中的示例并不完全完整,它仍然只是一个示例。还有很多其他 AppEvents 可以在该方法中使用。例如,你可以查看这里:

以查看一些触发的事件名称。

this.appEvents.trigger("EVENT_NAME")

以及我们如何挂钩这些事件以进行其他更改:

this.appEvents.on("EVENT_NAME")

你可以挂钩 Discourse 核心触发的任何事件到你的主题中。所以,如果我想在编辑器打开之前执行一些代码:

我可以在我的主题中添加类似这样的内容:

api.onAppEvent("composer:will-open", () => {
  console.log("这在编辑器打开之前触发");
});

话虽如此,我同意你的看法。我们应该在 API 文件中为该方法使用不同的示例。我已经记录下来以改进该示例。

这是一个非常棒的回复,表述得如此清晰,连我都能轻松理解。

关于“如何让某个功能触发”这个问题经常会出现。也许大家能在这里找到答案,但你最后的那篇帖子完全有资格成为一个独立的话题,甚至可以考虑添加到或链接到主题和/或插件指南中。

而且,既然现在“主题”大多指代“Ember”,而“插件”大多指代“Rails”,或许有人可以考虑对它们进行一些重构。回想一下刚开始的时候,有很多 Ember 功能需要依赖插件,而现在这些功能完全可以通过主题组件来实现,对吧?如今,Ember 相关功能既可以在插件中实现,也可以在主题组件中实现。

@Johani,关于“如何触发或被触发”这个问题的另一个版本是:我在一个插件中使用了 Custom Header Links 的某个版本。它链接到由我的插件添加的独立模型中创建的一些项目(“服务器”)。当创建一个 server 时,我希望重新构建头部链接,使其指向最近创建的两个服务器。我现在在一个初始化器中实现这个功能,它运行正常,但只有在添加服务器后刷新页面才能更新。

你之前教过我如何添加和监听触发器,所以我以为也许能解决这个问题,但执行该工作的页面位于 discourse-subscriptions 中。也许我想做的是向 discourse-subscriptions 提交一个 PR,在其中添加:

this.appEvents.trigger("purchase-complete")

在购买完成后(购买操作会触发将用户添加到某个组,进而触发创建服务器并将用户从该组中移除)。或者,如果能在购买完成后,或者在用户点击“购买完成”模态框中的“确定”按钮时直接触发页面刷新,那也很好,但我不知道该如何实现(我之前尝试的方法导致页面无限循环刷新……)

所以,也许可以在这段代码中:

https://github.com/discourse/discourse-subscriptions/blob/main/assets/javascripts/discourse/controllers/s-show.js.es6#L71-L81

在将 loading 设置为 false 之后,添加:

this.appEvents.trigger("successful-transaction")

然后我可以在我的初始化器中添加:

this.appEvents.on("successful-transaction")

来执行头部链接的操作?

我想一旦我这样做,就需要采用不同的方式,因为我担心这段代码:

api.decorateWidget("header-buttons:before", (helper) => {
  return helper.h("ul.pfaffmanager-header-links", headerLinks);
});

是在 header-buttons:before 位置添加内容而不是替换它们,所以每次触发时我都会得到更多的链接?

这又帮到我了!我需要做的是在帖子流更新时加载一个脚本。我找到的所有示例都在可以访问 appEvent 的组件中,通过 this。我终于找到了这个,现在在一个 apiInitializer 中,我可以

  api.onAppEvent('post-stream:refresh', args => {
   // do some stuff!!!
  });

现在这些广告会在每批帖子加载时加载。我已经 :heart: 了你的帖子,所以不得不写一个更详细的感谢。 :wink:

这是我最喜欢的主题之一 :heart: 我时不时地回来 :sweat_smile: 我真的很感激!:hugs: