0

Right now, I am doing this via jQuery, like so:

$(".navigation__item a").each(function () {
  if (window.location.pathname === $(this).attr("href")) {
    $(this).parent().addClass("active");
  }
});

However, this isn't full-proof so I'd like to pass along an "active" parameter to my navigation view, but I'm unsure of how to do that. Here's a snippet of my navigation template:

<aside class="navigation-wrap">
  <ul class="navigation">
    <li class="navigation__item{{#if current}} active{{/if}}">
      <a href="/admin/dashboard">Overview</a>
    </li>

    <li class="navigation__item{{#if current}} active{{/if}}">
      <a href="/admin/customization">Customization</a>
    </li>

    <li class="navigation__item{{#if current}} active{{/if}}">
      <a href="/admin/employees">Employee</a>
    </li>
  </ul>
</aside>

And here is a snippet of my routes file:

app.get("/admin/:directory/:page", require("connect-ensure-login").ensureLoggedIn("/admin"), (req, res) => {
  if (req.params.page === "new") {
    res.render("admin/" + req.params.directory + "/new", Object.assign({ layout: "layouts/admin", navHighlight: "active" }, app.get("result")[0]));
  } else {
    employeeService.find({ query: { Slug: req.params.page }}, (error, employee) => {
      employee = employee[0];

      res.render("admin/employees/employee", Object.assign({ employeeRequest: req.params.page, Title: "Admin | " + employee.Name, layout: "layouts/admin" }, app.get("result")[0]));
    });
  }
});

Has someone accomplished something like this yet? I've searched SO and elsewhere and haven't found anything.

NetOperator Wibby
  • 1,354
  • 5
  • 22
  • 44

2 Answers2

0

There are definitely different possible approaches. One thing which comes spontaneously to my mind is, that you could save your navigation in a variable like:

var navi = [
  {url: "/admin/dashboard", name: "Overview"},
  {url: "/admin/customization", name: "Customization"},
  {url: "/admin/employees", name: "Employee"}
];

So we have an object array and can loop through to write the <li> and <a> tags dynamically. With a condition it should then also be not a big deal to set a link active depending on the request URL.

With a minimum setup I was able to output depending on a hardcoded request URL:

<ul>
    <li class=''><a href='/admin/dashboard'>Overview</a></li>
    <li class=''><a href='/admin/customization'>Customization</a></li>
    <li class='active'><a href='/admin/employees'>Employee</a></li>
</ul>

Following now the setup.

index.js:

var express = require('express');
var app = express();
var hbs = require('hbs');

app.set('view engine', 'hbs');

hbs.registerHelper('list', function(naviItems, reqUrlItem, options) {
  var out = "";
  var cssClass = "";

  for(var i=0, l=naviItems.length; i<l; i++) {
    if (reqUrlItem === naviItems[i].url) {
        cssClass = "active";
    }
    out += "<li class='"+cssClass+"'><a href='"+naviItems[i].url+"'>" + naviItems[i].name + "</a></li>";
    cssClass = "";
  }

  return out;
});

var navi = [
  {url: "/admin/dashboard", name: "Overview"},
  {url: "/admin/customization", name: "Customization"},
  {url: "/admin/employees", name: "Employee"}
];
var reqUrl = "/admin/employees";

app.get('/', function(req, res) {
   res.render('test', {navi: navi, reqUrl: reqUrl});
});

app.listen(3000);

test.hbs

<ul>
     {{#list navi reqUrl}}{{/list}}
</ul>

So basically you register a Handlebars helper function called list which you call with your navigation variable and your request parameter (both have been passed to the template before). The helper function should be self-explanatory. Hope it helps!

Michael Troger
  • 3,336
  • 2
  • 25
  • 41
  • Yup, I'm using the hbs module! Is your variable being loaded in the backend? – NetOperator Wibby Sep 20 '16 at 21:20
  • I updated my answer! Yes it's all meant to be on the backend. – Michael Troger Sep 20 '16 at 22:01
  • For some reason, I am unable to get this code to work. Creating my navigation dynamically works, but applying the `active` class doesn't. – NetOperator Wibby Sep 21 '16 at 02:51
  • if it's not a syntax problem it's most likely because your request URL doesn't equal a URL in the list. Just debug by placing console.log(naviItems); console.log('request url: '+reqUrlItem); into your HBS helper function. It will be printed out in the server-side console. – Michael Troger Sep 21 '16 at 06:57
  • I'll try debugging to see what comes up. How would I handle sub-pages though? Like "/admin/employees/new"? – NetOperator Wibby Sep 21 '16 at 17:19
  • You mean you would like to mark the main menu point also if you're on sub-pages? You could change the condition in the helper function. Instead of if (reqUrlItem === naviItems[i].url) you could write if (reqUrlItem.includes(naviItems[i].url)) . This would probably only work with a max. 2-level navigation I guess. http://www.w3schools.com/jsref/jsref_includes.asp – Michael Troger Sep 21 '16 at 17:34
  • Thanks for your help, I ended up figuring out a solution based on your input a few weeks ago but forgot to update here. – NetOperator Wibby Oct 05 '16 at 20:03
0

With Micheal Troger's help, I figured out a solution.

src/routes/index.js

app.get("/admin/*", require("connect-ensure-login").ensureLoggedIn("/admin"), (req, res) => {
  const parts = req.path.split("/");

  /*
  console.log(
    req.path + "\n",
    "0: " + parts[0] + "\n",  //
    "1: " + parts[1] + "\n",  // admin
    "2: " + parts[2] + "\n",  // employees   // directory
    "3: " + parts[3]          // david-sarif // page
  );
  */

  if (parts[3]) {
    if (parts[3] === "new") {
      res.render("admin/" + parts[2] + "/new", Object.assign({ layout: "layouts/admin", navigation: app.get("navigationAdmin") }));
    } else {
      employeeService.find({ query: { Slug: parts[3] }}, (error, employee) => {
        employee = employee[0];

        res.render("admin/employees/employee", Object.assign({ employeeRequest: parts[3], Title: "Admin | " + employee.Name, layout: "layouts/admin", navigation: app.get("navigationAdmin") }));
      });

      handheldService.find({ query: { Slug: parts[3] }}, (error, handheld) => {
        handheld = handheld[0];

        res.render("admin/handhelds/handheld", Object.assign({ handheldRequest: parts[3], Title: "Admin | " + handheld.Name, layout: "layouts/admin", navigation: app.get("navigationAdmin") }));
      });
    }
  } else {
    res.render("admin/" + req.params[0], Object.assign({ user: req.user, title: "Admin", layout: "layouts/admin", navigation: app.get("navigationAdmin") }));
  }
});

src/middleware/index.js

module.exports = (app) => {
  app.use((req, res, next) => {
    // Create navigation for backend
    function createNavigation(navigation, data) {
      let
        html = "",
        extraClass = "";

      const parts = req.path.split("/");

      /*
      console.log(
        req.path + "\n",
        "0: " + parts[0] + "\n",  //
        "1: " + parts[1] + "\n",  // admin
        "2: " + parts[2] + "\n",  // employees   // directory
        "3: " + parts[3]          // david-sarif // page
      );
      */

      for (data of navigation) {
        if (parts[2] && data.url.includes(parts[2])) {
          extraClass = " active";
        }

        html += "<li class='navigation__item" + extraClass + "'><a href='" + data.url + "'>" + data.name + "</a></li>";
        extraClass = "";
      }

      app.set("navigationAdmin", html);
    }

    const navigationItems = [
      { url: "/admin/dashboard", name: "Overview" },
      { url: "/admin/customization", name: "Customization" },
      { url: "/admin/employees", name: "Employee" },
      { url: "/admin/handhelds", name: "Handheld" }
    ];

    createNavigation(navigationItems);
  });
};

This code allows for my navigation to stay highlighted whether I'm on the exact page or a subpage. For example, site.com/example and site.com/example/new will both light up "Example" in my navigation.

BOOM.

EDIT: Forgot to add where the navigation actually shows up!

<aside class="navigation-wrap">
  <ul class="navigation">
    {{{ navigation }}}
  </ul>
</aside>
NetOperator Wibby
  • 1,354
  • 5
  • 22
  • 44