hotshotiguana
hotshotiguana

Reputation: 1570

Aframe Emitting Custom Events Between Components

I'm trying to create a custom event dataReady in a-frame which will emit when a few different data sources have successfully downloaded. I would like for multiple other components to listen for this event and then cause their element parents to become visible or perform other actions.

In the sample code below, the eventListener is being added before the event is emitted (tested using console.log()), but the listener function on the element is not being called when the event is emitted. I feel like all the pieces are in place, but it just isn't working correctly. Any thoughts would be greatly appreciated?

Component 1 attached to <a-camera data-controller></a-camera>

AFRAME.registerComponent('data-controller', {
    init: function () {
        var el = this.el;
    },
    update: function () {
        async.eachSeries(['api-1', 'api-2'],
            function (item, cb) {
                console.log(item);
                cb();
            },
            function (err) {
                if (err) {
                    throw new Error('Error retrieving data from server');
                }

                console.log("emitting the event");
                this.el.emit("dataReady", {value: 2}, false);
            }.bind(this));
    }
});

Component 2 attached to <a-entity geometry="primitive:plane" road></a-entity>

AFRAME.registerComponent('road', {
    schema: {
        width: {type: 'number', default: 4},
        height: {type: 'number', default: 4}
    },

    init: function () {
        var data = this.data;
        var el = this.el;

        // Update geometry props
        el.setAttribute('geometry', 'width', data.width);
        el.setAttribute('geometry', 'height', data.height);

        console.log('adding an event listener');
        el.addEventListener('click', function (event) {
            console.log("we are ready to blast off b/c of event listened w/ detail value: " + event.detail.value);
        });
    }
});

index.html contents:

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">

    <!--Vendor Scripts-->
    <script src="./js/vendor/aframe.min.js"></script>
    <script src="./js/vendor/async.min.js"></script>

    <!--AFRAME Components-->
    <script src="./js/components/road.js"></script>
    <script src="./js/data-controller.js"></script>
</head>

<body>
<a-scene>
    <a-entity geometry="primitive:plane" road></a-entity>
    <a-camera data-controller></a-camera>
</a-scene>
</body>
</html>

Upvotes: 4

Views: 5032

Answers (1)

ngokevin
ngokevin

Reputation: 13233

You could rely on the event bubbling and listen on the scene, and store the entity in the event detail so you know where it came from:

// data-controller component
this.el.emit('dataready', {value: 2, el: this.el})
// Event will bubble to the scene.

// road component
this.el.sceneEl.addEventListener('dataready', function () { // ... });

Or you can pass a reference with the selector property type to which entity to want to listen to:

// road component
schema: {
    dataEl: {type: 'selector'},
    width: {type: 'number', default: 4},
    height: {type: 'number', default: 4}
},

init: function () {
  // ...
  this.data.dataEl.addEventListener('dataready', function () { // ... });
}

// <a-entity road="dataEl: [data-controller]"></a-entity>

Upvotes: 4

Related Questions