# Встраивайте комментарии из Discourse в ваше одностраничное приложение

**URL:** <https://meta.discourse.org/t/embed-comments-from-discourse-in-your-single-page-app/231798>\
**Category:** Administrators\
**Tags:** embedding, how-to\
**Created:** [03.Июль.2022 13:45:37 UTC](https://meta.discourse.org/t/embed-comments-from-discourse-in-your-single-page-app/231798 "2022-07-03T13:45:37Z")\
**Posts on this page:** 1\
**Showing post:** 1

<div class="post-metadata">

**Author:** ![MK2k](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/mk2k/32/49271_2.png) [@MK2k](https://meta.discourse.org/u/MK2k)\
**Post date:** [03.Июль.2022 13:45:38 UTC](https://meta.discourse.org/t/embed-comments-from-discourse-in-your-single-page-app/231798/1 "2022-07-03T13:45:38Z")

</div>

При попытке внедрения [[1]](https://meta.discourse.org/t/embedding-discourse-comments-via-javascript/31963) в ваше одностраничное приложение (SPA), которое не поддерживает серверный рендеринг страниц, вы неизбежно столкнётесь с проблемами, см.: [[2]](https://meta.discourse.org/t/discourse-comments-in-vuejs/106128)

Поэтому после некоторых экспериментов я хочу предложить следующий подход. Пример приведён для Vue.js, однако его можно легко адаптировать под другие фреймворки/библиотеки.

**Примечание** : Я буду использовать термин **статьи блога** , где должен быть встроен раздел комментариев Discourse. Но, конечно, это также могут быть отдельные страницы вашего сайта.

## 1. Проблемы в [[1]](https://meta.discourse.org/t/embedding-discourse-comments-via-javascript/31963)

### 1.1. `javascripts/embed.js` не может работать с контентом, рендерящимся на стороне клиента

Фрагмент `<script>...</script>`, который предлагается в [[1]](https://meta.discourse.org/t/embedding-discourse-comments-via-javascript/31963) вставить в ваш HTML, не будет частью реализации, к которой мы стремимся здесь. Мы будем использовать некоторые части `javascripts/embed.js`, предоставляемые вашим экземпляром Discourse, в качестве функций внутри нашего SPA.

### 1.2. Discourse не может парсить контент, рендерящийся на стороне клиента

Discourse автоматически создаёт темы для каждой статьи блога и пытается получить доступ к исходному URL (статьи блога), чтобы определить заголовок и содержимое. Это не работает с SPA, потому что Discourse получит часть без JavaScript, например: `Нам очень жаль, но этот сайт не работает должным образом без включенного JavaScript. Пожалуйста, включите его, чтобы продолжить.`

Мы будем использовать плагин RSS Polling для предоставления необходимых данных и создания тем за нас.

## 2. Реализация

### 2.1 RSS Polling и RSS/Atom-канал

Создайте конечную точку на вашем сайте, которая предоставляет RSS или Atom-канал для плагина RSS Polling. Эта конечная точка может быть либо просто **статическим файлом в формате XML** , либо **функцией на стороне сервера** , предоставляющей контент в формате XML, пример:

**URL** : `https://mysite.com/blog.atom`

**Содержимое** :

```plaintext
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title>Статьи блога моего сайта</title>
  <link href="https://mysite.com/blog/"/>
  <updated>2022-07-03T09:02:48.721Z</updated>
  <id>urn:uuid:790c1857-b968-49cc-9fbd-bf7afe3552c2</id>

  <entry>
    <title>Статья о технологиях</title>
    <author>
      <name>Ваше Имя Здесь</name>
    </author>
    <link href="https://mysite.com/blog/an-article-about-technology"/>
    <id>urn:uuid:f6cc13e4-d2eb-4385-af28-c867a94f48dc</id>
    <published>2022-07-03T00:00:00Z</published>
    <updated>2022-07-03T00:00:00Z</updated>
    <summary>Давайте обсудим некоторые технологии в этой статье.</summary>
  </entry>

</feed>

```

Установите плагин RSS Polling для Discourse согласно инструкциям для [[3] (Discourse в облаке)](https://meta.discourse.org/t/configure-the-discourse-rss-polling-plugin/156387) или [[4] (самостоятельный хостинг)](https://meta.discourse.org/t/install-plugins-in-discourse/19157)

Рекомендуемые настройки для RSS Polling в Администрирование → Настройки → Плагины:

| Ключ | Значение |
| --- | --- |
| rss polling enabled | true |
| rss polling frequency | 10 (т.е. 10 минут) |

Добавьте новый канал в конфигурации плагина RSS Polling в Администрирование → Плагины → RSS Polling

Настройте его согласно инструкциям для [[3]](https://meta.discourse.org/t/configure-the-discourse-rss-polling-plugin/156387):

| Ключ | Значение |
| --- | --- |
| **URL** | `https://mysite.com/blog.atom` |
| **Фильтр по категории** | `<это необязательно>` |
| **Автор** | `<определите автора автоматически генерируемых тем>` |
| **Категория** | `<определите категорию/категории, где будут публиковаться автоматически генерируемые темы>` |
| **Теги** | `<это необязательно>` |

### 2.2 Конфигурация маршрутизатора SPA

Discourse использует последнюю часть пути URL в качестве идентификатора для отдельной статьи блога.

Примеры:

`https://mysite.com/blog/an-article-about-technology`  
`https://mysite.com/blog/another-article-about-cats`

Настройте ваш маршрутизатор SPA соответствующим образом, чтобы отдельные статьи блога соответствовали отдельным URL.

Также: Discourse предоставит ссылку обратно на отдельную статью блога, поэтому с точки зрения удобства использования (UX) хорошо, чтобы при клике на неё ваш сайт показывал actual статью.

## 2.3 Компонент статьи

Как уже говорилось, мы не можем использовать подход с `<script></script>` из [[1]](https://meta.discourse.org/t/embedding-discourse-comments-via-javascript/31963). Поэтому мы реализуем iframe и некоторые функции из `javascripts/embed.js` в нашем компоненте:

**Article.vue**

Вещи, которые вам следует отредактировать:

| элемент | описание |
| --- | --- |
| #YOUR-DISCOURSE-URL# | URL вашего экземпляра Discourse, например, `discourse.mysite.com` |
| #YOUR-SITE-URL# | URL вашего сайта, например, `mysite.com`, возможно также `%2Fblog%2F`, если путь к статьям блога не `/blog/` |

```js
<template>
  <div id="article">

    <!-- ваш отформатированный контент статьи здесь -->

    <iframe
      v-if="slug"
      v-bind:src="`https://#YOUR-DISCOURSE-URL#/embed/comments?embed_url=https%3A%2F%2F#YOUR-SITE-URL#%2Fblog%2F${slug}%2F`"
      id="discourse-embed-frame"
      width="100%"
      v-bind:height="`${iframeHeight}px`"
      frameborder="0"
      scrolling="no"
      referrerpolicy="no-referrer-when-downgrade"
    />
  </div>
</template>

<script>
export default {
  data: () => ({
    slug: null, // слаг статьи блога, например, "an-article-about-technology", пока маршрут "https://mysite.com/blog/an-article-about-technology"
    iframeHeight: 0 // Discourse сообщит нам точную высоту iframe (см.: метод receiveMessage)
  }),

  methods: {
    // коммуникация через iframe
    receiveMessage(event) {
      if (!event) {
        return;
      }
      if (!(event.origin || "").includes("#YOUR-DISCOURSE-URL#")) {
        return;
      }

      if (event.data) {
        if (event.data.type === "discourse-resize" && event.data.height) {
          this.iframeHeight = +event.data.height;
        }

        if (event.data.type === "discourse-scroll" && event.data.top) {
          // найти смещение iframe
          const destY = this.findPosY(this.$refs["discourse-embed-frame"]) + event.data.top;
          window.scrollTo(0, destY);
        }
      }
    },

    // Спасибо http://amendsoft-javascript.blogspot.ca/2010/04/find-x-and-y-coordinate-of-html-control.html
    findPosY(obj) {
      var top = 0;
      if (obj.offsetParent) {
        while (1) {
          top += obj.offsetTop;
          if (!obj.offsetParent) break;
          obj = obj.offsetParent;
        }
      } else if (obj.y) {
        top += obj.y;
      }
      return top;
    }
  },

  async created() {
    this.slug = this.$router.currentRoute.path.split("/")[2];
  },

  mounted() {
    window.addEventListener("message", this.receiveMessage);
  },
  beforeDestroy() {
    window.removeEventListener("message", this.receiveMessage);
  }
}

```

### 2.4 Конфигурация встраивания Discourse

Теперь, когда опрос RSS/Atom-каналов настроен и реализация на вашем сайте готова, мы наконец можем настроить встраивание на экземпляре Discourse.

Перейдите в Администрирование → Настройка → Встраивание и добавьте хост:

| Ключ | Значение |
| --- | --- |
| Разрешённые хосты | базовый URL вашего сайта, например, “[mysite.com](http://mysite.com)” |
| Имя класса | необязательное имя класса для стилизации |
| Список разрешённых путей | например, “/blog/.\*” |
| Публиковать в категорию | та же категория, что и в настройках RSS Polling Category |

## 3. Заключительные замечания

Опрос RSS/Atom-каналов, а также сам Discourse создадут новую тему, если она не существует для отдельной статьи блога. Убедитесь, что опрос RSS/Atom-каналов выполняется первым (т.е. подождите, пока тема не будет создана, прежде чем посещать статью блога на вашем сайте).

Причина: Discourse не может парсить заголовок и краткое содержание, поэтому тема будет называться `mysite.com` с кратким содержанием `Нам очень жаль, но этот сайт не работает без javascript.` 😉

Если по какой-то причине Discourse сработал первым, вы можете просто удалить тему и подождать, пока сработает опрос RSS/Atom-каналов.

всего доброго

– MK2k

---

_[View the full topic](https://meta.discourse.org/t/embed-comments-from-discourse-in-your-single-page-app/231798)._
