A tour of how the Widget (Virtual DOM) code in Discourse works

Right, after a lot of headaches I’ve got it working - thanks for the help! :tada:

The two other problems I hit which could possibly do with some re-engineering in the widget code:

  • When setting dirtyKeys, it only re-renders the first widget with that buildKey. I’ve got around that by adding the post-id to the post-avatar’s buildKey so that every buildKey is unique
  • Setting the dirtyKey doesn’t work if the widget you want to re-render is inside another widget. In my case the post-avatar is inside a post. I think this is because of the shadowTree stuff.
    It would be really nice if the widget code could work out that the widget is deep in the tree and re-render its parents. I’ve got around this by setting the post widget as dirty as well.

That’s because keys are supposed to be unique! So adding the post id to the avatar is correct in this case. I might want to raise an error in development mode if two elements use the same keys. If you want to commit the post avatar key to master that would be fine.

That’s exactly what it is. By default I think a widget is supposed to re-render its parents, but the shadowTree is a performance optimization that avoids too much re-rendering and requires you to be more explicit.

Just in case anyone is trying to track down how widgets can use handlebars templates, see the commit message here (can’t find it documented anywhere on meta):

https://github.com/discourse/discourse/commit/dffb1fc4ee8a4d58f48145decb1590a304e8cf7d

위젯을 사용하여 내비게이션 바를 만드는 방법은 무엇인가요?

이렇게요

image

다음 코드에서 지시문은 더 이상 작동하지 않습니다. 렌더링은 정상적으로 이루어지지만, 클릭 시 상태가 증가하지 않습니다:

<script type="text/javascript">
        const { createWidget } = require('discourse/widgets/widget');

        createWidget('increment-button', {
            tagName: 'button',

            defaultState() {
                return { clicks: 0 };
            },

            html(attrs, state) {
                return `Click me! ${state.clicks} clicks`;
            },

            click() {
                this.state.clicks++;
            }
        });
</script>


<script type='text/x-handlebars' data-template-name='/connectors/above-footer/increment-button'>
    {{mount-widget widget="increment-button" }}
</script>```

이상하네요. 제 환경에서는 해당 컴포넌트를 시도해 봤을 때 key가 없어서 아예 렌더링이 되지 않았습니다. 위에 있는 문서에 buildKey 메서드를 추가해서 업데이트했는데, 제 환경에서는 정상적으로 동작합니다. 이 방법으로 시도해 보세요.

@eviltrout 감사합니다! 덕분에 잘 작동하게 만들었어요. 정말 감사드립니다.

인수에서 전달된 HTML을 위젯에 렌더링하는 방법은 무엇인가요? 현재는 HTML이 이스케이프 처리되어 그대로 렌더링되고 있습니다.

또한 다음 예제는 작동하지 않습니다:

user 변수가 null인 것 같습니다. 위젯이 마운트되는 hbs 파일에서 user를 전달하기 위해 무언가를 해야 하나요?

이 질문에 대한 답을 찾았어요:

답변:

import { createWidget } from 'discourse/widgets/widget';
import RawHtml from "discourse/widgets/raw-html";

createWidget('display-name', {
  tagName: 'div.name',

  html() {
    return new RawHtml({ html: `<div>${this.siteSettings.user_rank_alert_message}</div>` });
  }
});

하지만 아웃렛에서 데이터가 전달되지 않는 커넥터 hbs에서 사용자 데이터를 가져오는 방법은 여전히 모르겠습니다.

삼중 중괄호 한번 봐봐, 형 :smiley:

조급해하지 마세요 :wink: :warning: :

삼중 중괄호 대신 사용할 수 있는 헬퍼가 있습니다:

{{html-safe result}}

Hey @eviltrout,

이 게시물의 원문(OP)에서 이렇게 말씀하셨다는 걸 확인했어요:

하지만 이 토픽의 아래쪽에서 이렇게 언급하고 있습니다.

원문(OP)을 수정해 주실 수 있을까요?

감사합니다. 해당 수정을 완료했습니다.

@eviltrout 이건 action="deleteThing"가 되어야 하는 거 아닌가요?

위젯에 따라 다릅니다. @eviltrout의 예시에서는 my-widget이 "deleteThing"이라는 이름의 액션을 전달받는 것을 기대합니다. 다른 위젯은 액션에 다른 이름을 사용하며 (실제로 액션을 "action"이라고 부르는 것도 가능합니다).