Say I have a custom component that loads up a <canvas> tag, some graphical features that it draws. Then the parent component must add more information to it later on through fetch-client and thus update the drawing.
However, the object class in the subcomponent is not ready at the very start. It needs time to load.
export class ParentSinglePage {
@bindable childClass;
loadEvenMoreData(){
console.log("Object class is NOT ready... :( .. I can't believe you tried to load data into a 'null' object", this.childClass);
}
childClassChanged(nv, ov){
console.log("Object class seems ready: ", this.childClass);
console.log("Exactly same is nv: ", nv);
console.log("Okay, so why even bother with .call binding... just call loadData() from here?");
}
}
The child component is calling “ReadyToLoad” AFTER it is ready, but I guess, it perhaps should be a promise? Or is there a way to promise bindables or something?
Or should I rather call the “loadData()” in the Changed handler? The problem with this is if I have “50 childElements” on the page, then I have to create 50 change-handlers…
I’d rather have a way for each subcomponent (child element) to call its own “loadDataSpecific()” function in the PARENT. But only after subcomponent (child element) has readied the class object.
When the component is ready call dispatchEvent on it with a bubbling event. Then in the parent, bind a delegate on a container with all the children. This single event handler will catch all the ready events. The event target will give you the element in question.
You add a selected.delegate=MySpecificExtraDataLoadFunction() to your subelement tag in the parent html and have the “valueChanged” function inside the child custom element class and it bubbles the event when childClass changes and thus has new data inside (as in it has been loaded!)?
It is sort of what I wanted to do, abstract out the inner-workings of the element into the child element ES6 script, and just start each parent with a number of functions or a big-handler that can load extra data into specific child elements.
Ok I just did my experiments with $event way, and I don’t think this method will work because at the end of this, I have to still set this.childClass = $event.detail (circular copying into a bindable). This bubbling up works well for transferring data from the child to the parent. But I need both-directions. Two-way binding.
The child starts the canvas, the parent adds more data into it after child is ready and the child needs to also see the parent’s changes to the data of the canvas to redraw things. There may be other buttons and events that later on may interact with that object.
I almost need like a singleton class of sorts that is accessibly by both parent and child component. The parent also needs to know when the canvas is ready before adding more data.
I would use the data to drive custom elements inside a canvas custom element that use the canvas API to render themselves. With some clever design, you’ll be able to simplify using the canvas.
<my-canvas>
<my-circle repeat.for="circle of loadedCircles"
r.bind="circle.r" x.bind="circle.x" y.bind="circle.y">
</my-circle>
</my-canvas>
This solves your async loading issue because you (1) load the data and then (2) use the data to drive components in the view which (3) cause lifecycle events to be called.
Canvas is tricky because there is no “path” object like there is in svg. You have to wipe the canvas clean and rerender. That means you have to do a little bit of extra heavy lifting to communicate to the child elements when the parent is wiped clean. There’s also a timing issue between Aurelia’s component render lifecycle and the canvas drawing.
I recommend using DOM events only for bubbling actual events in a hierarchy, only when the goal is to inform other components of an event. For example, you might bubble an “dataloaded” event to say “hey, the data loaded”, so another component can say “when the data loads, hide the loader.” In this case, you’re doing the opposite, you’re trying to say “when the data loads, the canvas should do something!” That’s not an event, so I wouldn’t fire a DOM event.
You could handle this with the event aggregator, but I don’t recommend it. The rule of thumb is don’t use the event aggregator unless you’re building plugins. If you do use the event aggregator, again, it should be to say “something happened!” and not “I want something to happen,” and it should only be in the case where a bubbled DOM event cannot work.
If this answer was helpful and you’d like to see more like them, please consider supporting me on Patreon: https://www.patreon.com/davismj