Рендер поста над контейнером discovery-list-container-top Outlet

Я новичок в этом, извините.

Я попросил Ask Discourse:

в теме я хочу отрендерить первый пост из двух тем выше списка тем в Outlet discovery-list-container-top

он дал мне несколько версий, которые на мой взгляд должны были работать. Все они вызывают ошибку в предпросмотре. Я спросил Ask Discourse:

как мне поместить пример кода во вкладку JS вместо файлов?

он дал мне эту версию

import { apiInitializer } from "discourse/lib/api";
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";

class FirstPosts extends Component {
  @tracked posts = [];

  constructor() {
    super(...arguments);
    this.loadPosts();
  }

  async loadPosts() {
    let topicIds = [123, 456]; // замените на ID ваших тем
    let posts = [];
    for (let id of topicIds) {
      let res = await fetch(`/t/${id}.json`);
      let data = await res.json();
      if (data.post_stream && data.post_stream.posts.length > 0) {
        posts.push(data.post_stream.posts[0]);
      }
    }
    this.posts = posts;
  }

  static template = <template>
    {{#each this.posts as |post|}}
      <div class="first-post-preview">
        <h4>{{post.topic_title}}</h4>
        {{{post.cooked}}}
      </div>
    {{/each}}
  </template>;
}

export default apiInitializer("0.11.1", api => {
  api.renderInOutlet("discovery-list-container-top", FirstPosts);
});

и когда я вставил код ошибки в Ask Discourse, он дал мне эту версию

import { apiInitializer } from "discourse/lib/api";

// Глобальное хранилище данных (костыль, но работает для вкладки JS)
window.firstPostsData = [];

fetch("/t/123.json")
  .then(r => r.json())
  .then(data => window.firstPostsData.push(data.post_stream.posts[0]));

fetch("/t/456.json")
  .then(r => r.json())
  .then(data => window.firstPostsData.push(data.post_stream.posts[0]));

export default apiInitializer(api => {
  api.renderInOutlet("discovery-list-container-top", 
    <template>
      {{#each (theme-prefix "firstPostsData" window=true) as |post|}}
        <div class="first-post-preview">
          <h4>{{post.topic_title}}</h4>
          {{{post.cooked}}}
        </div>
      {{/each}}
    </template>
  );
});

Может ли кто-нибудь сказать мне, что я делаю не так?

ошибка в консоли была следующей:

Ошибка компиляции: SyntaxError: /theme-4/discourse/api-initializers/theme-initializer.gjs:

вот что показало в предпросмотре

Спасибо

Я немного доработал код, и теперь он, кажется, работает:

import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { htmlSafe } from "@ember/template";
import { ajax } from "discourse/lib/ajax";
import { apiInitializer } from "discourse/lib/api";

class FirstPosts extends Component {
  @tracked posts = [];

  constructor() {
    super(...arguments);
    this.loadPosts();
  }

  async loadPosts() {
    let topicIds = [123, 1807]; // замените на ID ваших тем
    let posts = [];
    for (const id of topicIds) {
      const data = await ajax(`/t/${id}.json`);
      if (data.post_stream && data.post_stream.posts.length > 0) {
        posts.push(data.post_stream.posts[0]);
      }
    }
    this.posts = posts;
  }

  <template>
    {{#each this.posts as |post|}}
      <div class="first-post-preview">
        <h4>{{post.topic_title}}</h4>
        {{htmlSafe post.cooked}}
      </div>
    {{/each}}
  </template>;
}

export default apiInitializer((api) => {
  api.renderInOutlet("discovery-list-container-top", FirstPosts);
});

РЕДАКТИРОВАНО: Теперь используется ajax() и htmlSafe вместо тройных фигурных скобок.

Спасибо, Нейт! Я пытаюсь понять, что именно ты изменил.

Твой код, кажется, совпадает с моей первой версией.

Ты заменил let на const в цикле for.

Это верно?

Моя версия с let выдает ошибку:

for (let id of topicIds) {
      let res = await fetch(`/t/${id}.json`);
      let data = await res.json();
      if (data.post_stream && data.post_stream.posts.length > 0) {
        posts.push(data.post_stream.posts[0]);
      }
    }

Версия Нейта работает:

for (let id of topicIds) {
      const res = await fetch(`/t/${id}.json`);
      const data = await res.json();
      if (data.post_stream && data.post_stream.posts.length > 0) {
        posts.push(data.post_stream.posts[0]);
      }
    }

Может ли кто-нибудь объяснить, в чём проблема? Почему let не работает, а const — работает?

Спасибо ещё раз, Нейт.

Я попробовал let, и результат остался прежним. Просто обычно я использую const, когда применяю fetch() или ajax().

Полагаю, ошибка возникает из-за строки static template = . С ней я получаю:

[THEME 10283 'Pokemon Theme'] Error: connector component has no associated template. Ensure the template is colocated or authored with gjs.
    at h (plugin-connectors.js:35:11)
    at eY.renderInOutlet (plugin-api.gjs:1116:5)
    at todo.gjs:43:7
    at eH (plugin-api.gjs:3363:10)
    at Object.initialize (api.js:21:14)
    at i.initialize (app.js:265:28)
    at index.js:379:19
    at e.each (index.js:183:7)
    at e.walk (index.js:112:10)
    at e.each (index.js:59:20)
    at e.topsort (index.js:65:10)
    at iL._runInitializer (index.js:392:11)
    at iL.runInstanceInitializers (index.js:377:10)
    at l._bootSync (instance.js:116:22)
    at iL.didBecomeReady (index.js:784:18)
    at invoke (index.js:262:14)
    at m.flush (index.js:180:11)
    at g.flush (index.js:334:19)
    at z._end (index.js:762:32)
    at _boundAutorunEnd (index.js:499:12)

А если использовать <template> напрямую, ошибки нет. Мне кажется, что использование static template здесь некорректно, поэтому просто использование тега template работает.

Я глупо удалил версию, которую тестировал, и теперь не могу запустить последнюю версию от Нейта.

theme-initializer.gjs:18 Uncaught (in promise) ReferenceError: constid is not defined
    at n.loadPosts (theme-initializer.gjs:18:10)
    at new n (theme-initializer.gjs:12:10)
    at h.createComponent (index.js:259:12)
    at $.create (index.js:419:28)
    at Object.evaluate (index.js:985:23)
    at Object.evaluate (index.js:103:106)
    at tr.evaluateSyscall (index.js:2873:20)
    at tr.evaluateInner (index.js:2852:64)
    at tr.evaluateOuter (index.js:2849:10)
    at tH.next (index.js:4167:45)
    at tH._execute (index.js:4157:21)
    at tH.execute (index.js:4133:41)
    at tq.sync (index.js:4194:120)
    at tz.render (index-BCp6wOJU.js:4636:43)
    at index-BCp6wOJU.js:4934:16
    at eX (index.js:2414:7)
    at tG._renderRoots (index-BCp6wOJU.js:4914:7)
    at tG._renderRootsTransaction (index-BCp6wOJU.js:4962:12)
    at tG._renderRoot (index-BCp6wOJU.js:4904:10)
    at tG._appendDefinition (index-BCp6wOJU.js:4828:10)
    at tG.appendOutletView (index-BCp6wOJU.js:4818:10)
    at invoke (index.js:264:14)
    at m.flush (index.js:180:11)
    at g.flush (index.js:334:19)
    at z._end (index.js:762:32)
    at _boundAutorunEnd (index.js:499:12)

Эта первая версия — единственная, которую мне удаётся запустить.

import { apiInitializer } from "discourse/lib/api";
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";

class FirstPosts extends Component {
  @tracked posts = [];

  constructor() {
    super(...arguments);
    this.loadPosts();
  }

  async loadPosts() {
    let topicIds = [98, 239]; // замените на ID ваших тем
    let posts = [];
    for (let id of topicIds) {
      const res = await fetch(`/t/${id}.json`);
      const data = await res.json();
      if (data.post_stream && data.post_stream.posts.length > 0) {
        posts.push(data.post_stream.posts[0]);
      }
    }
    this.posts = posts;
  }

  <template>
    {{#each this.posts as |post|}}
      <div class="first-post-preview">
        <h4>{{post.topic_title}}</h4>
        {{{post.cooked}}}
      </div>
    {{/each}}
  </template>;
}

export default apiInitializer((api) => {
  api.renderInOutlet("above-main-container", FirstPosts);
});

Я пытаюсь сделать это через вкладку JS темы.

Может, мне стоит перейти на тему на GitHub? Возможно, проблема именно в этом ограничении?

Да, let и const здесь фактически идентичны, так что это не проблема.

Я считаю (в компонентах тем и плагинах), что шаблоны с таким синтаксисом можно включать только в отдельный файл .gjs, так как этот суффикс указывает интерпретатору на необходимость его предварительной компиляции. Не уверен, как это влияет на встроенную онлайн-систему тем.

В любом случае, переход на GitHub — очень хорошая идея.

Спасибо за этот совет, я сейчас пытаюсь это сделать. То, что я уже продвинулся, вселяет во мне уверенность. Код Нейта выше на самом деле работает и размещает две темы сверху. Я создам новую тему с идеей своей темы и постараюсь задокументировать то, что узнаю. ask.discourse.org — это просто потрясающе!