Search code examples
angularaccordionng-bootstrap

Component init inside ng bootstrap accordion even if closed


I'm facing an issue using ng bootstrap's accordion with components inside them. I have an *ngFor and for each index array there is an accordion that contains a component. The problem is, even accordions at DOM rendering are closed all components inside accordion starting their life-cycle causing https calls for each component inside each accordion. By default i left the accordion property destroyOnHide (property of ngbAccordion) set as true but nothing.

Did someone face this problem?

Below HTML code component settings-tab.component.html

div
  class="mt-2"
  #accordion="ngbAccordion"
  ngbAccordion
  [closeOthers]="true"
  (shown)="lastAccordionOpened = $event"
>
  <div
    ngbAccordionItem
    *ngFor="let room of rooms; let index = index"
    [ngbAccordionItem]="index.toString()"
  >
    <div ngbAccordionHeader>
      <button
        ngbAccordionButton
        [style.backgroundColor]="room.bgColorHex"
        [style.color]="room.textColorHex"
      >
        {{ room.name }}
      </button>
    </div>
    <div ngbAccordionCollapse>
      <div ngbAccordionBody>
        <app-room-settings
          [room]="room"
          (roomChanged)="roomChanged.emit()"
        ></app-room-settings>
      </div>
    </div>
  </div>
</div>

HTTP calls are made inside and if I set a console.log inside the constructor there are as many console.logs as the length of the array of rooms


Solution

  • UPDATE:

    The problem was that the accordion body content must be wrapped in an ng-template since this was not done, the content is getting rendered even when the accordion is not open.

    The same method can be found in the Official Doc Examples


    First set destroyOnHide to false so that the components are destroyed.

    Then you need to add trackBy so that ngFor knows which elements has changed, if not it will rerender all components.

    <div
      class="mt-2"
      [destroyOnHide]="false"
      #accordion="ngbAccordion"
      ngbAccordion
      [closeOthers]="true"
      (shown)="lastAccordionOpened = $event"
    >
      <div
        ngbAccordionItem
        *ngFor="let room of rooms; trackBy: trackByFn;let index = index"
        [ngbAccordionItem]="index.toString()"
      >
        <div ngbAccordionHeader>
          <button
            ngbAccordionButton
            [style.backgroundColor]="room.bgColorHex"
            [style.color]="room.textColorHex"
          >
            {{ room.name }}
          </button>
        </div>
        <div ngbAccordionCollapse>
          <div ngbAccordionBody>
            <ng-template>
                <app-room-settings
                  [room]="room"
                  (roomChanged)="roomChanged.emit()"
                ></app-room-settings>
            </ng-template>
          </div>
        </div>
      </div>
    </div>
    

    TS:

    ...
    identify(index: number, item: any){
       return item.id; 
    }
    ...
    

    You have to supply and unique ID for each element, if there is no such index, you can either generate an ID, or just return the index.