0

I have a parent component like this. So number of items in menu depends on model. And model changes.

<parent>
<menu>
  <item *ngIf="model.info">
   ... some data
  <item *ngIf="model.comments">
  .... some data 
  <item *ngIf="model.attachments">
   ... some data
  <item *ngIf="model.orders">
  .... some data
 </menu>
</parent>

Parent component:

 export class Parent {
   @Input() public model: any;
 }   

Where menu is a component itself using ng-content like this, I am creating menu based on menuItems count.

selector: 'menu',
template: ` <ul class="nav"
    <li *ngFor="let item of items"  [class.active]="item.active"
    <a class="btn" (click)="menuItemClicked(item)"> </a>
    </li>
    </ul>

  <div class="content">
     <ng-content></ng-content>
  </div>

`
export class MenuComponent  {
  @ContentChildren(MenuItem) items;

  ngAfterContentInit() {
     this.setActiveMenuItem();
  }

  public menuItemClicked(item) {
    this.changeActiveItem(item)
  }


  private setActiveItem() {
      const items = this.items.toArray();
      const actives = this.items.filter(i => i.active);

      if (!actives.length && items.length) {
         items[0].active = true;
      }
  }

  private changeActiveItem(item) {
      const items = this.items.toArray();
      items.forEach(i => i.active = false);
      item.active = true;
  }

}

Where a single menuItem itself is a component, that uses ng-content:

@Component({
selector: 'item',
template: `
   <ng-content *ngIf="active"></ng-content>
`
})
export class MenuItemComponent {
  @Input() active = false;

  public setActive() {
    this.active = true;
  }

  public setNotActive() {
    this.active = false;
  }

}

Parent component is used like this:

<app>
    <parent *ngIf="model.displayMenu" [model]="model"></parent>
</app>

So it is created only once, and then model changes based on user actions (clicks. etc.)

Now what is the problem ? The problem is that suppose I have created a parent component. And in my usecase model changes. I know exactly when model changes, but the thing is that on that change I want always the first menu item to be active on that change.

My problem is that I am not able to use ngAfterViewInit hook, because the items are not already there. It takes some time for angular change detection to change that content. If I used ngAfterViewInit, the menu items are not updated in that hook yet. I do not want to use ngAfterViewChecked, because it constantly triggers change detection and causes performance issues (behind there is a constant stream change in model info).

Example: I have active 4th menu item and model changes. The next model has only 2 menu items, and I want the menu item smoothly to switch to 1st menu item. If I do not do this, then I get empty content.

How could this be solved?

Jerzy Gruszka
  • 1,629
  • 4
  • 15
  • 20
  • Kinda difficult to understand, but it seems like you want one item to be active at all times, so if no items are active, you are setting the first item to be the active one. Is that correct? – Jason Goemaat Mar 14 '18 at 19:21
  • And by 'model changes' you mean a user might click on something somewhere else in your app and now `model.attachments` which was the active menu item might be null so none of the menu items will be active, so you want to set the first menu item to now be the active one? – Jason Goemaat Mar 14 '18 at 19:33

1 Answers1

0

You could simply implement your ContentChildren with a setter:

items: QueryList<MenuItem>;

@ContentChildren(MenuItem) 
set menuItems(items) {
    this.items = items;
    this.setActiveMenuItem();
}

The menuItems-setter is called when the contenchildren change.

Hope this help and I understood your problem correctly.

Leon
  • 480
  • 4
  • 8