#860·framework

Custom attribute "attached" event too early?

Author: DamageLimiterCreated Feb 8, 2018Updated May 30, 2022

I'm submitting a bug report

  • Library Version: "aurelia-framework": "1.1.5"

Please tell us about your environment:

  • Operating System: Windows 10

  • Node Version: 8.9.4

  • NPM Version: 5.6.0

  • Aurelia CLI OR JSPM OR Webpack AND Version CLI 0.32.0 | webpack 3.5.5

  • Browser: all

  • Language: TypeScript 2.6.2

Current behavior: (corresponding stackoverflow questions: https://stackoverflow.com/questions/48596232/custom-attribute-attached-event-too-early) The custom component
I've created a custom component for the navigation of my app. It consists of an ul element and all its li elements are dynamically created based on the items in the router's navigation list. This is happening in the attached event of the component. Nothing special is going on here.

The custom attribute
But because I want to have a good looking fancy menu I also created a custom attribute and implemented it into the root ul element of the custom component. In the attached event of the custom attribute I'd like to do some dom manipulations so that my menu looks like a menu created by those cool kids.

The problem
Although the attached event of the custom attribute is fired AFTER the attached event of the custom component, the dynamically created li items are not part of the dom in the attached event of the custom attribute yet.

The question
My assumption was that on attached event the view of the actual component is attached to dom and that all components before are also attached to the dom. And when I am done with the attached event the html that has been dynamically created here is also attached. Am I mistaken?

On a side note
I am aware of using TaskQueue could solve my problem. However, I would like to know if there's a different approach/solution first because I believe that moving things in time could cause a chain of paradoxes leaving you in a maintenance nightmare.

Repro: Custom-Component HTML

<ul id="mainMenu" smart-menu>
    <li class="${category.hasActiveItem ? 'active open' : ''}" repeat.for="category of navigationItems">
        <a if.bind="category.navModels.length == 1" href.bind="category.navModels[0].href">
            <i class="${category.icon}"></i>
            <span class="menu-item-parent">${category.navModels[0].title}</span>
        </a>
        <a if.bind="category.navModels.length > 1" href="#" title.bind="category.categoryName">
            <i class="${category.icon}"></i>
            <span class="menu-item-parent">${category.categoryName}</span>
        </a>
        <ul if.bind="category.navModels.length > 1" repeat.for="nav of category.navModels">
            <li class="${nav.isActive ? 'active' : ''}">
                <a href.bind="nav.href" title.bind="nav.title">
                    <span class="menu-item-parent">${nav.title}</span>
                </a>
            </li>
        </ul>
    </li>
</ul>

SmartMenuCustomAttribute

public async attached() {
    //this does work
    //*******************************
    //this._task.queueMicroTask({
    //  call: () => {
    //    this.addCollapseIcons();
    //  }
    //});

    //this does not work
    this.addCollapseIcons();
  }

private addCollapseIcons(){
    this.$("#mainMenu).find('li:has(> ul)').each((i, li) => {
      let $menuItem = $(li);
      let $a = $menuItem.find('>a');
      let sign = $('<b class="collapse-sign"><em class="fa fa-plus-square-o"/></b>');

      $a.on('click', (e) => {
        this.toggle($menuItem);
        e.stopPropagation();
        return false;
      }).append(sign);
      
    })
  }
  • What is the motivation / use case for changing the behavior? I kind of expected to be able to modify attached items without the need of defering the execution. Is this a bug or does it work as designed?