Petros Kyriakou
Petros Kyriakou

Reputation: 5343

ngTemplateOutlet prevents directive from working correctly

I am building a tabs component and in its headers i added a ripple component that attaches a class and does the animation as seen in code below.

However even though it works in all my other components i.e buttons seems that it fails to work when used inside an ngTemplateOutlet context. Perhaps i am doing something wrong. Any tips welcome.

tabsComponentTemplate

<div class="nui-tab-group">
  <ng-template #defaultTabsHeader let-tabs="tabs">
    <ul class="nui-tab-group-buttons" *ngIf="tabs">
      <li class="tab-button"
          [ngClass]="{selected: tab.selected}"
          (click)="selectedTab(tab)"
          ripple-c
          *ngFor="let tab of tabs">{{ tab.title }}
      </li>
    </ul>
  </ng-template>

  <ng-container *ngTemplateOutlet="headerTemplate || defaultTabsHeader; context: tabsContext"></ng-container>

  <ng-content></ng-content>
</div>

ripple component main part

@HostListener('click', ['$event', '$event.currentTarget'])
  click(event, element) {
    if (this.rippleStyle === 'expand') {
      let ripple = document.createElement('span'),
        rect = element.getBoundingClientRect(),
        radius = Math.max(rect.height, rect.width),
        left = event.pageX - rect.left - radius / 2 - document.body.scrollLeft,
        top = event.pageY - rect.top - radius / 2 - document.body.scrollTop;

      ripple.className = 'expand-ripple-effect animate';
      ripple.style.width = ripple.style.height = radius + 'px';
      ripple.style.left = left + 'px';
      ripple.style.top = top + 'px';
      ripple.addEventListener('animationend', () => {
        element.removeChild(ripple);
      });
      element.appendChild(ripple);
    }
  }

Upvotes: 1

Views: 1773

Answers (1)

yurzui
yurzui

Reputation: 214165

The main reason for this is that your list is rerendered every time change detection happens because you're passing new object to context every time:

get tabsContext() {
  return {
    tabs: this.tabs
  }
}

NgTemplateOutlet directive sees this changes and clears templateю

Use prepared data for template

tabsContext: any;

ngAfterContentInit (): void {  
  ...  
  this.tabsContext = {
    tabs: this.tabs
  }
}

Stackblitz Example

See also similar case

Upvotes: 1

Related Questions