# 无法让我的组件中的JS代码编译，错误：SyntaxError: 私有字段必须在封闭的类中使用

**URL:** https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492
**Category:** Development
**Created:** [2024年九月21日 04:52 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492 "2024-09-21T04:52:21Z")
**Posts on this page:** 15
**Page:** 1

<div class="post-metadata">

### Author: ![SupportGeek](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/supportgeek/32/373337_2.png) [@SupportGeek](https://meta.discourse.org/u/SupportGeek)
#### Post date: [2024年九月21日 04:52 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492/1 "2024-09-21T04:52:22Z")

</div>

在一天结束时，我需要在 discourse 主题列表正文中添加一个新的主题按钮。每个工程师都在抱怨他们不喜欢“开始新对话”按钮只在每个类别的主题列表底部。他们不想使用顶部的“新主题”菜单选项，因为这不会在他们当前所在的类别中启动主题。

我已经通过 llama 3.1 405B 和 gpt4（通过 [ask.discourse.com](http://ask.discourse.com)）运行了此代码，但无法获得可以编译的 JavaScript 版本。我一直收到编译错误：SyntaxError: Private field must be used in an enclosing class。

我还收到一个 widget 注册表错误，但这是因为自定义 widget 由于 js 编译错误而从未被创建。

我正在使用 Discourse Central Theme。

```plaintext
给定这个 Header 部分：
“<script type="text/discourse-plugin" version="0.8.18">
  api.createWidget('custom-new-topic-button', {
    tagName: 'div.custom-new-topic-button',

    buildKey: () => `custom-new-topic-button`,

    html() {
      return [
        this.attach('button', {
          className: 'btn btn-primary',
          action: 'createNewTopic',
          contents: 'New Topic'
        })
      ];
    },

    click() {
      const composerController = this.container.lookup("controller:composer");
      const currentCategory = this.container.lookup("controller:navigation/category").get("model.id");

      composerController.open({
        action: require("discourse/models/composer").default.CREATE_TOPIC,
        draftKey: require("discourse/models/composer").default.DRAFT,
        categoryId: currentCategory,
      });

      return false;
    },
  });

  api.decorateWidget('topic-list:before', (helper) => {
    if (api.getCurrentUser()) {
      helper.appendChild(helper.createWidget('custom-new-topic-button'));
    }
  });
</script>

”
这个 Head 部分
<script>
    {{#if currentUser}}
     <div>
      {{custom-new-topic-button}}
     </div>
   {{/if}}
</script>

和这个 CSS

.topic-list-body::before {
    content: "";
    display: block;
    position: relative; /* Important to allow absolute positioning within */
  }
  
  .topic-list-body-new-topic {
    position: absolute;
    top: 0;
    left: 0;
    padding: 10px;
    background: #f2f3f5;
    border-bottom: 1px solid #ccc;
  }
  .custom-new-topic-button .btn.btn-primary {
    background-color: #007bff;
    border-color: #007bff;
    color: #fff;
  }

```

这是我原来的代码，最新的建议是

```plaintext
<script type="text/discourse-plugin" version="0.8.18">
  api.createWidget('custom-new-topic-button', {
    tagName: 'div.custom-new-topic-button',

    buildKey() {
      return 'custom-new-topic-button';
    },

    defaultState() {
      return {};
    },

    html() {
      return [
        this.attach('button', {
          className: 'btn btn-primary',
          action: 'createNewTopic',
          contents: 'New Topic'
        })
      ];
    },

    createNewTopic() {
      const composerController = this.container.lookup("controller:composer");
      const currentCategory = this.container.lookup("controller:navigation/category").get("model.id");
      const Composer = require("discourse/models/composer").default;

      composerController.open({
        action: Composer.CREATE_TOPIC,
        draftKey: Composer.DRAFT,
        categoryId: currentCategory,
      });

      return false;
    },
  });

  api.decorateWidget('topic-list:before', (helper) => {
    if (api.getCurrentUser()) {
      helper.appendChild(helper.createWidget('custom-new-topic-button'));
    }
  });
</script>

```

---

<div class="post-metadata">

### Author: ![SupportGeek](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/supportgeek/32/373337_2.png) [@SupportGeek](https://meta.discourse.org/u/SupportGeek)
#### Post date: [2024年九月21日 04:52 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492/2 "2024-09-21T04:52:52Z")

</div>

@steven.webster @vasanth.mohan

---

<div class="post-metadata">

### Author: ![SupportGeek](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/supportgeek/32/373337_2.png) [@SupportGeek](https://meta.discourse.org/u/SupportGeek)
#### Post date: [2024年九月21日 07:58 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492/3 "2024-09-21T07:58:14Z")

</div>

搞定了。@sam 我用完了每日的调用次数……抱歉。然后我跑到我们的 LLAMA 405 B，经过一番来回折腾，我得到了可以工作的代码。我无法想象这会花我多少钱。我可能会在论坛上听到其他人的声音，比如“白痴，有一个设置可以在这个位置完成这件事”。但这对于通过持续的提示修改来获得工作代码来说，是一次有趣的练习。

其中一些关键点是（请注意，其中一些可能不准确，因为我总共只是一名 JavaScript 开发人员和 Discourse 开发人员大约两天）：

这是一个由 Discourse 托管的论坛，我没有操作系统访问权限  
这是一个插件，所以你不能直接引用 Discourse。  
你不能直接引用 Ember  
如果你要使用它，请确保将其绑定到 self

然后进入你想要的功能特性。

```plaintext
<script type="text/discourse-plugin" version="1.24.0">
  api.modifyClass('component:topic-list', {
    pluginId: 'your-plugin-id',

    didRender: function() {
      const category = this.get('category');
      if (!category) {
        console.error('未找到类别');
        return;
      }

      const currentUser = this.currentUser;
      if (!currentUser) {
        console.error('未找到当前用户');
        return;
      }

      const canCreateTopic = currentUser.can_create_topic;
      if (canCreateTopic === undefined) {
        console.error('未定义 can_create_topic');
        return;
      }

      console.log('category:', category);
      console.log('canCreateTopic:', canCreateTopic);

      if (category && canCreateTopic) {
        const topicListBody = document.querySelector('.topic-list-body');
        const listContainer = document.querySelector('.list-container');
        const topicList = document.querySelector('.topic-list');

        let container = topicListBody || listContainer || topicList;

        if (!container) {
          container = this.element; // 使用组件的元素作为容器
        }

        console.log('container:', container);

        const existingButton = container.querySelector('.new-topic-button');
        if (!existingButton) {
          const newTopicButton = document.createElement('a');
          newTopicButton.href = '#';
          newTopicButton.className = 'new-topic-button';
          newTopicButton.setAttribute('data-category-id', category.id);
          newTopicButton.textContent = 'New Topic';

          const self = this; // 存储组件的上下文
          newTopicButton.onclick = (e) => {
            e.preventDefault();
            const router = api.container.lookup('router:main');
            const url = router.generate('new-topic', { queryParams: { category: category.slug } });
            router.transitionTo(url);
          };

          container.prepend(newTopicButton);
          console.log('button added');
        }
      }
    }
  });
</script>

```

---

<div class="post-metadata">

### Author: ![SupportGeek](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/supportgeek/32/373337_2.png) [@SupportGeek](https://meta.discourse.org/u/SupportGeek)
#### Post date: [2024年九月21日 08:10 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492/4 "2024-09-21T08:10:26Z")

</div>

我说得太早了，那个版本有一个棘手的 `didRender` 问题，它不想发布到第一个类别以外的任何类别。我让 llama 修复了这个问题。

```plaintext
<script type="text/discourse-plugin" version="1.24.0">
  api.modifyClass('component:topic-list', {
    pluginId: 'your-plugin-id',

    didRender: function() {
      const category = this.get('category');
      if (!category) {
        console.error('找不到类别');
        return;
      }

      const currentUser = this.currentUser;
      if (!currentUser) {
        console.error('找不到当前用户');
        return;
      }

      const canCreateTopic = currentUser.can_create_topic;
      if (canCreateTopic === undefined) {
        console.error('未定义 can create topic');
        return;
      }

      console.log('类别:', category);
      console.log('canCreateTopic:', canCreateTopic);

      if (category && canCreateTopic) {
        const topicListBody = document.querySelector('.topic-list-body');
        const listContainer = document.querySelector('.list-container');
        const topicList = document.querySelector('.topic-list');

        let container = topicListBody || listContainer || topicList;

        if (!container) {
          container = this.element; // 使用组件的元素作为容器
        }

        console.log('容器:', container);

        let newTopicButton = container.querySelector('.new-topic-button');
        if (!newTopicButton) {
          newTopicButton = document.createElement('a');
          newTopicButton.href = '#';
          newTopicButton.className = 'new-topic-button';
          container.prepend(newTopicButton);
          console.log('已添加按钮');
        }

        newTopicButton.setAttribute('data-category-id', category.id);
        newTopicButton.textContent = '新主题';

        const self = this; // 存储组件的上下文
        newTopicButton.onclick = (e) => {
          e.preventDefault();
          const router = api.container.lookup('router:main');
          const url = router.generate('new-topic', { queryParams: { category: category.slug } });
          router.transitionTo(url);
        };
      }
    }
  });
</script>

```

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [2024年九月21日 12:07 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492/5 "2024-09-21T12:07:37Z")

</div>

您似乎已经解决了您的问题，就像我写求助帖时经常做的那样。

[New Topic Header Button - #67 by patrickemin](https://meta.discourse.org/t/new-topic-header-button/84551/67) 提供了一个示例，并且可能正是您想要的。

另外，告诉您的工程师他们可以按“c”键来撰写消息。

---

<div class="post-metadata">

### Author: ![SupportGeek](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/supportgeek/32/373337_2.png) [@SupportGeek](https://meta.discourse.org/u/SupportGeek)
#### Post date: [2024年九月21日 14:30 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492/6 "2024-09-21T14:30:03Z")

</div>

在 Central 中，开始新对话按钮位于主题列表的底部。大家都希望将按钮放在顶部。我可能会将其更改为一个小的加号，但这两张截图显示了它。

 ![The image displays a screenshot of a mobile phone browser showing a discussion forum page with various posts visible. (Captioned by AI)](https://global.discourse-cdn.com/meta/original/4X/1/0/c/10c671ddc99ce7b03bc6b027ce45634eabfca607.png)  
 ![The image shows a chat window with a user's input and response from an AI. (Captioned by AI)](https://global.discourse-cdn.com/meta/original/4X/c/7/1/c719d710bf571e51a263567d7246a85a18d18e86.png)

---

<div class="post-metadata">

### Author: ![SupportGeek](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/supportgeek/32/373337_2.png) [@SupportGeek](https://meta.discourse.org/u/SupportGeek)
#### Post date: [2024年九月21日 15:22 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492/7 "2024-09-21T15:22:48Z")

</div>

@pfaffman 我尝试了新的主题标题按钮作为第一次尝试。但是，这与新的 Discourse Central 主题不兼容。

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [2024年九月21日 15:32 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492/8 "2024-09-21T15:32:44Z")

</div>

小组件即将消失。使用小组件 API 不是一个好主意。

请使用 Glimmer 组件。

---

<div class="post-metadata">

### Author: ![SupportGeek](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/supportgeek/32/373337_2.png) [@SupportGeek](https://meta.discourse.org/u/SupportGeek)
#### Post date: [2024年九月21日 15:36 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492/9 "2024-09-21T15:36:06Z")

</div>

我们还需要从AI rags中移除大量旧内容，并等待小部件模型进行微调。甚至ask.discourse.com也倾向于小部件路线。什么将取代小部件？我将看看是否能让模型吐出考虑到这一点的代码。顺便说一句，我大部分的练习是为了看看我是否能让模型完成这项工作，因为我对js几乎一无所知:)

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [2024年九月21日 15:45 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492/10 "2024-09-21T15:45:15Z")

</div>

> [@SupportGeek](#):
>
> 顺便说一句，我大部分的练习是为了看看我是否能让一个模型来做这项工作，因为我几乎不知道 js

我懂这种感觉。

> [@SupportGeek](#):
>
> 什么将取代小部件？

Glimmer 组件。Somewhere there is a decent topic describing them.

但这里有一个创建组件的示例：

> <https://github.com/discourse/discourse-new-topic-button-theme-component/blob/main/javascripts/discourse/components/custom-header-topic-button.gjs>

下面是一个插入它的 JavaScript 方法：

> <https://github.com/discourse/discourse-new-topic-button-theme-component/blob/main/javascripts/discourse/initializers/initialize-header-new-topic-button.gjs>

插入它的另一种方法是创建连接器，然后插入组件，但初始化器方法的优点是您可以将所需的插件出口传递给初始化器，这样您就可以有一个设置来更改显示位置。

我认为您想找一个调用 `shouldDisplay`（或类似名称）的示例，如果您希望它仅在某些情况下显示（例如，如果他们已登录）。

---

<div class="post-metadata">

### Author: ![SupportGeek](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/supportgeek/32/373337_2.png) [@SupportGeek](https://meta.discourse.org/u/SupportGeek)
#### Post date: [2024年九月21日 15:47 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492/11 "2024-09-21T15:47:32Z")

</div>

我昨晚让 Llama 生成的最终代码没有使用小部件，但也没有使用 Glimmer。我今天晚些时候会再研究一下 Glimmer。

```plaintext
<script type="text/discourse-plugin" version="1.24.0">
  api.modifyClass('component:topic-list', {
    pluginId: 'your-plugin-id',

    didRender: function() {
      const category = this.get('category');
      if (!category) {
        console.error('找不到分类');
        return;
      }

      const currentUser = this.currentUser;
      if (!currentUser) {
        console.error('找不到当前用户');
        return;
      }

      const canCreateTopic = currentUser.can_create_topic;
      if (canCreateTopic === undefined) {
        console.error('未定义创建主题的权限');
        return;
      }

      console.log('category:', category);
      console.log('canCreateTopic:', canCreateTopic);

      if (category && canCreateTopic) {
        const topicListBody = document.querySelector('.topic-list-body');
        const listContainer = document.querySelector('.list-container');
        const topicList = document.querySelector('.topic-list');

        let container = topicListBody || listContainer || topicList;

        if (!container) {
          container = this.element; // 使用组件的元素作为容器
        }

        console.log('container:', container);

        let newTopicButton = container.querySelector('.new-topic-button');
        if (!newTopicButton) {
          newTopicButton = document.createElement('a');
          newTopicButton.href = '#';
          newTopicButton.className = 'new-topic-button';
          container.prepend(newTopicButton);
          console.log('已添加按钮');
        }

        newTopicButton.setAttribute('data-category-id', category.id);
        newTopicButton.textContent = '新主题';

        const self = this; // 存储组件的上下文
        newTopicButton.onclick = (e) => {
          e.preventDefault();
          const router = api.container.lookup('router:main');
          const url = router.generate('new-topic', { queryParams: { category: category.slug } });
          router.transitionTo(url);
        };
      }
    }
  });

</script>

```

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [2024年九月21日 15:50 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492/12 "2024-09-21T15:50:53Z")

</div>

注意：主题列表目前未转换为 Glimmer。

但是，如果您将组件附加到插件插座，则应使用 Glimmer。

如果必须附加到小部件，可以使用 RenderGlimmer 来实现。

示例如下：

> <https://github.com/merefield/discourse-tc-bars-tag-intersection-chooser/blob/d032a1c38200eecbd6a747f2880b4bd2bc3804e8/javascripts/discourse/widgets/layouts-tag-combo.js.es6#L33>

我恳请您在没有先自行学习基础知识的情况下，不要过度依赖 LLM，因为您需要对 LLM 的输出进行最终的质量保证。

您还需要正确地构建解决方案。

在此处阅读有关 EmberJS 的更多信息：

> **[Templates are HTML - Components - Ember Guides](https://guides.emberjs.com/release/components/)**
>
> At its core, Ember's UIs are HTML driven - every part of the UI that is shown to the user is defined in a component template somewhere in your application. Because of this, templates are central to Ember, and one of the most important parts of the...

---

<div class="post-metadata">

### Author: ![SupportGeek](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/supportgeek/32/373337_2.png) [@SupportGeek](https://meta.discourse.org/u/SupportGeek)
#### Post date: [2024年九月21日 16:55 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492/13 "2024-09-21T16:55:41Z")

</div>

谢谢。我一定会更加努力地学习，因为我对 discourse 和 js 都很陌生。

我在一家人工智能公司工作，所以这也是一次提示工程的练习。

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [2024年九月21日 17:05 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492/14 "2024-09-21T17:05:22Z")

</div>

这样你就能很好地了解他们的优缺点了。

我写了第一个 Discourse AI 机器人，名为 [Chatbot](https://meta.discourse.org/t/discourse-chatbot/256652?u=merefield)，所以我也有一些见解。

---

<div class="post-metadata">

### Author: ![sam](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/sam/32/102149_2.png) [@sam](https://meta.discourse.org/u/sam)
#### Post date: [2024年九月21日 22:40 UTC](https://meta.discourse.org/t/cannot-get-the-js-in-my-component-to-compile-syntaxerror-private-field-must-be-used-in-an-enclosing-class/327492/15 "2024-09-21T22:40:05Z")

</div>

将在系统提示中添加一些特殊指导，以确保“ask”从不推荐使用小部件
