javascriptaugmented-realityaframe

A-FRAME component does not update "properly" - oldData's empty object


I'm following the step-by-step introduction from A-FRAME framework and currently learning how to write a component and get issue updating a component. I've already looked at that thread to make sure i was doing it right.

Here's the case. A box component is created. Here's a draft to illustrate that. When no oldData means we are in init phase. if component props changes (oldData not empty), component props are updated with new values. Init and update functions both fire once.

The thing is that when setting new values to component attributes,

el.setAttribute("box", { width: 2, height: 2, depth: 2, color: "#33FF60" });
el.setAttribute("box", { width: 2, height: 4, depth: 3, color: "#F533FF" });

the box is rendered accordingly, but the logs i've putted as warning in the update function, to inform about the update phases, the conditions, does not fire, like if each time setAttribute() is used, i stay in the init phase, not going through the update phase, with "no oldData - init phase" logged as result (oldData's empty object).

  if (Object.keys(oldData).length === 0) {
  console.log("no oldData - init phase");
  return;
}

Also, i've placed a listener into the init phase to get details when component changes but it never fires when component "update".

 el.addEventListener("componentChanged", function (e) {
  console.log(e);
  console.log("listener init");
  if (e.detail.name === "width") {
    console.log(e.detail.newData);
  }
});

Asking the community in the hope to get some feedbacks from other viewpoints. There's probably something i don't see here.

PS: component written in separate .js file, called into index.html head with defer attribute for JS to be executed after page parsed.


Solution

  • With two calls like this:

    el.setAttribute("box", { width: 2, height: 2, depth: 2, color: "#33FF60" });
    el.setAttribute("box", { width: 2, height: 4, depth: 3, color: "#F533FF" });
    

    the second setAttribute won't wait for the first one. They both happen within the same renderloop with only the last one being registered.

    If you wait a bit:

    let el = document.querySelector("a-entity");
    setTimeout(() => {
      el.setAttribute("box", { color: "#FF00AA" });
    }, 1500)
    setTimeout(() => {
      el.setAttribute("box", { color: "#AA00FF" });
    }, 3000)
    <script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
    <script>
    console.log("🔥");
    AFRAME.registerComponent("box", {
      schema: {
        color: { type: "color", default: "#DAF7A6" },
      },
      init: function() {
        console.log("init 🔵");
        var data = this.data;
        var el = this.el;
    
        // create mesh
        this.geometry = new THREE.BoxGeometry(data.width, data.height, data.depth);
        this.material = new THREE.MeshStandardMaterial({ color: data.color });
        this.mesh = new THREE.Mesh(this.geometry, this.material);
        // set mesh on entity
        el.setObject3D("mesh", this.mesh);
      },
      update: function(oldData) {
        console.log("update 🟢");
        console.log("current color:", this.data.color)
        console.log("old color:", oldData.color)
    
        var data = this.data;
        var el = this.el;
        // if oldData empty, then means we're in init process
        if (Object.keys(oldData).length === 0) {
          console.log("no oldData - init phase");
          return;
        }
    
        // material related props changed. update material
        if (data.color !== oldData.color) {
          console.log("material changed");
          el.getObject3D("mesh").material.color = new THREE.Color(data.color);
        }
      }
    });
    </script>
    
    <a-scene>
      <a-entity box position="0 2 -5" scale="10 10 0.5"></a-entity>
    </a-scene>

    You'll get three sets of logs:

    1. Init with some random color
    2. First setAttribute changing the random color to #33FF60
    3. Second setAttribute changing #33FF60 to #F533FF

    Also You should register your component before using it in the scene (here it doesn't matter so much since you add it with setAttribute, but if it gets registered after the scene initializes, it won't work [source]

    We must register components before we use them anywhere in . Meaning from an HTML file, components should come in order before