Node - Render variable into partial layout ejs-locals - node.js

I'm use passport and I need to my layout (using ejs-locals 'partial') can render my username variable.
app.use(function(req, res, next) {
res.locals =
{
what : 'MyProject',
who : req.user.username
}
next();
});
I realize 'req.user.username' only available if 'isAuthenticated' returning true. The following code works, but Im sure there is a handy way to this instead of writing variable for every routes.
app.get('/dashboard',isAuthenticated, function(req, res) {
res.render('template/index', {who:req.user.username});
});
Then, here is my layout:
<div id="wrapper">
<!-- Navigation -->
<nav style="margin-bottom: 0">
<%- partial('header.ejs') %>
<%- partial('left.ejs') %>
</nav>
<%- body %>
</div>
and last my header.ejs
<span style='padding-left:18px'>Hi, <%=who%></span>

Try using <% include header.ejs %> instead.
That works in ejs, not sure if ejs-locals is the same. In header.ejs you should have access to who. Worth trying.

Related

Express nodejs Handlebars not rendering database content correctly inside EJS file

Im having a problem with my express nodejs project where the handlebars wont render on my ejs file. I created some handlebars which fetch some data from a database. When I render the hbs files the content from the database is displayed correctly, but when I include the hbs files in my ejs file together with my page header and footer the content from the database is not retrieved. I would appreciate any help.
cars.hbs
<!DOCTYPE html>
<html>
<body>
<p> The {{dbcars.model}} features a {{dbcars.specs.engine}} engine... </p>
</body>
</html>
home.ejs
<body>
<p> <%- include('navbar.ejs') %> </p>
<p> <%- include('handlebars/cars.hbs') %> </p>
<p> <%- include('footer.ejs') %> </p>
</body>
</html>
(note: when I render (handlebars/cars.hbs) instead of (home.ejs) the handlebars render correctly. )
car.js
router.get('/vehicle/:id', async (req, res) => {
Car.findById(req.params.id, function(err, carRouter) {
res.render("home.ejs", {
dbcar: carRouter
})
This is the result im getting:
(navbar)
The {{dbcars.model}} features a {{dbcars.specs.engine}} engine...
(footer)
This is the result im trying to get:
(navbar)
The Nissan Sentra features a 2.0 L 4-cylinder engine...
(footer)
You should create cars.ejs as well
cars.ejs
<% if (dbcar.length > 0) { %>
<% dbcar.forEach(dbcars => { %>
<p> The <%= dbcars.model %> features a <%= dbcars.specs.engine %> engine... </p>
<% }) %>
<% } else { %>
<p>There is no information to display.</p>
<% } %>
home.ejs
<%- include('handlebars/cars.ejs'); %>
car.js
router.get('/vehicle/:id', async (req, res) => {
Car.findById(req.params.id, function(err, carRouter) {
res.render("cars.ejs", {
dbcar: carRouter
})
};
};

Unable to send user data to views

This is my general format for views:
<html>
<head></head>
<body>
<%- include('_header'); %>
<main>
(html code for a particular page)
</main>
<%- include('_footer'); %>
</body>
</html>
This is my code for _header.ejs:
<header>
<a id="logos" href="/">
<img id="dr" src="/imgs/logo.png" alt="logo">
</a>
<div id="nav_bar">
Achievements
Sponsors
Team
Gallery
<% if(profile_user.id) { %>
Profile
<% } else { %>
Log In
<% } %>
</div>
</header>
I have routes folder which gets controllers from different controllers. I am getting an error _header.ejs that profile_user.id is not defined.
11| Gallery
>> 12| <% if(profile_user) { %>
13| <%= profile_user.user %>
14| <% } else { %>
15| Log In
profile_user is not defined
Now the solution to this problem would be to use User.findOne() but then I don't want to do this for all controllers. I want to globalize profile_user into all views. Can you tell me how to do that?
Also, I have tried to do this by adding middleware and sending using it in my app.js but it never gets called.
console.log(10);
if (await req.isAuthenticated()) {
// req.user contains the current signed in user from the session cookie and we are just sending this to the locals for the views
res.locals.user = req.user;
console.log('Locals\' user loaded');
}
next();
}
In app.js I have called the function
app.use(passport.setAuthenticatedUser);
and updated the _header.ejs code to this:
<%= user.user %>
<% } else { %>
Log In
<% } %>
Solution to any of the two problems (through middleware or through controllers) will do.
Let me know if you need anything else

I am facing the some error in my ejs file can someone please assist me?

The error I am facing is as follows:
I have the following code written in server.js file. What am I doing wrong?
var express = require('express');
var app = express();
var instagram = require('instagram-node').instagram();
const ejsLint = require('ejs-lint');
app.use(express.static(__dirname + '/public'));
app.set('view engine', 'ejs');
instagram.use({
client_id:'1063599834134140',
client_secret:'66cbe60a931310beb7a8cf6d4ec7a12f '
});
app.get('/', function(req, res){
//
instagram.media_popular(function(err, medias, remaining, limit)
{
res.render('pages/index', {grams: medias});
});
});
app.listen(8080, function(err)
{
if(err)
{
console.log("Error Occured");
}
else
{
console.log("listening on port 8080");
}
});
And the following is index.ejs
<!DOCTYPE html>
<html>
<head>
<% include ../partials/head %>
<title></title>
</head>
<body class="container">
<header>
<% include ../partials/header %>
</header>
<main>
<div class="row">
<% grams.forEach(function(true){ %>
<div class="instagram-pic col-sm-3">
<a href="<%= gram.link %>" target="_blank">
<img src="<%= gram.images.standard_resolution.url %>" class="img-responsive">
</a>
</div>
<div class="instagram-bar">
<div class="likes">
<span class="glyphicon glyphicon-heart"></span>
<%= gram.likes.count %>
</div>
</div>
<div class="comments">
<span class="glyphicon glyphicon-comment"></span>
<%= gram.comments.count %>
</div>
<% }) %>
</div>
</main>
<footer>
<% include ../partials/footer %>
</footer>
</body>
</html>
Looks to me like your includes are wrong. Using the ejs-lint package that your error message references gives the following:
Unexpected token (4:17) in asdf.ejs
<% include ../partials/head %>
In looking at some ejs docs, the syntax they give for include is:
Includes are relative to the template with the include call. (This requires the 'filename' option.) For example if you have "./views/users.ejs" and "./views/user/show.ejs" you would use <%- include('user/show'); %>.
You'll likely want to use the raw output tag (<%-) with your include to avoid double-escaping the HTML output.
So in your case that would be
<%- include('../partials/head') %>
And of course, same for your other includes.
Also for future reference, it would be way better to copy/paste the error text into a code block in your question, instead of linking a screen shot.

get data in a ejs include with express-partials

I want to pass data to a include in ejs layout. I'm using express and express-partials
In the controller.js I have this:
exports.index = function(req, res, next) {
res.render('index', {uptime:'3'});
};
this is layout.ejs:
<!DOCTYPE html>
<html>
<head>
<%- include head %>
</head>
<header>
<%- include nav %>
</header>
<body>
<section>
<%- body %>
</section>
</body>
<footer>
</footer>
</html>
this is nav.ejs:
<li><%= uptime %></li>
and i get this error:
ReferenceError: /root/apps/webpanel/webpanel/views/nav.ejs:23
21| </li>
22| </ul>
>> 23| <li><%= uptime %></li>
24|
25|
26|
uptime is not defined
if I put the uptime in index.ejs the error disappears, but I want to get different views for nav and body...
What is the best solution to work correctly? or what i'm doing wrong?:/
Thank you!!
Maybe in this code : res.render('index', { uptime : '3'});
you choose to render the index.ejs
but the uptime key in layout.ejs
you said res.render('layout', { uptime: '3'}); does well
You should understand the first param of .render() meaning and ensure which template you need to render.
I suggest you to put in session, for access in some route, somelike:
exports.index = function(req, res, next) {
req.session.uptime = 3;
res.render('index');
};
And:
<li><%= session.uptime %></li>

How do i pass a user object to a partials in ejs?

I'm trying to render a user's name on a navbar, but that navbar is in a partial folder and none of the routes in node.js renders navbar.ejs because it is just a partial. I'm using ejs-mate library for having a reusable layout component to be used for other's html components so that I don't need to copy and paste codes (Following the DRY principle), why? because ejs doesn't have a built in reusable layout component's feature, and other libraries are not maintained by other developers.
Currently
I have two routes
app.get('/', passport.isAuthenticated, function(req, res) {
res.render('home' {
user: req.user
});
});
app.get('/anotherRoute', function(req, res) {
res.render('test');
});
I have two ejs files
home.ejs
<%- layout('boilerplate') %>
<h1>Home Page</h1>
another.ejs
<%- layout('boilerplate') %>
<h1>Another Page</h1>
Those two ejs files are using boilerplate.ejs for the layout, so I don't to copy and paste the same codes over and over again.
boilerplate.ejs - In this file I have include navbar and some other partials.
<!DOCTYPE html>
<html lang="en">
<head>
<% include ./partials/navbar %>
</head>
<body>
<header>
<% include ./partials/header %>
</header>
<div class="container">
<main>
<%- body -%>
</main>
<footer>
<% include ./partials/footer %>
</footer>
</div>
</body>
</html>
This is the navbar file, and take a look at the navbar-right class,
if user is logged in render name
else render about and signup
navbar.ejs
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="/">
StartupClone
</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li>About</li>
<% if (!user) { %>
<li>Signup</li>
<li>Login</li>
</ul>
<% } else{ %>
<li><a><%= user.profile.name %></a></li>
<% } %>
</div>
</nav>
The problem right now it is only working with a route that i pass a user's object to but not other routes. I know that I have to pass the user's object to every single route but it is not good because again I have to copy and paste the codes again in every single route which is against the DRY principle,
Imagine if have 40 routes that need to use that navbar, it would be a nightmare for me :(
If you guys have any idea on how to hack this, feel free to lend your thoughts on how to tackle this problem.
It is compulsory to pass the object you are trying to access on the view.
One thing you could do is store user object in session and in view check session for user object.
That way you will have to pass just the session object atleast I use this trick for this problem.
In your middleware passport.isAuthenticated on succesful login save the user object in session
module.exports.isAuthenticated = function(req, res, next) {
/*
check for valid credentials
*/
if (success)
{
req.session.user = foundUserInfo;
}
next();
}
and in your partial view
<!-- navbar.ejs -->
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="/">
StartupClone
</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li>About</li>
<% if (!session.user) { %>
<li>Signup</li>
<li>Login</li>
</ul>
<% } else{ %>
<li><a><%= session.user.profile.name %></a></li>
<% } %>
</div>
Now you just have to pass session object for each route.
app.get('/', passport.isAuthenticated, function(req, res) {
res.render('home' {
user: req.user,
session: req.session
});
});
app.get('/anotherRoute', function(req, res) {
res.render('test', {session: null});
});
You have to pass an object that has the used properties for each res.render() call, there's no way around it. It's even in the usage example in ejs-mate's documentation.
Having said that, there is a way to keep your code DRY. Use a function that sets shared properties on an input object, then for each route set the unique properties it requires:
function setSharedProperties(req, data) {
if(!(data instanceof Object)) {
data = {};
}
data.user = req.user;
return data;
}
app.get('/', passport.isAuthenticated, function(req, res) {
res.render('home', setSharedProperties(req))
});
app.get('/anotherRoute', function(req, res) {
res.render('test', setSharedProperties(req, {prop1: val1}));
});

Resources