Получите доступ к элементам из родительского компонента Glimmer

Я добавляю компонент в before-topic-list-body. Мне нужно, чтобы он получал данные либо из категории, либо из темы (и я добавлю нужные мне данные в тот сериализатор, который будет более уместным).

Я потратил на это пару часов и понял, что зашел в тупик. Есть большая вероятность, что это проблема, которую можно решить за 2–5 минут, если кто-то даст мне подсказку.

Я смутно помню, что раньше думал или слышал что-то об этом, но сейчас я в тупике.

:thinking: хм-м-м

возможно, в этой теме есть что-то полезное?

Спасибо, @Lilly! Идея неплохая, но я опасался, что currentUser относится ко всей странице, а не только к тому месту, где вставляется компонент, и, кажется, я прав. Чтобы понять, что доступно таким образом, я выполнил grep по официальным плагинам и получил этот список сервисов (если бы я был умнее, то знал бы, как найти их в исходном коде Discourse, но я не такой):

  @service adminPluginNavManager;
  @service appEvents;
  @service capabilities;
  @service chatApi;
  @service composer;
      @service currentUser;
  @service currentUser;
  @service dialog;
  @service dTemplatesModal;
  @service encryptWidgetStore;
  @service hCaptchaService;
  @service imageCaptionPopup;
  @service menu;
  @service messageBus;
  @service modal;
  @service moreTopicsPreferenceTracking;
  @service presence;
  @service quickSearch;
  @service router;
  @service search;
  @service searchPreferencesManager;
  @service session;
  @service site;
  @service siteSettings;
  @service store;
  @service taskActions;
  @service toasts;
  @service upgradeStore;
  @service userFieldValidations;
  @service whosOnline;

Я нашёл это. Вот откуда это берётся в моём контексте:

<RatingOne @scaleValue={{this.scaleValue}} @topic={{this.parentView._parentView.topic}} />

Так что я могу использовать parentView и _parentView, чтобы подняться вверх до темы! Это именно та магия, которую я искал (и которая, как я думаю, решит ещё одну проблему в другом проекте). Теперь мне осталось только поместить всё это в контроллер темы, что, надеюсь, всё то, что я (по крайней мере, в основном) умею делать!

Вывелось ли это сообщение об устаревании в консоль? Оно должно было появиться, так как мы скоро удалим доступ к parentView из PluginOutlet. Фактически, это настолько не рекомендуется, что в интерфейсе для администраторов должно отображаться предупреждающее баннер :sweat_smile:

Если вы не увидели никаких предупреждений, пожалуйста, сообщите нам об этом!

(parentView является частью классической системы компонентов Ember, от которой мы отказываемся)

Если вам нужно получить доступ к информации из более высокого уровня в иерархии, то её либо нужно передать как аргумент в Plugin Outlet, либо использовать один из доступных сервисов (например, currentUser или router).

А, я понял, что произошло. Вы используете ._parentView, а не .parentView. Версия с подчеркиванием позволяет обойти предупреждение об устаревании. Однако это перестанет работать в ближайшие несколько недель.

Этот PR предотвращает подобные обходные пути, поэтому и .parentView, и ._parentView будут вызывать предупреждение об устаревании:

Спасибо, @david!

Нет. Даже после того, как я поискал некоторое время. Я использую версию 27eea3250cbde2e8bca754f445bee403c059eba3

<RatingOne @scaleValue={{this.scaleValue}} @topic={{this.parentView._parentView.topic}} />
<td class="rating-one">
  <span class="rating-title">{{this.ratingName}}</span> 
  Это тема {{this.topic.id}}
  Это категория {{this.topic.category.title}} (id категории: {{this.topic.category_id}})
  {{log "оценка этого" this}}
  {{log "значение шкалы" this.scaleValue}}
  {{log "значение оценки" this.ratingValue}}
  {{log "категория" this.topic.category_id}}
    <form>
        {{!-- {{this.ratingLow}} --}}
       {{#each this.ratingOptions as |option|}}
            <input type="radio" name="rating" value={{option.value}} checked={{if (eq option.value this.ratingValue) "checked"}}> <span class="rotated-label">{{option.label}}</span>
        {{/each}}
        {{!-- {{this.ratingHigh}} --}}
    </form>
</td>

А, понятно. Значит, могу ли я использовать router, чтобы получить доступ к теме или категории? То, что мне (как я думаю) нужно сделать, — это передать эти ratingOptions в сериализатор темы или категории, чтобы я мог добавить эти множественные оценки тем в список тем. Это позволит оценивать их, не заходя в сами темы (предположительно, пользователи уже знакомы с ними, поэтому им не нужно заходить в тему, чтобы понять, за что они голосуют).

Хм. Похоже, что нет.

Хорошо, это должно быть исправлено в этом коммите. Можете подтвердить, что вы теперь видите предупреждение?

В идеале такие вещи должны быть доступны как аргументы в плагин-аутлете.

Вы упоминали ‘before-topic-list-body’ выше. У него есть все эти аргументы:

{{log "rating this parent" this.parentView._parentView.topic}}

вызывает некрасивую ошибку.

<RatingOne @scaleValue={{this.scaleValue}} @passedRouter={{this.router}} @topic={{this.parentView}} />

кажется, не вызывает ошибки.

Мне действительно нужен above-topic-list-item, но благодаря вашему отличному примеру я смог найти, что тема должна находиться в outletArgs,

https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/components/topic-list/topic-list-item.gjs#L284

Ха! Я собираюсь это сделать!

Так что outletArgs означает, что теперь это находится в this самого компонента? (Так это выглядит — я думал, что мне нужно будет как-то искать в args…)

Так что в моём коннекторе hbs я могу обращаться к this.topic, а затем вызывать свой компонент вот так:

<RatingOne @name="one" @topic={{this.topic}}/>

А затем в hbs для компонента RatingOne (когда-нибудь переименую его просто в rating, раз уж я понял, как передавать ему данные) я могу написать:

  Это тема {{this.topic.id}} {{this.topic.title}}
  Это id категории: {{this.topic.category_id}})

и получить данные о теме!

И теперь, когда я вижу тему, я могу добавить свои аргументы в сериализатор темы, верно? (Возможно, лучше передать их просто в категорию… или, может быть, я просто передам значение “doTheThing” в сериализатор и получу реальные данные из SiteSettings, так как, кажется, они хотят, чтобы это было на уровне сайта, а не категории).

Если я каким-то образом не наткнулся на что-то ещё устаревшее, то, похоже, вы это сделали. Огромное спасибо. :beer: :beers: :clinking_glasses: :moneybag:

Это связано с тем, как я заставляю вещи работать в Glimmer, поэтому я оставляю это здесь.

Мне нужно вызывать этот код после отрисовки каждой темы, чтобы переупорядочить элементы <td>. Я использовал above-topic-item, который размещает рейтинги перед заголовком темы. Этот безумный хак переупорядочивает элементы <td> с помощью JavaScript.

// document.addEventListener("DOMContentLoaded", function() {
//   const table = document.querySelector('table.topic-list.ember-view');
//   const rows = table.getElementsByTagName('tr');

//   // todo: это нужно вызывать при загрузке страницы
//   for (let row of rows) {
//     const cells = Array.from(row.getElementsByTagName('td'));
//     cells.sort((a, b) => {
//       const orderA = parseInt(window.getComputedStyle(a).order, 10);
//       const orderB = parseInt(window.getComputedStyle(b).order, 10);
//       return orderA - orderB;    });

//     for (let cell of cells) {
//       row.appendChild(cell);
//     }
//   }
// });

Кажется, мне «просто» нужно вызвать какой-то afterRender или что-то подобное вместо того, что у меня есть на уровне документа. Может быть, роутер?

Знаете, я не могу не задаться вопросом, Джей, не послужила ли ваша последняя вопросом вдохновением для этого: Upcoming topic-list changes - how to prepare themes and plugins :thinking: :wink:

И я считаю, что ответ на ваш последний вопрос в конечном итоге таков: «изучите новый API! :rocket:»

Забавно. И человек, который хотел выполнить эту работу, только что появился. Возможно, теперь всё будет проще!