Glimmer 컴포넌트에서 상위 컴포넌트의 데이터에 액세스하는 방법

before-topic-list-body에 컴포넌트를 추가하려고 합니다. 카테고리나 토픽 중 어디에서든 필요한 데이터를 가져와야 하는데, 더 적절한 직렬화기(serializer)에 원하는 데이터를 추가할 예정입니다.

몇 시간 동안 고민해 보았지만, 해결책이 떠오르지 않습니다. 이 문제는 힌트만 주면 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를 사용하면 topic까지 거슬러 올라갈 수 있네요! 제가 찾고 있던 마법이 바로 이거예요. (이걸로 다른 프로젝트의 또 다른 문제도 해결될 것 같아요.) 이제 남은 일은 topic 컨트롤러에 필요한 것들을 넣는 건데, 이건 제가 (거의) 어떻게 해야 하는지 아는 일들이에요!

콘솔에 비권장(deprecation) 메시지가 출력되었나요? PluginOutlet에서 parentView 접근을 곧 제거할 예정이므로 출력되었어야 합니다. 사실, 이 기능은 매우 비권장 사항이라 관리자 UI에 경고 배너가 표시되어야 할 정도입니다 :sweat_smile:

경고를 보지 못했다면 알려주세요!

(parentView는 우리가 점차 벗어나고 있는 Ember의 클래식 컴포넌트 시스템의 일부입니다)

트리에서 더 위쪽의 정보에 접근하려면, Plugin Outlet에 인자로 전달하거나, 사용 가능한 서비스(예: currentUser 또는 router) 중 하나를 사용해야 합니다

아, 무슨 일이 일어나는지 알겠네요. ._parentView를 사용하고 계신데, .parentView는 아닙니다. 밑줄이 붙은 버전은 비권장(deprecation) 메시지를 우회할 수 있도록 허용합니다. 하지만 여전히 몇 주 안에 동작이 중단될 것입니다.

이 PR은 그러한 우회 방법을 방지하므로, .parentView와 ._parentView 모두 비권장(deprecation)을 발생시킵니다:

고맙습니다, @david !

아니요. 좀 찾아봐도 나오지 않았습니다. 저는 27eea3250cbde2e8bca754f445bee403c059eba3 버전을 사용하고 있습니다.

<RatingOne @scaleValue={{this.scaleValue}} @topic={{this.parentView._parentView.topic}} />
<td class="rating-one">
  <span class="rating-title">{{this.ratingName}}</span> 
  This is topic {{this.topic.id}}
  This is category {{this.topic.category.title}} (category id: {{this.topic.category_id}})
  {{log "rating this" this}}
  {{log "scale value" this.scaleValue}}
  {{log "rating value" this.ratingValue}}
  {{log "category" 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를 토픽 또는 카테고리 직렬화기(serializer)에 넣어서, 토픽 목록에 이러한 여러 토픽 평가를 추가하는 것입니다. 이렇게 하면 토픽에 들어가지 않고도 평가할 수 있게 됩니다(사람들은 이미 토픽에 익숙할 것이므로, 투표 대상이 무엇인지 알기 위해 토픽에 들어갈 필요가 없을 것입니다).

음. 그렇게 보이진 않습니다.

좋습니다, 이 커밋으로 수정되었어야 합니다. 이제 경고가 표시되는지 확인해 주시겠습니까?

이상적으로는 이러한 것들이 플러그인 아웃렛(plugin outlet)의 인자로 제공되어야 합니다.

위에서 '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}}/>

그리고 RatingOne 컴포넌트의 hbs에서 (어느 날은 그냥 rating으로 이름이 바뀔 예정인데, 값을 전달하는 법을 알아냈으니까요) 이렇게 쓸 수 있습니다:

  This is topic {{this.topic.id}} {{this.topic.title}}
  This is category id: {{this.topic.category_id}})

그러면 토픽 관련 정보를 가져올 수 있네요!

이제 토픽을 볼 수 있으니, 토픽 시리얼라이저에 제 인자들을 추가하면 되는 거죠? (카테고리에만 전달하는 게 더 나을 수도 있고… 아니면 시리얼라이저에 “doTheThing” 값을 전달하고 실제 데이터는 SiteSettings에서 가져오는 게 좋을 것 같습니다. 사이트 레벨에서 이것이 필요하다고 생각하니까요, 카테고리 레벨이 아니라서요.)

혹시 제가 다른 비추천(deprecated)된 기능을 우연히 발견한 게 아니라면, 님 덕분에 해결된 것 같습니다. 정말 감사합니다. :beer: :beers: :clinking_glasses: :moneybag:

이것은 글리머(glimmer)에서 제가 어떻게 동작을 구현하는지와 관련이 있어서, 여기 남겨둡니다.

각 토픽이 렌더링된 후 <td> 항목을 다시 정렬하기 위해 이 코드를 호출해야 합니다. 토픽 제목 앞에 평점을 표시하기 위해 above-topic-item을 사용했습니다. 이 엉성한 해킹은 일부 JavaScript의 도움으로 <td> 항목을 다시 정렬합니다.

// 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);
//     }
//   }
// });

현재 가지고 있는 document 레벨의 처리 대신 afterRender 또는 다른 것을 호출하면 될 것 같습니다. 라우터(rater)일까요?

제이, 지난 질문이 이 글의 영감이 된 건 아닌지 궁금하네요: Upcoming topic-list changes - how to prepare themes and plugins :thinking: :wink:

그리고 지난 질문에 대한 답은 결국 “새로운 API를 확인하세요!”(Customizing the topic list) :rocket:라고 생각합니다.

재미있군. 그리고 이 작업이 필요한 일을 원했던 사람이 방금 다시 나타났어. 아마 이제 좀 더 수월할지도 모르겠군!