Scroller as Chrome with custom controlParent

edited November 2014 in Enyo 2.4
I've run into a problem trying to use a scroller as chrome component and trying to override its controlParent. It appears the content inside the scroller has no owner or parent. The setup I have is rather complicated, but I've tried to boil it down to the simplest form I could find that isolates the case:

The desired behavior would be as follows:
- I'd like for myGenericKind to render an enyo.Scroller containing a form tag which acts as the scroller's controlParent.
- I'd like for the mySpecificKind to render it's components inside the form tag.
- I'd like for the myGenericKind to bubble the onDestroying event all the way up to myHost so that it can respond to the fact that it's child is about to be destroyed.
- I'd like for the myBubblyThing to bubble the onDestroying event all the way up to myHost so that it can respond to the fact that it's child is about to be destroyed.

Instead, the onDestroying event from myBubblyThing never bubbles up to myHost, because the controlParent of myGenericKind.scroller cannot find a bubbleTarget.

The bubbling only fails when I add a custom controlParent to the scroller (the form tag). As soon as I remove it, there is no problem bubbling the destroy event.

However, if I don't add a custom client to the scroller, the entire scroller gets replaced by the components of mySpecificKind.

I'd like to both keep the form tag inside the enyo.Scroller as controlParent ánd still be able to bubble events from inside the controlParent up the tree.

Does anyone have any ideas what's going wrong or and/or how to fix it?


  • I think I've sorted this out. The issue here is the order in which the controls are destroyed. Component.destroyComponents() iterates over its list of owned components (via getComponents()) and destroys each in FIFO order. So in your example, that's Scroller, form, anonymous control with content, and finally myBubblyThing.

    When you have the form as the controlParent, the event bubbling fails because when it gets to the form, it stops because the form has already gone through its destroy() and is no longer a child of or owned by anything. Therefore, getBubbleTarget() returns nothing and the bubbling stops.


    The best solution probably depends on the rest of your app. One option is to dispatch your custom event starting at the components owner rather than itself.

    My first instinct was to override getBubbleTarget() but you likely don't want to do that for all events and that method doesn't receive any context about the event so it's all or nothing as it is. You could do something like this to indicate you need some special handling, though:

    And probably several other hacky options ...

  • @theryanjduffy‌ the getBubbleTarget solution is quite interesting actually. I don't really understand why it works though..

    As far as I know, the default implementation of getBubbleTarget successively looks for a bubbleTarget property, a parent property, and finally an owner property.

    As I understand it, when the bubbling fails, all three of those properties are undefined. The overridden getBubbleTarget method just returns the owner property no-questions-asked.

    But, if the original method thinks the owner property is undefined, why does the overridden method disagree?

    That is, I see that it's working with the overridden method, and if I simply comment out the custom firingOnDestroying case within the overridden method it fails. But I cannot get my head around what the difference is.
  • Allright, I just tried this in-app, and bubbling from the owner seems to work very well in my current app. It also seems cleaner than overriding getBubbleTarget simply because I'll only be overriding the destroy method (which I was doing anyways) and leaves the getBubbleTarget method in-tact.

    So yeah, very very big thanks a lot for finding this solution!
  • So, trying to figure out what makes the difference here:

    If I use the following code:
    var myMixin = {
        name: "myMixin",
        destroy: enyo.inherit(function(sup) {
            return function() {
                this.bubbleUp("onDestroying", {
                    message: + " " + this.destroyMessage
                sup.apply(this, arguments);
    I would expect the following flow:
    • myBubblyThing.destroy inherits myMixin.destroy
    • myMixin.destroy calls myBubblyThing.bubbleUp, which is inherited from enyo.Component.prototype.bubbleUp
    • myBubblyThing.bubbleUp calls myBubblyThing.getBubbleTarget, which is inherited from enyo.UiComponent.prototype.bubbleUp
    • myBubblyThing.getBubbleTarget checks it's bubbleTarget, parent and finally owner, finding that all of them are undefined
    • myBubblyThing.bubbleUp has no bubble target, and thus returns false
  • Now, when I use:
    var myMixin = {
        name: "myMixin",
        destroy: enyo.inherit(function(sup) {
            return function() {
                this.owner.bubble("onDestroying", {
                    message: + " " + this.destroyMessage
                sup.apply(this, arguments);
    The myMixin.destroy method clearly is able to acccess the an owner property. But assuming the same starting conditions as the scenario governing the reconstructed fail case in my previous post, it should already been undefined at this point?
  • This line is wrong:
    myBubblyThing.getBubbleTarget checks it's bubbleTarget, parent and finally owner, finding that all of them are undefined
    myBubblyThing.getBubbleTarget is getting a value: it's this.parent which is the form. But, since the form has been destroyed, the form's call to getBubbleTarget() is what is returning nothing.

    That's ultimately why my solutions work. By skipping bubbling through the control's parent (the form), bubbling works because the owner hasn't been destroyed yet. It isn't destroyed until all of its components are destroyed. The order is driven by ownership only and not containment.
  • Aha, that makes total sense. Thanks!
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!