pfaffman
(Jay Pfaffman)
4월 10, 2024, 6:19오후
1
글림머(Glimmer) 컴포넌트를 하나 가지고 있습니다. 동작은 합니다. 하지만 이 컴포넌트를 토픽의 첫 번째 게시물 뒤에 배치해야 합니다. 해당 위치에 플러그인 아웃렛이 없습니다.
<article> 태그의 innerHTML에 텍스트를 삽입하는 방법은 알아냈고, 그렇게 하면 원하는 위치에 텍스트가 표시되지만, 컴포넌트로 이를 수행하는 방법은 없어 보입니다.
무엇을 놓치고 있는 걸까요?
adplugin이 게시물 사이에 광고를 삽입하는 방식을 살펴봤지만, 완전히 이해하지는 못했습니다.
export default {
name: "initialize-ad-plugin",
initialize(container) {
registerWidgetShim(
"after-post-ad",
"div.widget-connector",
hbs`<PostBottomAd @model={{@data}} />`
);
withPluginApi("0.1", (api) => {
api.decorateWidget("post:after", (helper) => {
return helper.attach("after-post-ad", helper.widget.model);
});
});
....
david
(David Taylor)
4월 10, 2024, 6:31오후
2
Jay Pfaffman:
거기에는 플러그인 출력이 없습니다.
하나 추가해 보죠! 어디에 원하시나요?
(지금 하시는 일을 수행할 수 있는 레거시 방식은 있지만, 토픽 뷰를 현대화할 때 깨질 것입니다. 플러그인 출력을 추가하면 훨씬 더 미래에 대비할 수 있습니다)
david
(David Taylor)
4월 10, 2024, 7:06오후
3
이 아웃렛은 decorateWidget("post:after의 기능과 일치하며, 이는 adplugin이 사용하는 것입니다:
main ← after-post-outlet
closed 08:33AM - 19 Apr 24 UTC
pfaffman
(Jay Pfaffman)
4월 10, 2024, 7:24오후
4
정말 멋지네요! 원하면 얻을 수 있어요!
사실 플러그인 아웃렛을 추가하는 것이 “쉬운” 일이라는 건 “알고” 있지만, 제가 어떻게 추가하는지 대략 알고 있었던 경우를 떠올려 보면, hbs 파일 안에서였어요.
그럼 모든 게시물 뒤에 추가되는 건가요? 그렇다면 첫 번째 게시물 뒤에만 나오도록 하는 마법을 찾아내야 할 것 같은데요?
article:after는 이전 시도를 하면서 어느 시점에 생각해 낸 방법이었어요.
david
(David Taylor)
4월 10, 2024, 7:30오후
5
Jay Pfaffman:
마법을 찾아야 할 것 같습니다
포스트가 아웃렛(outlet) 인자 중 하나로 전달되므로, post.post_number == 1을 확인할 수 있습니다.
Jay Pfaffman:
불평하면 얻는 거야!
하하! 이런 종류의 질문에 도움을 드리는 걸 항상 기꺼이 합니다. 위젯 워크어라운드(가장 접근법, 가까운 미래에 비추천으로 전환될 예정)보다는 테마와 플러그인이 이러한 표준 API를 사용하도록 하는 것이 모든 사람에게 훨씬 좋습니다.
네, 그건 이해할 만합니다. 코어 측에서 위젯 안에 추가하는 것은 골치 아픈 일입니다. 하지만 테마/플러그인 측에서는 매우 깔끔해야 합니다
내일 테스트 실패 문제를 해결하고 PR을 병합하도록 하겠습니다. (추가된 html 요소가 qunit 테스트의 일부 취약한 셀렉터에 영향을 미치고 있는 것 같습니다)
pfaffman
(Jay Pfaffman)
4월 10, 2024, 7:33오후
6
아하! @outletArgs={{hash post=@data.post}}. 이 방법을 어떻게 구현하는지 알아낼 수 있을 것 같습니다.
다시 한번 감사합니다!
david
(David Taylor)
4월 15, 2024, 1:08오후
7
죄송합니다! @pfaffman , 제가 조금 낙관적으로 말씀드린 것 같습니다. 제가 만든 PR은 아웃렛(outlet)이 사용되지 않는 경우에도 모든 게시물 사이에 새로운 <div> 래퍼를 도입하게 됩니다. 이는 우리가 원하지 않는 방식입니다.
래퍼를 피할 수 있는 방법이 있을 수 있지만… 당장 할 수 있는 단순한 방법은 없습니다.
따라서 당장 가장 좋은 해결책은 OP에서 언급하신 adplugin 구현을 복사하는 것입니다.
Jay Pfaffman:
하지만 그 내용을 제대로 이해하지 못했습니다
대략적인 과정은 다음과 같습니다:
원하는 것을 렌더링하는 컴포넌트(Glimmer 또는 classic, 상관없음)를 생성합니다.
registerWidgetShim을 사용하여 해당 컴포넌트를 위젯으로 사용 가능하게 만듭니다. adplugin 예제는 PostBottomAd 컴포넌트를 렌더링하는 "after-post-ad"라는 위젯을 생성하고 있습니다. 여기서는 모든 위젯 속성(@data)을 컴포넌트의 @model 인자로 전달하고 있습니다.
api.decorateWidget을 사용하여 post:after 위치에 새로운 위젯 심을 렌더링합니다. 귀하의 경우 첫 번째 게시물에만 표시하고 싶으므로 다음과 같은 방식으로 처리할 수 있습니다.
api.decorateWidget("post:after", (helper) => {
if(helper.widget.model.post_number === 1){
return helper.attach("my-widget-shim");
}
});
나중에 토픽 페이지가 Glimmer로 전환되면, 위젯 심/데코레이션을 제거하고 플러그인 아웃렛으로 대체해야 합니다. 컴포넌트 내의 모든 표시 로직이 플러그인 아웃렛에서도 재사용 가능하므로 이는 꽤 쉬울 것입니다.
진행 상황 알려주세요! 후속 질문이 있으시면 기꺼이 도와드리겠습니다 - 여기에는 처리해야 할 부분이 많다는 것을 알고 있습니다.
pfaffman
(Jay Pfaffman)
4월 16, 2024, 3:20오후
8
거의 다 됐어요!
유일한 문제는 currentUser를 컴포넌트에 전달하는 방법을 모르겠는 겁니다. 아래는 작동하지 않는 코드입니다.
import { hbs } from "ember-cli-htmlbars";
import { withPluginApi } from "discourse/lib/plugin-api";
import Site from "discourse/models/site";
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
import Banner from "../components/banner";
export default {
name: "initialize-banner-widget",
initialize(container) {
registerWidgetShim(
"geo-banner-widget",
"div.widget-connector",
hbs`<Banner @currentUser={{currentUser}} />`
);
// withPluginApi("0.1", (api) => {
// console.log("doing plugin");
// api.decorateWidget("post:after", (helper) => {
// return helper.attach("geo-banner-widget", helper.widget.model);
// });
// });
withPluginApi("0.1", (api) => {
const currentUser = container.lookup("service:current-user");
api.decorateWidget("post:after", (helper) => {
console.log(`decorate widget ${currentUser.username}`, );
if(helper.widget.model.post_number === 1){
return helper.attach("geo-banner-widget", { currentUser });
}
});
});
},
};
david
(David Taylor)
4월 16, 2024, 3:38오후
9
Banner 컴포넌트에서 다음과 같이 currentUser 서비스를 주입할 수 있습니다:
class Banner extends Component {
@service currentUser;
}
이렇게 하면 위젯에서 값을 전달할 필요가 없습니다.
pfaffman
(Jay Pfaffman)
4월 16, 2024, 4:09오후
10
currentUser를 넘기는 건 어리석은 짓이라는 걸 알았지만, 그걸 깨닫지 못했어요!
혹시 다른 분들도 유용하게 쓰실 수 있을지 모른다는 전제 아래, 다른 부분은 다음과 같습니다.
import Service, { inject as service } from "@ember/service";
import { hbs } from "ember-cli-htmlbars";
import { withPluginApi } from "discourse/lib/plugin-api";
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
export default {
name: "initialize-banner-widget",
initialize(container) {
registerWidgetShim(
"geo-banner-widget",
"div.widget-connector",
hbs`<Banner/>`
);
withPluginApi("0.1", (api) => {
api.decorateWidget("post:after", (helper) => {
const currentUser = container.lookup("service:current-user");
if(helper.widget.model.post_number === 1){
return helper.attach("geo-banner-widget");
}
});
});
},
};
david
(David Taylor)
4월 16, 2024, 4:54오후
12
맞아요!
이 경우 Service 클래스를 가져올 필요가 없을 것 같습니다. 이는 직접 Service를 생성할 때만 필요한 것이니까요. service 인젝션 데코레이터만 있으면 됩니다.
또한 Discourse/Ember의 최신 버전에서는 ‘inject as’ 별명을 사용할 필요가 없도록 더 간단하게 만들 수 있습니다. 이제 Ember는 인젝션 데코레이터를 service로 직접 사용할 수 있게 되었습니다.
import { service } from "@ember/service";
(다만, 기존 방식인 { inject as service }도 여전히 작동하며, Ember에서 이를 비추천(deprecate)할 계획이 있다는 소식은 들은 바 없습니다)
수정: 하지만 플러그인 아웃렛 대신 decorateWidget을 사용할 수도 있을 것 같네요… .
pfaffman
(Jay Pfaffman)
4월 16, 2024, 6:03오후
13
린터(linter)도 그렇게 생각하는 것 같습니다
이 방식이 더 좋습니다. 하우스 애드(하우스 광고) 플러그인은 여전히 구식 방식으로 하고 있네요.