I'm using Express and EJS to serve pages. I'm using Bootstrap for the UI, specifically the navbar.
I'd like to add an 'active'
class to the current page's <li>
item, to show the current page. However, I cannot find how to get the URL from within the EJS code rendering the page.
I found 2 workarounds: I used included passing the page name as a parameter in the route's res.render('myview', {pageName: 'myView'});
- which is not scalable and may cause issues.
The other way, was to use jQuery on the client side to add the 'active'
class to the item upon page ready - but that means including this piece of script on every view + some useless client side cycles.
Having used several server side rendering languages before, I feel like I'm missing something. And the online EJS documentation is not that great.
Is there any way to find my current path/url from the EJS code?
Update: I took the top 2 suggestions, and passed the view name as a parameter to the view. I really liked @tandrewnichols' idea to calculate it automatically, but ultimately, it was easier to just copy-paste strings :)
As far as I know, you can't do what you're asking for unless you modify EJS internally. However, a less bothersome solution would be to pass the URL property of the request on each page invocation, rather than define it per route.
app.get('/url', function (req, res) {
res.render('view', {
page: req.url,
nav: {
'Page 1': '/page1',
'Page 2': '/page2',
'Page 3': '/page3'
}
});
});
If you only wanted to get the first part of the URL and match it, then you could just call split('/')
on req.url
. You could then put a loop inside your template file to create the list for your navigation bar.
<% nav.forEach(function(title) { %>
<% if (nav[title] == page) { %>
<li class="active">This part of the navigation bar is active.</li>
<% } else { %>
<li>This part of the navigation bar is normal.</li>
<% } %>
<% }) %>
In pretty much every node/express templating language I've used (ejs, kiwi, swig, jade), the answer is no. I've always just set a variable called "active" and then checked for it. As you say, it's not a great answer, though I don't know that scalability is the issue. If every url renders it's own view (or even if you have a common handler for view rendering), it shouldn't be that hard to say something like req.active = "Somepage"
. Another possibility would be to add middleware that does it for you based on the route. Something like
app.use(function(req, res, next){
req.active = req.path.split('/')[1] // [0] will be empty since routes start with '/'
next();
});
Then you just make sure any routes that have a corresponding nav component use unique paths, like
app.get('/students', ....)
app.get('/classes', ....)
app.get('/teachers', ....)
// etc.
EDIT: In response to your comment, I always throw ALL my view stuff into one object key inside req
, and usually I name the key by whatever templater I'm using. So I would probably actually use the above example to set req.ejs.active
and then do
res.render('myview', req.ejs);
This method makes it much easier to separate logic out into multiple middleware functions and not have to pass a huge anonymous object to res.render.
index.js
/* object menu */
const menu = [
{
name: 'Home',
url: '/'
},
{
name: 'About',
url: '/about'
}
]
/* route */
app.get( '/', function( request, response) {
let data = {
title: 'Home',
url: request.url,
menu: menu
}
response.render( 'home', data )
} )
app.get( '/about', function( request, response) {
let data = {
title: 'About',
url: request.url,
menu: menu
}
response.render( 'about', data )
} )
menu.js
<% for ( let i in menu ) { %> // loop menu
<% if ( menu[i].url == url ) { %> // match, add active in class
<a class="active" href="<%= menu[i].url %>" ><%= menu[i].name %></a>
<% } else { %>
<a class="" href="<%= menu[i].url %>" ><%= menu[i].name %></a>
<% } %>
<% } %>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With