0

I currently have 2 components. One of them, creates a series of dodecahedrons and the other one sets them invisible. Both of them are generated at runtime.

The problem is with the invisible one. I can't get the dynamically generated children by using:

el.querySelector('*')

in the init method of the invisible component.

I have even tried:

  • Listening for the "loaded" event and then registering the component.
  • Using promises and listening to a loaded event.
  • Using a window.setTimeout() function of 3 seconds after the component has been registered.

This is my full code, so far:

export class InvisibleComponent {
  constructor() {
    /**
     * Sets the current element and all of its children as invisible
     * by using its materials property.
     * 
     */
    const _this = this;
    AFRAME.registerComponent('invisible', {
      init: function () {
        _this.setTransparent(this.el);
        console.log(this.el);
        let children = this.el.querySelector('*');
        if (!children) return;
        Array.from(children).forEach(child => {
          _this.setTransparent(this.el);
        })
      },
      remove: function () {
        if (!this.el) return;
        _this.setOpaque(this.el);
        let children = this.el.querySelector('*');
        if (!children) return;
        Array.from(children).forEach(child => {
          _this.setOpaque(this.el);
        })
      }
    })
  }
  setTransparent(el: AFrame.Entity) {

    this.getMaterial(el).then((material :any) => {
      this.setAttributes(el,material.opacity);
    })
    .catch(resolve => {
      console.log("Material couldn't be gathered. Using 100% of opacity instead", resolve);
      this.setAttributes(el, 1);
    });
  }

  setAttributes(el : AFrame.Entity, opacity){
    el.setAttribute('data-previous-opacity',opacity);
    el.setAttribute('material', 'opacity:0; transparent: true; visible:false;');
  }

  /**
   * This is a solution if the element which is trying to load 
   * hasn't been loaded yet. All elements must have a material
   * in order to be inserted in a scene.
   * @param el 
   */
  getMaterial(el: AFrame.Entity): Promise<number> {
    return new Promise<number>((resolve, reject) => {
      let material = el.getAttribute('material');
      if (material) resolve(material);

      el.sceneEl.addEventListener('loaded', function () {
        let material = el.getAttribute('material');
        !material ? reject(undefined) : resolve(material);
      })


    });
  }

  // If you had previous property, this will not remove them.
  setOpaque(el: AFrame.Entity) {
    let dataPreviousOpacity = el.getAttribute('data-previous-opacity');
    if (!dataPreviousOpacity) dataPreviousOpacity = 1;
    el.setAttribute('material', 'transparent', dataPreviousOpacity === 1 ? false : true);
    el.setAttribute('material', 'opacity', dataPreviousOpacity);
    el.setAttribute('material', 'visible', true);

  }
}
2
  • 1
    Components shouldn't be registered at run time, but st page load. The child-attached event should help, emit an event when you detect all children are loaded and have the other component listen for that event. Or setTimeouts should work as well. Will help to make the indivisible component set the dodecahedron generator as a dependency Commented Jul 31, 2017 at 22:20
  • @ngokevin: Makes sense. I'll also check this approach out. Thx. Will post back once I finish solving another issue I have (totally different problem) Commented Aug 1, 2017 at 13:25

1 Answer 1

2

For some reason I can't make it work with looking for the entities children.
Here is my approach:

  1. Expose the created element:

    init:function(){
      //expose the object:
      this.object = document.createElement('a-dode(..)')
    }
    
  2. Access the object using el.components.componentName.object

It can be a public array of objects, however You like. It does not seem to have issues with accessing undefined objects. If You have though, You could make a interval checking if the object is undefined, or loop through an array, executing 'set invisible' on each 'non-undefined' element.

Working fiddle here: component 'create' makes a box, 'change_color' sets the color+position. There's also a getObject() function, I'll mess with it later, for my approach have serious security issues, and would probably need a proper getter for the object.


I'm not sure what is the proper way of the mentioned 'security issues', but I've managed to make a 'getter', with passing the object to the getter using bind();:

init: function(){
  object = (...);
  this.getObject = this.getObject.bind(this,[object]);
},
getObject(){
  return arguments[0];
}

If this is horrible, and should have been dealt otherwise, please let me know.
Maybe i should use the get getterName(){//getterBody}, but it throws me some errors.
Updated fiddle here.

Sign up to request clarification or add additional context in comments.

2 Comments

Interesting. Thanks for the heads up. This could work. Let me figure out a way to automate the process, and I'll post back ;)
While I have deviated myself on what I was doing, there is a problem with the el.components.componentName.object, since the componentName doesn't seem to be defined at the moment I'm accessing it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.