Shadow1349
Shadow1349

Reputation: 771

AngularJS Dynamic Navbar

I need some help building a navigation bar with N elements.

I have a directive that looks like this:

angular.module('tool')
.directive('navigation', [function () {
    return {
        restrict: 'E',
        controller: 'NavigationController',
        templateUrl: '/Scripts/Directives/Navigation.html'
    }
}]); 

Navigation.html:

<ul  class="nav navbar-nav" ng-repeat="NavItem in navitems">
    <li>
        <a href="{{NavItem.Href}}">{{NavItem.Name}}</a>
    </li>
</ul>

The layout:

<div class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">Project name</a>
        </div>
        <div class="navbar-collapse collapse">
            <navigation></navigation>
        </div>
    </div>
</div>

NavigationController:

angular.module('tool')
.controller('NavigationController', ['$scope', '$route', 'NavigationFactory', function ($scope, $route, NavigationFactory) {
    $scope.$route = $route;

    NavigationFactory.navigation().success(function (data) {
        let nav = JSON.parse(data);
        $scope.navitems = data.NavItems;
    });
}]);

And finally the NavigationFactory:

angular.module('tool')
.factory('NavigationFactory', ['$http', function ($http) {
    return {
        navigation: function () {
            return $http({ method: 'GET', url: '' });
        }
    }
}]);

Here is the JSON that it will be parsed:

{
  "NavItems": [
    {
      "SubNav": [],
      "Id": 1,
      "Name": "FileSystem",
      "ParentId": 0,
      "IsAdminItem": false,
      "Href": "/#/FileSystem"
    },
    {
      "SubNav": [],
      "Id": 2,
      "Name": "Settings",
      "ParentId": 0,
      "IsAdminItem": false,
      "Href": "/#/Settings"
    }
  ]
}

What I have right now will work for this JSON but I want to also be able to dynamically generate the dropdown items if one of the NavItems has SubNav values.

I have tried to use my directive recursively by doing this:

<ul  class="nav navbar-nav" ng-repeat="NavItem in navitems">
    <li>
        <a ng-hide="NavItem.SubNav.length > 0" href="{{NavItem.Href}}">{{NavItem.Name}}</a>
        <div ng-show="NavItem.SubNav.length > 0" ng-switch>
            <div ng-switch-when="true">
                <div ng-init="navitems = NavItem.SubNav" ng-include="Navigation.html"></div>
            </div>
        </div>
    </li>
</ul>

However, when I try to do that I don't get any errors it just doesn't show. Here is the test JSON with subnav that got me that error:

{
  "NavItems": [
    {
      "SubNav": [],
      "Id": 1,
      "Name": "FileSystem",
      "ParentId": 0,
      "IsAdminItem": false,
      "Href": "/#/FileSystem"
    },
    {
      "SubNav": [
        {
          "SubNav": [],
          "Id": 3,
          "Name": "Logout",
          "ParentId": 2,
          "IsAdminItem": false,
          "Href": "/#/Logout"
        }
      ],
      "Id": 2,
      "Name": "Settings",
      "ParentId": 0,
      "IsAdminItem": false,
      "Href": "/#/Settings"
    }
  ]
}

How can I make it so that I am able to dynamically generate a Navigation bar with dropdowns using nth level architecture so that when I want to create more navitems I can just create them in the database and not have to worry about the front end?

Thanks!

EDIT

Eventually I want to have nested dropdowns so I don't want to have to keep adding ng-repeats in my directive.

Upvotes: 0

Views: 2469

Answers (2)

Shadow1349
Shadow1349

Reputation: 771

Thank you eliagentili for putting me on the right track, however your answer did not exactly work for me so I modified it slightly.

Here is the new directive js:

angular.module('tool')
.directive('navigation', ['$compile', function ($compile) {
    return {
        restrict: 'E',
        replace: true,
        scope: {
            item: '='
        },
        templateUrl: '/Scripts/Directives/Navigation.html'
    }
}]);

and the new directive template:

<li ng-class="{'dropdown':item.SubNav.length > 0}">
    <a ng-if="item.SubNav.length == 0" href="{{item.Href}}">{{item.Name}}</a>
    <a ng-if="item.SubNav.length > 0" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="true">{{item.Name}} <span class="caret"></span></a>
    <ul class="dropdown-menu">
        <navigation ng-repeat="SubItem in item.SubNav" item="SubItem"></navigation>
    </ul>
</li>

and the layout:

<nav class="navbar navbar-default">
    <div class="container-fluid">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="/#/">Project name</a>
        </div>

        <div class="navbar-collapse collapse" ng-controller="NavigationController">
            <ul class="nav navbar-nav">
                <navigation ng-repeat="SubMenuItem in navitems" item="SubMenuItem"></navigation>
            </ul>
        </div>
    </div>
</nav>

Hope this helps someone else!

Upvotes: 0

eliagentili
eliagentili

Reputation: 619

Try changing your template for navigation directive declaring only the template for the li tag.

You can even declare a new directive specifically for that, and then create a recursive template like this:

menu-item.directive.html

<div ng-if="!$ctrl.item.SubNav"
     ng-click="$ctrl.collapseOnClick()">
  <a href="{{ $ctrl.item.Href }}">
    {{ $ctrl.item.Name }}
  </a>
</div>

<div ng-if="$ctrl.item.SubNav"
     class="btn-group"
     uib-dropdown
     is-open="status.isopen"
     ng-mouseover="status.isopen = true"
     ng-mouseleave="status.isopen = false">

  <a href="{{ $ctrl.item.Href }}"
     ng-disabled="disabled">
    <i class="fa fa-{{ $ctrl.item.icon }}"></i> {{ $ctrl.item.Name }} <i class="fa fa-caret-down"></i>
  </a>

  <ul class="dropdown-menu" uib-dropdown-menu role="menu">
    <menu-item ng-repeat="submenuItem in $ctrl.item.SubNav" item="submenuItem"></menu-item>
  </ul>
</div>

Upvotes: 1

Related Questions