Access tracked variables in VanillaJS?

AI-generated summary

Accessing tracked variables in VanillaJS is being discussed. NateDhaliwal is trying to add an event listener to an iframe, but it’s returning null. Alteras suggests using the Glimmer {{on}} modifier instead of working around DOM rendering and race conditions.

NateDhaliwal finds the suggestion helpful, but encounters a compile error when trying to implement it. Alteras corrects the mistake, and NateDhaliwal updates the code, but receives an ESLint error.

Arkshine resolves the issue by suggesting the import of the on modifier from @ember/modifier. pfaffman recommends looking at the Discourse source code, as well as all-the-plugins and all-the-themes repositories for learning purposes.

I have this piece of code:

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}}"
      }
    ]
  }, "*");
});

It changes the iframe which is in a DModal to include the code’s language and content. However, it tells me that iFrame is null, so it could not add addEventListener to it.
I’ve tried this:

<template>
  <DButton
    @translatedLabel="Show Modal"
    @action={{this.showModal}}
  />

  {{#if this.modalIsVisible}}
    <DModal @title="Code Compiler" @closeModal={{this.hideModal}}>
      <p>Code: {{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>

But that didn’t work as well. Is there a way I can do this?


Link to repo:
https://github.com/NateDhaliwal/Discourse-OneCompiler-Code-Runner

Looks like you’re working in gjs, so you can just use glimmer functions instead of trying to work around DOM rendering and race conditions. Add the glimmer {{on}} modifier with the iframe element and make your on loaded function fire on it.

@action
onIframeLoaded() {
...
}

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

That… exists!? Wow, I’ll give it a shot.

Hmm… got this error:

Compile error: Error: /discourse/theme-9718/discourse/components/show-onecompiler: Parse error on line 18:
...   {{on "load" this.onIframeLoaded()}}>
-----------------------^
Expecting 'ID', got 'INVALID' (discourse/components/show-onecompiler.gjs)

Code:

@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="Show Modal"
      @action={{this.showModal}}
    />

    {{#if this.modalIsVisible}}
      <DModal @title="Code Compiler" @closeModal={{this.hideModal}}>
        <p>Code: {{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"
          {{on "load" this.onIframeLoaded()}}>
        </iframe>
      </DModal>
    {{/if}}
  </template>

Ah, I messed up my example. It should be {{on "load" this.onIframeLoaded}}, no parentheses, since it should be a reference to a function.

I’ve edited my reply.

Hmm… it says that Error: 74:13 error 'on' is not defined in ESLint. And the docs have no mention of an import because it’s a modifier.
I added event as a parameter to onIframeLoaded because the docs did that.

Add import { on } from "@ember/modifier"; and this should work.
It’s a good idea to look at the Discourse source code. It’s a good place to learn with existing code.

See also GitHub - discourse/all-the-plugins · GitHub and GitHub - discourse/all-the-themes · GitHub