angularng-bootstrapangular-component-router

How can attach a route to the display of an ng-accordion panel?


I have an ng-accordion defined using an *ngFor loop, and I would like to adjust the location and bind a route to the view of a particular panel. Ideally, when a client clicks to expand a panel, the location would update in the browser and a new history item would be stored in the browser. Additionally, if a client enters a URL which would correspond to a particular accordion item being displayed I want to ensure that the appropriate state is reflected.

For example:

<ngb-accordion closeOthers="true">
  <ngb-panel *ngFor="let faq of faqService.getItems()" id="{{faq.id}}" title="{{faq.title}}">
    <ng-template ngbPanelContent>
      {{faq.body}}
    </ng-template>
  </ngb-panel>
</ngb-accordion>

And the mapped routes might be:

/faq/ABCD
/faq/EFGH
/faq/IJKL

Toggling a particular panel would update the location/ActiveRoute and pasting a URL which would map to a particular panel would cause that panel to be expanded. Any suggestions on how to wire this up?


Solution

  • Your application route will need to be configured something like this, we need the faqId to be a parameter of the route:

    export const appRoutes = [
      {
        path: 'faq/:faqId',
        component: FaqComponent
      }
    ];
    

    and imported into your module like this:

    imports: [RouterModule.forRoot(appRoutes)]
    

    In the markup:

    <ngb-accordion closeOthers="true" [activeIds]="selectedFaqId" (panelChange)="onPanelChange($event)">
      <ngb-panel *ngFor="let faq of faqs" id="{{faq.id}}" title="{{faq.title}}"  >
        <ng-template ngbPanelContent>
          {{faq.body}}
        </ng-template>
      </ngb-panel>
    </ngb-accordion>
    

    Component (I mocked data for this example):

    import { Component } from '@angular/core';
    import { ActivatedRoute, Router } from '@angular/router';
    import { NgbPanelChangeEvent } from '@ng-bootstrap/ng-bootstrap';
    
    @Component({
      selector: 'app-faq',
      templateUrl: './faq.component.html'
    })
    export class FaqComponent {
    
      selectedFaqId = '';
      faqs = [
        {
          id: 'a',
          title: 'faq 1 title',
          body: 'faq 1 body'
        },
        {
          id: 'b',
          title: 'faq 2 title',
          body: 'faq 2 body'
        }
      ];
    
      constructor(private route: ActivatedRoute, private router: Router) {
        route.params.subscribe(x => {
          this.selectedFaqId = x.faqId;
          console.log(this.selectedFaqId);
        })
      }
    
      onPanelChange(event: NgbPanelChangeEvent) {
        this.router.navigateByUrl(`faq/${event.panelId}`);
        console.log(event);
      }
    
    }
    

    I've added a subscription to the route parameters, so that we can reset the selectedFaqId when the route is changed.

    I bound the selectedFaqId to the selectedIds attribute of the accordion. This will expand the panel of the selected Id.

    I respond to manual expansion of the accordion panels by binding to the panelChange event. In that event we set the route to the selected Faq Id. That will navigate the url to the selectedFaqId.