Skip to content

Feature: navbar configuration #1200

@smnbbrv

Description

@smnbbrv

Hi guys,

here is a small module I use for my projects:

// index.ts
export * from './navbar-item-abstract.class';
export * from './navbar-header.class';
export * from './navbar-item.class';
export * from './navbar-separator.class';

// navbar-header.class.ts
import { NavbarItemAbstract } from './navbar-item-abstract.class';

export class NavbarHeader extends NavbarItemAbstract {

  public type = 'header';

  constructor(public headerText: string) {
    super();
  }

}

// navbar-item-abstract.class.ts
export abstract class NavbarItemAbstract {

  public abstract type: string;

}

// navbar-item.class.ts
import { NavbarItemAbstract } from './navbar-item-abstract.class';

export class NavbarItem extends NavbarItemAbstract {

  public type = 'item';

  constructor(
    private name: string,
    private options?: {
      iconClass?: string; // icon is not supported yet
      routerLink?: any[];
      externalLink?: {
        url: string;
        target: string;
      };
      subnavigation?: NavbarItemAbstract[];
    }
  ) {
    super();

    this.options = this.options || {};

    if (this.options.externalLink) {
      this.options.externalLink.target = this.options.externalLink.target || '_self';
    }
  }

  public get iconClass() {
    return this.options.iconClass;
  }

  public get routerLink() {
    return this.options.routerLink;
  }

  public get externalLink() {
    return this.options.externalLink;
  }

  public get subnavigation() {
    return this.options.subnavigation;
  }

}

// navbar-items.component.ts
import { Component, Input } from '@angular/core';
import { NavbarItemAbstract } from './navbar-item-abstract.class';

@Component({
  selector: '[navbarItems]',
  template: `
<template ngFor let-item [ngForOf]="navbarItems">
  <li *ngIf="!item.subnavigation || !item.subnavigation.length" routerLinkActive="active">
    <a *ngIf="item.routerLink" [routerLink]="item.routerLink">{{ item.name }}</a>
    <a *ngIf="item.externalLink" [href]="item.externalLink.url" [target]="item.externalLink.target">{{ item.name }}</a>
  </li>

  <li *ngIf="item.subnavigation &amp;&amp; item.subnavigation.length" class="dropdown" dropdown>
    <a dropdownToggle class="dropdown-toggle" role="button" aria-haspopup="true">
      {{ item.name }} <span class="caret"></span>
    </a>
    <ul class="dropdown-menu">
      <template ngFor let-subItem [ngForOf]="item.subnavigation">
        <li *ngIf="subItem.type === 'item'" routerLinkActive="active">
          <a *ngIf="subItem.routerLink" [routerLink]="subItem.routerLink">{{ subItem.name }}</a>
          <a *ngIf="subItem.externalLink" [href]="subItem.externalLink.url" [target]="subItem.externalLink.target">{{ subItem.name }}</a>
        </li>
        <li *ngIf="subItem.type === 'separator'" role="separator" class="divider"></li>
        <li *ngIf="subItem.type === 'header'" class="dropdown-header">{{ subItem.headerText }}</li>
      </template>
    </ul>
  </li>
</template>
  `
})
export class NavbarItemsComponent {

  @Input()
  public navbarItems: NavbarItemAbstract[];

}

// navbar-separator.class.ts
import { NavbarItemAbstract } from './navbar-item-abstract.class';

export class NavbarSeparator extends NavbarItemAbstract {

  public type = 'separator';

  constructor() {
    super();
  }

}

// and finally navbar.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { DropdownModule } from 'ng2-bootstrap';
import { NavbarItemsComponent } from './navbar-items.component';

@NgModule({
  declarations: [
    NavbarItemsComponent
  ],
  imports: [
    CommonModule,
    RouterModule,
    DropdownModule
  ],
  exports: [
    NavbarItemsComponent
  ]
})
export class NavbarModule {}

What it does: it allows a navbar configuration right inside of the component without writing HTML.

Why it is useful: when it is about more than 10 menu items the HTML becomes unreadable and unsupportable. Also it is useful to avoid the repetitions, e.g. adding rights check for every li item is really annoying while configuring it inside of controller is damn easy.

Usage example:

Component:

  public ngOnInit() {
    this.navigationItems = [
      new NavbarItem('Content', { subnavigation: [
        new NavbarHeader('Content'),
        new NavbarItem('Menu', { routerLink: ['menu'] }),
        new NavbarItem('Pages', { routerLink: ['pages'] }),
        new NavbarSeparator(),
        new NavbarHeader('Global parts'),
        new NavbarItem('Header', { routerLink: ['header'] }),
        new NavbarItem('Footer', { routerLink: ['footer'] })
      ]}),
      new NavbarItem('Assets', { subnavigation: [
        new NavbarItem('Images', { routerLink: ['image/search'] }),
        new NavbarItem('Video', { routerLink: ['video/search'] }),
      ]})
    ];
  }

HTML:

<nav class="navbar navbar-default" *ngIf="shown">
  <div class="container-fluid">
    <div class="navbar-header">
      <a class="navbar-brand" routerLink="/">Content</a>
    </div>

    <ul class="nav navbar-nav" [navbarItems]="navigationItems"></ul>

    <ul class="nav navbar-nav navbar-right" [navbarItems]="navigationItems-right"></ul>
  </div>
</nav>

So the actual question is: is it possible to build this functionality as another ng2-bootstrap module?

This could also cover the #540

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions