0

I'm having trouble getting the data returned properly. When you first load this page, it logs a blank {} for oldData, and "this" returns the element itself. When you click, it then logs the mouseEvent for the oldData, and the window element for "this".

I expect it should consistently log data from the schema. I'm not sure if the object should be blank when the update first fires, but it depends what "oldData" is at that point. I also expect "this" to consistently be a reference to the component function, not the window. What am I not considering here?

<html lang="en">
<head>
  <script src="https://aframe.io/aframe/dist/aframe-master.min.js"></script>
  <script>
    AFRAME.registerComponent('my-component', {
      multiple: false,
      schema: {
        mood: {
          type: 'string',
          default: 'happy'
        }
      },

      init() {
        window.addEventListener('click', this.update)
      },

      update(oldData) {
        console.log("oldData: ", oldData);
        console.log("this: ", this);
      },
    })
  </script>
</head>
<body>
  <a-scene>
    <a-sky color="blue"></a-sky>
    <a-box id="bluebox" color="green" position="-1 0 -3" my-component></a-box>
    <a-light position="-3 4 2" type="spot" target="#bluebox"></a-light>
  </a-scene>
</body>
</html>
user7637745
  • 965
  • 2
  • 14
  • 27
  • try `this.update.bind(this)` – mhodges Jul 05 '18 at 22:00
  • `this` is defined at runtime, not statically or lexically. It is completely subject to calling context. – mhodges Jul 05 '18 at 22:01
  • Since update is not called on on any object (i.e. `myObj.update()`) it inherits the top-level `this` context, which is `window`. It is purely coincidental that your click handler is also on the window element. – mhodges Jul 05 '18 at 22:05
  • Related: [How to access the correct `this` inside a callback?](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) – Jonathan Lonowski Jul 05 '18 at 22:12

3 Answers3

0

Two problems:

  1. Methods are not bound to the component unless you call them as componentObj.methodName(). You can bound them explicitly with bind:

    this.methodName = this.methodName.bind(this);

  2. If the update method is called through the event listener the oldData parameter won't be passed. Better to create your own method onClick. You can rely on this.data instead of oldData in your method logic.

    window.addEventListener('click', this.onClick.bind(this));

    AFRAME.registerComponent('foo', { schema: {}, init: function () { window.addEventListener('click', this.onClick.bind(this)); }, update: function () {}, onClick: function () { ... this.data ... } });

Diego Marcos
  • 4,502
  • 3
  • 15
  • 20
  • 1
    `methodName() { ... }` is a valid shorthand for `methodName: function () { ... }` added in [ECMAScript 2015](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Syntax). – Jonathan Lonowski Jul 05 '18 at 22:03
  • 1
    So this would be properly declared? `` It still seems to not work correctly. It doesn't seem anything has changed. – Noah Neumark Jul 05 '18 at 22:06
0

You are passing this.update as a callback and the 'this' will be under the context of the listener method. try:

init () {
  this.update = this.update.bind(this)
  window.addEventListener('click', this.update)
},
Warren Wan
  • 77
  • 5
  • It actually doesn't inherit from the window event listener - it inherits all the way to the top-level, which is `window`. `addEventListener` does not bind context like jQuery's `.on()` does – mhodges Jul 05 '18 at 22:06
  • Your solution should still work, however. OP can you confirm? – mhodges Jul 05 '18 at 22:08
  • This is just partially correct. The oldData parameter won't be passed to the update method through the event listener – Diego Marcos Jul 05 '18 at 22:14
  • @DiegoMarcos Good point. According to [the docs](https://aframe.io/docs/0.8.0/core/component.html#update-olddata) update is called after init or after any property/data has been changed. Maybe the OP can leverage this by modifying an unused or nonvital property via `.setProperty()`? – mhodges Jul 05 '18 at 22:22
0

Okay this works correctly. Upon click (using bind method), the color attribute changes on the internal component, which fires the update method, which displays the oldData and the new data. It also changes the attribute of the external color component based on the internal color from this.data.

<script>

  AFRAME.registerComponent('my-component', {

    multiple: false,

    schema: {
      color: {default: '#' + Math.random().toString(16).slice(2, 8).toUpperCase()}          
    },

    init: function () {
      this.changeAtt = this.changeAtt.bind(this)
      window.addEventListener('click', this.changeAtt);
      this.el.setAttribute('color', this.data.color)
    },

    update: function (oldData){
      console.log("oldData: ", oldData);
      console.log("current Data: ", this.data);
    },

    changeAtt () {
      el = this.el;
      el.setAttribute('my-component', {color: '#' + Math.random().toString(16).slice(2, 8).toUpperCase()})
      el.setAttribute('color', this.data.color)
    }

  })

</script>