Я новичок в темизации Discourse и Ember.js, и в настоящее время работаю над темой, которая включает элементы навигации с основного сайта.
JSON с элементами навигации поступает через AJAX-запрос, и мне нужно отобразить его в двух местах.
Первое место — header-buttons:before. Мне удалось это сделать с помощью api.decorateWidget и вызова событий приложения site-header:force-refresh.
ajax( settings.site_navigation_links_endpoint ).then((result) => {
if (!result.length) {
return;
}
result.map((customHeaderLinksArray) => {
const anchorAttributes = {
title : customHeaderLinksArray.title,
href : customHeaderLinksArray.url,
target : "self"
};
headerLinks.push(
h(
`li.custom-header-nav-item`,
h( "a.custom-header-nav-item-link", anchorAttributes, customHeaderLinksArray.title )
)
);
});
headerNav = h("ul.custom-header-nav", headerLinks);
if( settings.site_navigation_position == "inline" ) {
api.decorateWidget("header-buttons:before", (helper) => {
return headerNav;
});
}
appEvents.trigger("site-header:force-refresh");
// Для отображения во втором месте (см. ниже)
// ...
// ...
}
Второе место — через pluginOutlet, below-site-header.
Я полагаю, что я не могу сделать то же самое, что и выше, используя api.decorateWidget, потому что это pluginOutlet, а не виджет(?).
Мои вопросы:
Как вручную вставить контент из AJAX-запроса в pluginOutlet?
Также я хотел бы узнать, как компилировать виртуальное DOM-дерево на лету после моего AJAX-вызова? Например, переменную выше, headerNav, я хотел бы получить в виде скомпилированной HTML-разметки. Не уверен, какую библиотеку или функцию использовать.
Если возможно, как также перерендерить pluginOutlet? Аналогично appEvents.trigger("site-header:force-refresh");
В теме вы добавите свой файл widget-name.js в директорию javascripts/discourse/widgets.
Подробнее о виджетах читайте здесь: A tour of how the Widget (Virtual DOM) code in Discourse works, но обратите внимание, что мы постепенно отказываемся от виджетов, поэтому любые знания, полученные в этом процессе, не будут полезны в долгосрочной перспективе.
Оставьте ваш декоратор виджета без изменений и создайте отдельный компонент Ember, который выполнит то, что вам нужно, в pluginOutlet. Мы отказываемся от виджетов в пользу компонентов Ember (на которых построена большая часть Discourse).
Этот процесс выглядит следующим образом:
Создайте файлы JS и HBS (шаблон) компонента в директории javascripts/discourse/components вашей темы.
component-name.js
component-name.hbs
Создайте компонент Ember… к сожалению, этот шаг по сути означает «изучить Ember» (Руководство по Ember), но, думаю, это даст вам общее представление для начала:
В component-name.js:
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
const endpoint = settings.site_navigation_links_endpoint;
export default class ComponentName extends Component {
@tracked navLinks = null;
@action
async fetchNavLinks() {
try {
const response = await fetch(endpoint);
const data = await response.json(); // предполагается, что это JSON
this.navLinks = data;
} catch (error) {
console.error("Ошибка:", error);
}
}
}
Это вызовет действие fetchNavLinks всякий раз, когда компонент вставляется (в данном случае, когда вы посещаете сайт Discourse и приложение рендерится). Всякий раз, когда navLinks обновляется, содержимое компонента также обновляется, так как это отслеживаемое свойство.
Если вы хотите обновлять ссылки чаще, чем при рендеринге компонента, вам нужно добавить в JS дополнительную логику… например, проверку того, соответствует ли текущий маршрут (страница) определенным условиям.
Этот компонент будет добавлен в pluginOutlet путем добавления его в коннектор в javascripts/discourse/connectors/below-site-header/my-component-connector.hbs:
Я выбрал второй подход с использованием компонента, так как виджеты постепенно выводятся из употребления. Кроме небольшой опечатки в вызове функции — в файле .hbs должно быть {{did-insert this.fetchNavLinks}} — всё работает!
Отлично, что у нас есть did-insert, это большое облегчение! Я теперь завершил задачу.