API Powered Angular Dynamic Sidebar

Blue and black striped navigation with top level items and children.

Last Updated on August 9, 2024 by E. Scott

This Angular dynamic sidebar illustrates how to populate a navigation with JSON. Something that may become necessary in the event you need to assign user permissions to page content. This would more than likely be accomplished via an API. Hence the dummy data JSON stream. No doubt there’s other ways to develop such a feature. This is but one possible method.

Like many of my other projects here, this is written in Angular. However, it’s a basic foundation. Otherwise, it’s largely TypeScript. Some areas are even vanilla JavaScript. The underlying tools needed here are HTML, CSS, JavaScript, and TypeScript.

Angular dynamic sidebar shown in a mock website with grey header, sidebar background and body section.

In turn, converting this to another framework or removing Angular is a low level endeavor.

If you’re new to TypeScript or looking for more project exercises, this countdown is a good one. Not a lot of code, but very powerful. For vanilla JavaScript, this is a good practice resource. I’ve always found building projects to be more useful than conventional tutorials. Lastly. this date picker covers some good concepts.

Expanded sidebar shown in a mock website with grey header, sidebar background and body section.

Git Repo

This Angular dynamic sidebar has both static links and optional dropdown menus.

Angular Dynamic Sidebar Data

Let’s begin with the data.

  • linkText is the top tier text
  • parentLink is the top tier link
  • The menu boolean dictates whether the drop down is open or closed.
  • The submenu array contains the drop down menu text and link.
  • Drop down menus appear contingent on whether or not the array has a length.
  • The payload below shows two menu items without a drop down, followed by a third menu item with a drop down.
// JSON Driven Menu Selection
[
  { "linkText": "About", "parentLink": "/about", "menu": false, "submenu": [] },
  { linkText": "AngularJS SPA", "parentLink": "/angularjs-spa", "menu": false, "submenu": [] },
  { "linkText": "Codepens", "parentLink": "", "menu": false,
      "submenu": [
        { "childtext": "Link 1",
          "link": "https://codepen.io/UX_Dev/live/5c88e99d0e9a03834dc2d113c24f9daa" },
        { "childtext": "Link 2",
          "link": "https://codepen.io/UX_Dev/pen/LmxqKa" },
        { "childtext": "Link 3",
          "link": "https://codepen.io/UX_Dev/pen/yzejjQ" },
        { "childtext": "Link 4",
          "link": "https://codepen.io/UX_Dev/pen/xoLQrY" },
        { "childtext": "Link 5",
          "link": "https://codepen.io/UX_Dev/pen/2788684ea795e215e9dff54bd5abc66b" }
      ]
  }
]

For many years now, I’ve been building data driven content. As a developer, this is something I recommend to all developers. Building data driven UIs everyday for an extended period will cause your skills to improve drastically.

When using payloads, we work with APIs. Some of which have keys or require special values in the request itself. Neither of which are required in this case as this payload is being fetched locally. But if you’re fetching data like this elsewhere, you may want to hide the API key or some of the data the server returns. This is undoubtedly a topic for another day, but just food for thought. APIs have rules. There’s a structure to follow. One in which must be adhered to, to avoid vulnerabilities.

Developing an Angular Dynamic Sidebar

First stop is to create an interface that represents the payload. SidebarModel does just that. A TypeScript interface is not compiled to JavaScript, since it doesn’t exist in the language. It’s merely a virtual outline solely residing in the TypeScript ecosystem. Used for type checking. Upon transpiling however, they’re removed.

export class SidebarModel {
    linkText: string;
    parentLink: string;
    menu: boolean;
    submenu: { childtext: string; link: string }[];
  }
  • Set the result equal to an empty array with a SidebarModel type.
  • Get the data in the constructor with a reference to the HttpClient.
  • Set result equal to the response.
export class SidebarComponent {
    private unsubscribe$ = new Subject<void>();
    result: SidebarModel[] = [];
    expandAll: boolean = false;
    
    constructor(private _http: HttpClient) {
      this._http.get<SidebarModel[]>(
        'path-to-json')
        .pipe(takeUntil(this.unsubscribe$))
        .subscribe((res) => {
        this.result = res;
      });
    }
  }
  • The ddToggle fn toggles the menu boolean by passing the indice.
  • toggleAll does the same thing, but it applies to every element.
  • That’s really all there is to it!
  ddToggle(i: number) {
    this.result[i].menu = !this.result[i].menu;
  }

  toggleAll() {
    this.expandAll = !this.expandAll;
    for (var i = 0; i < this.result.length; i++) {
      this.result[i].menu = this.expandAll;
    }
  }
  • Loop over the result containing the imported data.
  • There’s two links—one for items with a submenu, the other without a submenu. Each contains the ddToggle fn. Items that have a parentLink are accommodated.
  • The sub-menu class has a dynamic class tied to the menu boolean value.
  • Following, we loop over the submenu items.
<div id="dynamic-sidebar">
  <span class="expand-all" (click)="toggleAll()">{{
    !expandAll ? 'Expand All' : 'Close All'
  }}</span>
  <div class="wrapper element-shadow">
    <div class="group" *ngFor="let item of result; let i = index">
      <a
        *ngIf="item.submenu.length"
        (click)="ddToggle(i)"
        class="light-blue-bg"
        >{{ item.linkText }}</a
      >
      <a
        *ngIf="!item.submenu.length"
        (click)="ddToggle(i)"
        class="light-blue-bg"
        >{{ item.linkText }}</a
      >
      <div
        *ngIf="item.submenu.length"
        class="caret"
        [ngClass]="{ 'rotate-caret': item.menu }"
      >
        ▶
      </div>
      <div class="sub-menu" [ngClass]="{ 'show-menu': item.menu }">
        <ul *ngFor="let menu of result[i].submenu">
          <li>
            <a>{{ menu.childtext }}</a>
          </li>
        </ul>
      </div>
    </div>
  </div>
</div>

The only thing remaining here is to add HttpClientModule to the module. Which to my understanding is not needed in current versions of Angular.

Not doing this in Angular 16 or lower I believe, will cause an error. As stated above, there’s numerous ways to accomplish this. Regardless of how it’s done, conceptually, this is a theoretical reference. Feel free to fork this Angular dynamic sidebar project located here. Have a great day!

2 Trackbacks / Pingbacks

  1. Powerful & Attractive, Angular Countdown Timer w/ Minimal Code - Frontend Development
  2. Custom ChartJS Line Chart Solution - Frontend Development

Leave a Reply

Your email address will not be published.


*