Как получить доступ к отслеживаемым переменным в VanillaJS?

У меня есть следующий фрагмент кода:

let iFrame = document.getElementById('oc-editor');
iFrame.addEventListener('load', function() {
  iFrame.contentWindow.postMessage({
    eventType: 'populateCode',
    language: "{{this.codeLang}}",
    files: [
      {
        "name": "file.{{this.codeLang}}",
        "content": "{{this.codeLang}}"
      }
    ]
  }, "*");
});

Он должен изменять iframe, находящийся внутри DModal, чтобы добавить язык и содержимое кода. Однако я получаю ошибку, что iFrame равен null, поэтому к нему нельзя добавить обработчик addEventListener.

Я пробовал следующее:

<template>
  <DButton
    @translatedLabel="Показать модальное окно"
    @action={{this.showModal}}
  />

  {{#if this.modalIsVisible}}
    <DModal @title="Компилятор кода" @closeModal={{this.hideModal}}>
      <p>Код: {{this.getCode}}</p>
      <iframe
       frameBorder="0"
       height="450px"  
       src="https://onecompiler.com/embed/{{this.codeLang}}" 
       width="100%"
       id="oc-editor"
       title="OneCompiler Code Editor"
       listenToEvents="true">
      </iframe>
      <script>
        let iFrame = document.getElementById('oc-editor');
        iFrame.addEventListener('load', function() {
          iFrame.contentWindow.postMessage({
            eventType: 'populateCode',
            language: "{{this.codeLang}}",
            files: [
              {
                "name": "file.{{this.codeLang}}",
                "content": "{{this.codeLang}}"
              }
            ]
          }, "*");
        });
      </script>
    </DModal>
  {{/if}}
</template>

Но это тоже не помогло. Есть ли способ решить эту задачу?


Ссылка на репозиторий:

Похоже, вы работаете в gjs, поэтому вместо попыток обойти рендеринг DOM и состояния гонки можно просто использовать функции glimmer. Добавьте модификатор glimmer {{on}} к элементу iframe и привяжите к нему вашу функцию on loaded.

@action
onIframeLoaded() {
...
}

...
<iframe {{on "load" this.onIframeLoaded}}

Такое вообще существует!? Вау, я попробую.

Хм… получил эту ошибку:

Ошибка компиляции: Ошибка: /discourse/theme-9718/discourse/components/show-onecompiler: Ошибка синтаксического анализа на строке 18:
...   {{on "load" this.onIframeLoaded()}}>
-----------------------^
Ожидается 'ID', получено 'INVALID' (discourse/components/show-onecompiler.gjs)

Код:

@action
  onIframeLoaded() {
    let iFrame = document.getElementById('oc-editor');
    iFrame.contentWindow.postMessage({
      eventType: 'populateCode',
      language: "{{this.codeLang}}",
      files: [
        {
          "name": "file.{{this.codeLang}}",
          "content": "{{this.codeLang}}"
        }
      ]
    }, "*");
    return;
  }


  <template>
    <DButton
      @translatedLabel="Показать модальное окно"
      @action={{this.showModal}}
    />

    {{#if this.modalIsVisible}}
      <DModal @title="Компилятор кода" @closeModal={{this.hideModal}}>
        <p>Код: {{this.getCode}}</p>
        <iframe
          frameBorder="0"
          height="450px"  
          src="https://onecompiler.com/embed/{{this.codeLang}}" 
          width="100%"
          id="oc-editor"
          title="Редактор кода OneCompiler"
          listenToEvents="true"
          {{on "load" this.onIframeLoaded()}}>
        </iframe>
      </DModal>
    {{/if}}
  </template>

Ах, я ошибся в примере. Должно быть {{on "load" this.onIframeLoaded}}, без скобок, так как это должна быть ссылка на функцию.

Я отредактировал свой ответ.

Хм… в ESLint сообщается об ошибке: Error: 74:13 error 'on' is not defined. В документации нет упоминания об импорте, так как это модификатор. Я добавил event в качестве параметра для onIframeLoaded, потому что так сделано в документации.

Добавьте import { on } from "@ember/modifier";, и всё должно заработать.
Хорошая идея — посмотреть исходный код Discourse. Это отличное место для обучения на основе существующего кода.

Также см. GitHub - discourse/all-the-plugins · GitHub и GitHub - discourse/all-the-themes · GitHub