Render app views per user agent in EJS - node.js

I'm working on a Node.js/Express that uses EJS and trying to render certain views on desktop or mobile.
I've added the following middleware to return the user agent in my app.js:
app.use(function(req, res, next) {
res.locals.ua = req.get('User-Agent');
next();
});
Next, if I echo <%= ua %> in my view, I successfully get the user agent, but I'm not sure how to actually check specific agents.
I tried <$ if(ua.match(/iPhone)) { %> as a quick example, but got a syntax error. Any help would be appreciated!

Several errors:
1) You started with a $ inside your embedded script. You should instead use a %.
2) You need to close out your regex with a /.
Something like this:
<% if(ua.match(/Intel/)) { %>
<%= ua %>
<% } %>

Related

Why does the wrong EJS tag cause this specific error: SyntaxError: Unexpected token ')'

Goal: I am trying to understand an error which occurred while trying to render a list of "secrets" (strings passed from JSON objects) in a simple Node app serving EJS files.
Code
app.js
const express = require('express');
const ejs = require('ejs');
const app = express();
app.set('view engine', 'ejs');
app.get("/secrets", (req, res) => {
const user1 = { "username": "jon", "secret": "blue blue blue"};
const user2 = { "username": "paul", "secret": "red red red"};
const users = [user1, user2];
res.render("secrets", {usersWithSecrets: users});
});
app.listen(3000, function() {
console.log("Server started on port 3000.");
});
secrets.ejs
<body>
<html>
<h1>Secrets List:</h1>
<%= usersWithSecrets.forEach((user) => { %>
<p class="secret-text"><%=user.secret%></p>
<%})%>
<hr>
</body>
</html>
Error: SyntaxError: Unexpected token ')' in C:\Users\Owner\Desktop\Sams\scode\Learning\Authentication_and_Redux\code\starting_code\Secrets-Starting_Code\views\secrets.ejs while compiling ejs
I was able to fix the error by changing the 5th line of secrets.ejs to
<% usersWithSecrets.forEach((user) => { %>
but I don't understand why I got the error that I did. I spent a very long time reviewing my code looking for a missing open parenthesis "(" before I even considered that the EJS tags were wrong. Admittedly I am a bit new to EJS, but this error was still very misleading.
Can someone please help me understand why this error was presented instead of an error stating that the EJS tags were wrong?
This happens because to EJS <%= usersWithSecrets.forEach((user) => { %> simply means to insert usersWithSecrets.forEach((user) => { in the <body> element. This is perfectly valid. EJS doesn't assume what you are wanting to do. In other words, that statement is no different than <%= hello world! %> and EJS will happily put that as the text in the <body> tag.
However, when you use the <% %> tag in <% }) %>, it tells EJS that everything here is a script and should be interpreted as javascript. Since there is only a closing bracket and parentheses and no valid javascript earlier to open these, there is now a syntax error. Changing the earlier from an output tag (<%= %>) to a script tag (<% %>) made it a valid javascript script with no error.
In short, just like the javascript interpreter can't assume your intentions, neither can the EJS parser. It couldn't possibly know that you were meaning to start a javascript script and not output usersWithSecrets.forEach((user) => { in the <body> tag. For more information on EJS see the documentation

How to res.render/send a variable after a redirect?

I want to send a message variable that says something like succeeded or failed wihtout the use of passport or sessions.
When it fails I can just do this:
res.status(401).render('register', {
message: 'Your username/email or password is incorrect.'
})
So I was thinking this was possible:
res.redirect('/');
res.render('', { message: 'Login success.'});
But it turns out it isn't:
Error: Cannot set headers after they are sent to the client
Are there any simple sollutions?
I tried making a router.get('/message/redirect :route') but I realised I ran into the same problem there too.
You are redirecting before rendering. So the render method can't send headers because you have already redirected them. You need to call either redirect or render, not both at once. you can do it like this.
res.render('/', { message: 'Login success.'});
remove this line
res.redirect('/');
In your EJS file just call <%= message %> to access the message. There is a better way of dealing with confirmation/error messages. Use Flash.
npm install express-flash
Then before rendering, call the flash. Flash takes two arguments, a name of the message and the message itself, Like this
req.flash("MessageName", "Login success");
res.redirect(pathName);
Make sure to use flash
const flash = require('express-flash');
app = express();
app.use(flash());
app.use((req, res, next) => {
res.locals.MessageName= req.flash("MessageName");
next();
});
To access the message in EJS file just call <%= MessageName %>
Here is sample from my code:
In the server file:
req.flash("error", "Something went wrong");
res.redirect('back');
In the EJS file:
<div class="flash">
<% if(error && error.length > 0){ %>
<div class="flash_error">
<h1 class="flash_error--text"><%= error %></h1>
</div>
<% } %>
</div>
Read this for more information

Passing req.user to template not working with ejs and express

I am using EJS and Express to take and check if the current user exists and if not show one thing instead of the other. My issue is that I am setting req.user to res.locals.currentUser but it is not passing through to the template so I can output it as well as check if it exists.
in my app.js
app.use(function(req, res, next){
res.locals.currentUser = req.user;
next();
});
my ejs file
<% if(!currentUser){ %>
<li>Login</li>
<li>Sign Up</li>
<% } else { %>
<li>Signed In As <%= currentUser.email %></li>
<li>Profile</li>
<li>Logout</li>
<% } %>
This evaluates always to false therefore showing the first section of the if statement.
What is wrong here to make it so I can't test or see the user data in my ejs view? When I console.log the currentUser is returns undefined.
Some information that might be important, I am using a redis session store instead of mongo store. I also am using body-parser not cookieparser.
I was facing the same issue while using Passport in node.js with ejs templating.
The solution is to place it below the passport lines of code( that is serialize and deserialize).
These functions are responsible for saving the user to "req.user", and thus if the app.use is placed above it, the code won't work as req.user is null until this.
Adios

EJS include: Render data from controller?

Inside my index.ejs (standard html file):
<% include taskList %>
My taskList.ejs tries to access a variable (tasks) which is delivered from the controller:
exports.taskList = function(req, res) {
res.render('taskList', {
tasks: tasks
});
}
This doesn't seem to be possible when including the taskList as the include doesn't call the controller -> tasks is undefined.
Any solution to this?

How do you test req.flash() in express?

I currently have an express/node application I want to test, and but responses that it gives are in the form of req.flash('warn', 'message goes here');
Unfortunately, the documentation on express.js does not describe how this message travels to the client very thoroughly.
I know expresso has an assert.response() function that tests response objects. I was wondering where the flash message goes, and how I can test it in a similar way (or if it's not possible, and I should be sending everything through the response object).
i don't think this is supposed to work like that. you can't just use req.flash()as your only way to respond to a request.
it's more like an easy way to flash messages to the user on your normal templates - e.g. after inserting/creating an article you can either say:
req.flash('error', 'could not insert because .... ');
or
req.flash('info', 'article added successfully!');
for my last project i then added two dynamic helpers to my app:
app.dynamicHelpers({
info: function (req, res) {
return req.flash('info');
},
error: function (req, res) {
return req.flash('error');
}
});
so that i can just say sth like this in my view:
<% if (info !== undefined && info != "") { %>
<div class="infoBubble">
<%= info %>
</div>
<% } %>
<% if (error !== undefined && error != "") { %>
<div class="errorBubble">
<strong>Fehler</strong>: <%= error %>
</div>
<% } %>
result looks like this:

Resources