ajax 호출 후 pluginOutlet에 내용을 수동으로 추가하는 방법? pluginOutlet을 다시 렌더링하거나 새로고침하는 방법?

안녕하세요, :wave:

저는 디스코urs 테마 제작과 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");

	// 두 번째 위치(아래 참조)에 표시하기 위해
	// ...
	// ...
}

두 번째 위치는 below-site-header 플러그인 아웃렛(pluginOutlet)입니다.
위와 마찬가지로 api.decorateWidget를 사용할 수 없다고 생각하는데, 이는 플러그인 아웃렛이 위젯이 아니기 때문일까요(?).

질문은 다음과 같습니다:

  1. 플러그인 아웃렛에 AJAX 콘텐츠를 수동으로 삽입하는 방법은 무엇인가요?
  2. 또한 AJAX 호출 후 가상 DOM을 즉시 컴파일하는 방법도 알고 싶습니다. 예를 들어, 위의 변수 headerNav에 대해 컴파일된 HTML 마크업을 얻고 싶습니다. 어떤 라이브러리/함수를 사용해야 할지 확실하지 않습니다.
  3. 가능하다면, appEvents.trigger("site-header:force-refresh");와 유사하게 플러그인 아웃렛을 다시 렌더링하는 방법도 알고 싶습니다.

미리 감사합니다! :man_bowing:

맞습니다. 이를 수행하는 두 가지 방법이 있습니다…

  1. 커스터마이징을 위젯으로 만든 다음, 다음과 같이 플러그인 아웃렛에 위젯을 마운트할 수 있습니다:

    {{mount-widget widget="widget-name"}}
    

    Discourse에서 위젯이 생성되는 예시를 확인해 볼 수 있습니다. 예를 들어 홈 로고: discourse/app/assets/javascripts/discourse/app/widgets/home-logo.js at 2dbcea9eeeb816dda347027497b3a49634ef851f · discourse/discourse · GitHub

    테마에서는 widget-name.js 파일을 javascripts/discourse/widgets 디렉터리에 추가해야 합니다.

    위젯에 대한 더 자세한 내용은 A tour of how the Widget (Virtual DOM) code in Discourse works 에서 확인할 수 있지만, 점진적으로 위젯을 단계적으로 폐지할 예정이므로 이 과정에서 배우는 것은 장기적으로 유용하지 않을 수 있다는 점을 유의하세요.

  2. 위젯 데코레이터를 그대로 유지하고, 플러그인 아웃렛에서 원하는 작업을 수행하는 별도의 Ember 컴포넌트를 생성합니다. 우리는 위젯 대신 Ember 컴포넌트(Discourse의 대부분이 이 기반으로 구축됨)를 단계적으로 도입하고 있습니다.

    이 과정은 다음과 같습니다:

    1. 테마의 javascripts/discourse/components 디렉터리에 컴포넌트 JS 및 HBS(템플릿) 파일을 생성합니다.
    • component-name.js

    • component-name.hbs

    1. Ember 컴포넌트를 구축합니다… 불행히도 이 단계는 기본적으로 “Ember 배우기” (Ember Guides)이지만, 시작하는 데 대략적인 아이디어를 줄 수 있을 것입니다:
    • 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(); // assuming this is json
          this.navLinks = data;
        } catch (error) {
          console.error("Failed:", error);
        }
      }
    }
    
    • component-name.hbs 내부:
    <div {{did-insert this.fetchNavLinks}}>
     {{#each this.navLinks as |link|}}
       <a href={{link.anchor}}>{{link.title}}</a>
     {{/each}}
    </div>
    

    이것은 컴포넌트가 삽입될 때마다(이 경우, Discourse 사이트를 방문하고 앱이 렌더링될 때) fetchNavLinks 액션을 호출합니다. navLinks가 업데이트되면 추적(tracked) 속성이기 때문에 컴포넌트의 내용이 업데이트됩니다.

    컴포넌트 렌더링보다 더 자주 링크를 업데이트하려면, 예를 들어 현재 라우트(페이지)가 특정 조건을 충족하는지 확인하는 등의 추가 로직을 이 JS에 추가해야 합니다.

    1. 이 컴포넌트는 javascripts/discourse/connectors/below-site-header/my-component-connector.hbs 파일에 아웃렛에 추가함으로써 플러그인 아웃렛에 추가됩니다:
    <ComponentName />
    

크리스, 정말 감사합니다! 정말 도움이 많이 됩니다.

위젯이 점차 폐지되고 있으므로, 컴포넌트를 사용하는 두 번째 접근법을 선택했습니다. 함수 호출의 작은 오타를 제외하면, .hbs 파일에는 {{did-insert this. fetchNavLinks}}라고 작성해야 하며, 이제 모든 것이 정상적으로 작동합니다!

did-insert를 사용할 수 있다는 것을 알게 되어 정말 다행입니다! 이제 작업을 완료했습니다. :man_bowing:

아, 알아봐서 다행이야. 위에서 수정했어.