I'm still trying to get my head around the best way to structure event flows. Someone made a lot of sense by telling me that contained objects should be decoupled from their parent containers so they're reusable between containers. Instead of calling their parent's function, they dispatch an event. Sensible!
I recently myself in the awkward-feeling situation of dispatching an event on a contained object, from its parent. It seems more intuitive for an object to be the one dispatching its own events. Maybe I'm bordering on breaking the Law of Demeter?
Example of what I'm doing:
child.dispatchEvent(new ChildEvent(ChildEvent.IM_BEING_A_CHILD));
Am I correct in feeling strange here, or is it a normal thing for one object to dispatch another object's events?
You are not necessarily breaking any rules here - if dispatchEvent is a public function then you are allowed to call it from anywhere you like.
If you want to keep things cleaner from an encapsulation point of view you could instead expose an explicit function that the parent can call for a particular event to occur - eg:
child.doSomeChildAction();
and on the child class:
public function doSomeChildAction():void
{
dispatchEvent(new ChildEvent(ChildEvent.IM_BEING_A_CHILD));
}
This way if you need to change the way child sends the event, what kind of event is sent, or what actions occur when the child sends the event it is all encapsulated in the child class, and so will be easier to manage.
Related
Is there a way to execute a method exactly after the component has its properties available but before the first render?
I mean something between the class contructor() and firstUpdated().
It sounds trivial, maybe in fact I'm missing something trivial..
The element's constructor is called when the element is created, either through the HTML parser, or for example through document.createElement
The next callback is connectedCallback which is called when the DOM node is connected to the document. At this point, you have access to the element's light DOM. Make sure to call super.connectedCallback() before doing your own work, as the LitElement instance has some work to do here.
The next callback is shouldUpdate, which is an optional predicate that informs whether or not LitElement should run its render cycle. Useful if for example, you have a single observed data property and destructure deep properties of it in render. I've found that it's best to treat this one as a predicate, and not to add all sorts of lifecycle logic inside.
After that, update and render are called, then updated and firstUpdated. It's generally considered bad practice to perform side effects in render, and the occasions that you really need to override update are rare.
In your case, it sounds very much like you should do your work in connectedCallback, unless you are relying on LitElement's rendered shadow DOM, in which case, you might consider running your code in firstUpdated, then calling this.requestUpdate() to force a second update (or changing some observed property in firstUpdated)
More info: https://lit-element.polymer-project.org/guide/lifecycle
I've seen people adding the event listener on the "ready" function and others on "connectedCallback". My question is, what are the pros and cons of each place? On connected we are responsible to remove it; in ready, it will stay there, and I'm unsure if it is a problem.
Should I do this:
connectedCallback() {
super.connectedCallback();
this.addEventListener('click', this.myFunction.bind(this));
}
disconnectedCallback() {
super.disconnetedCallback();
this.removeEventListener('click', this.myFunction);
}
Or this:
ready() {
super.ready();
this.addEventListener('click', this.myFunction.bind(this));
}
Up until Polymer 1.x.whatever , the ready callback in the life cycle of an element, was called, once
the element registered its shadow DOM
any <content>'s were distributed
and then, post ready , attached was fired
So, you could possibly have used ready as a one time callback after everything was indeed ready
With Polymer 2.0 onwards, there have been contractual changes to how callbacks are fired, and
The ready callback no longer is guaranteed to execute after the new <slots> are distributed meaning, there is no surety that the ready itself will wait for content / light DOM distribution.
attached is now the new connectedCallback and is essentially useful for element level DOM manipulations such as setting attributes , appending children etc. This is a lifecycle change that happens after the slot nodes are distributed and the element itself is attached in the DOM hierarchy, but not necessarily after a paint.
SO, for any event that does not rely on any ::slotted content, use the ready callback
for anything that requires a knowledge of all distributed content along with the shadow DOM, use the connectedCallback
However, when possible, use the afterNextRender method of the super class Polymer , within your element's callback to add event listeners
these are what I could possibly think of.
All this and much more, if it helps, here
I haven't yet read about us having to remove an event listener from a lifecycle callback, or anything as such.
If you are referring to cases where, the element itself may be connected and disconnected dynamically / or in the flow of things,
And, with that in mind, you are adding an event listener on a global / native element within your element's life cycle callbacks ,
like attaching an event listener on the window inside your custom-element's ready or connectedCallback ,
Only in such cases, does polymer advise you to remove the event listener on disconnect
So the *ADDED_TO_STAGE* and *REMOVED_FROM_STAGE* Events do not notify ancestors in any way. I've looked all around and can't find an answer...do I need to build a new class to do it, or is there a way to hack it into using the Capture/Bubble notification phases?
The descendants of a DisplayObject that is removed from the stage dispatches it's own REMOVED_FROM_STAGE and ADDED_FROM_STAGE events when their parent is removed from the display list of the stage.
So you can simply add an event listener to any descendant and have it handle those events directly.
I may be misunderstanding what you are trying to do, but in general events bubble upward from child objects to their parents. So when you say the descendants are not notified, you are correct as they are not upward on the heiarchy. However, if your goal is for a given object to know when it is added/removed to/from the display list of the stage, that can be had by listening to the specific DisplayObject.
So although the added/removed events do not bubble to their ancestors, there is a way to listen for their events directly.
And if you subclassed MovieClip or Sprite, you could have that class listen for the event, and then dispatch an event that does bubble to the ancestors.
However, I think it be better for us to understand better what you are trying to do as your approach might be the real issue.
I have an object that controls another object that is on the display list.
The setup looks like this:
Parent (Main Timeline)
- Child
-- Grandchild --> contains instance of behaviour class that controls the grandchild's movement
I have an event in the "behaviour" that I'd like to reach the parent, but the behavior does not extend Sprite or MovieClip.
How can I get this event to reach the parent?
There are two ways. If the "behavior" object has access to the GrandChild, the GrandChild has a root property (which, conveniently) is the root -- this will only work if there is a path-to-root though. You can't remove a child (or its parents) and then expect to be able to access the root directly. But, if you have a DisplayObject which you know is on the stage, you can use that to communicate to the root directly. (You can also, with proper casting, access all of the parents and grandparents of the Grandchild).
You can also have a centralized EventDispatcher which is listened to by whatever you want to listen to it. Basically, create a Singleton (you'll need to look that up for the AS3 way) which subclasses EventDispatcher and then tell that to dispatch whatever events you need.
It would look something like this:
//on the root
EventDispatcher.getInstance().addEventListener( "myCustomEvent", myEventhandler );
//in behavior
EventDispatcher.getInstance().dispatchEvent( new Event( "myCustomEvent" ) );
//root now acts accordingly.
The "BubblingEventDispatcher" class you are referring to is a bit misleading. It's actually just adding children to the display list in order to enable bubbling:
AS3 Event Bubbling outside of the Scenegraph/DisplayList
In order to relay events without having access to bubbling, you are basically stuck listening for the events on every level and relaying them along manually. It accomplishes the same thing as if you had bubbled the event but it's more of a hassle and it also introduces tighter coupling.
Is there a fast and efficient way of removing all children listeners etc from my app. If everything is contained in a display object on the stage called View? I have lots of dynamically called children and their listeners do not get removed when I remove the View they reside in.
public function _discard ():void
{
// Quick way to discard the view, remove children and listeners
removeChild(View);
View = null;
}
Is this a valid way of removing the parent and children?
If you use weakly referenced listeners then the listeners will not prevent the objects from being garbage collected when you nullify them.
useWeakReference is the 5th parameter in the addEventListener call.
AFAIK, there is no way to enumerate listeners in ActionScript. So you have to write cleanup code, removeEventListener calls symmetrical to addEventListener.
alxx is right. there is no easy trick to solve this problem. In order to avoid removing manually all children/listeners, I would create a helper method that receives a View and recursively removes all children and their respective listeners.