Search code examples
aframe

Aframe.io component update/rebuild after onchange in another js file


Hello I am new in WebVR and I will be pleased If you can help me.

I want to update my AFRAME init() variable after onchange my input in test.js and callback aframe(rebuild Aframe component). I would like do somethink like useEffectfrom React.

I have two file test.js and aframe.js

test.js

const el = document.getElementById("inputItem")
el.setAttribute("type", "date")
el.addEventListener('input', (e)=> { console.log(e.target.value) }
//other logic

aframe.js

AFRAME.registerComponent("test", {
init(){
this.input = document.getElementById("inputItem")
this.input.addEventListener('input', (e)=> { console.log("input changed", e.target.value) } );
console.log("my input value", this.input)
}
update(){
if( this.input.value != document.getElementById("inputItem").value )
      {this.input = "New input"
      console.log("input changed", this.input)
  }
}
// some code here to build aframe element and append it to html  

}

so my console.log function inside aframe work only one time. After onchange input my update function doesn't response. I know the problem is aframe component is calling only one time.

I tried this answer and read about component. I don't know how to recall/rebuild afraime component after changing my input.


Solution

  • Looks like you've mixed two ways of doing this:

    1. Using setAttribute() to trigger the update() functions.

    The update function in the custom component is called:

    • once after init
    • after any change made with setAttribute(<component_name>)

    Here's an example of triggering update with setAttribute from an "external" js file (same behaviour, as long as the script is loaded after the <input> element is attached) :

    const input = document.getElementById("logtext"); // grab the input 
    input.addEventListener("input", e => {
      // on each 'input' event
      const text = e.target.value; // extract the text
      // set the [logger] "text" attribute, to trigger the update
      document.querySelector("[logger]").setAttribute("logger", "text", text); 
    })
    <script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
    <script>
      AFRAME.registerComponent("logger", {
        schema: {
          text: {
            default: ""
          }
        },
        // on any update
        update: function() {
          this.el.setAttribute("text", "value", this.data.text); // update the text component
        }
      })
    </script>
    
    <div style="position: fixed; z-index: 999">
      <label for="logtext">Text to be rendered:</label>
      <input type="text" id="logtext" name="logtext"><br><br>
    </div>
    <a-scene>
      <a-text position="-1 1.75 -3" value="" color="black" logger></a-text>
      <a-box position="0 1 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
    </a-scene>

    2. Using event listeners.

    You can add any logic you want to the event listener callbacks. It can modify you own component, it can modify other components.

    Here's an example of modyfing another component with setAttribute() in reaction to an event coming from the <input> element:

    <script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
    <script>
      AFRAME.registerComponent("logger", {
        init: function() {
          // grab the input element
          const input = document.getElementById("logtext");
          // on "input", do whatever you need with the updates
          input.addEventListener("input", e => {
            const text = e.target.value; // extract the text from the event
            this.el.setAttribute("text", "value", text); // update the text component
          });
        }
      })
    </script>
    
    <div style="position: fixed; z-index: 999">
      <label for="logtext">Text to be rendered:</label>
      <input type="text" id="logtext" name="logtext"><br><br>
    </div>
    <a-scene>
      <a-text position="-1 1.75 -3" value="" color="black" logger></a-text>
      <a-box position="0 1 -3" rotation="0 45 0" color="#4CC3D9"></a-box>
    </a-scene>


    I think the first option is similar to react since:

    • using the schema is similar to keeping a component "state" (as in useState)
    • the update function reacts to the component changes (as is useEffect)