I am following Sail.js tutorial from http://irlnathan.github.io/sailscasts/blog/2013/08/27/building-a-sails-application-ep4-handling-validation-errors-with-a-flash-message/
However I ran into a small problem. In the tutorial the author uses registration files inside his user folder and assigns routes in the user controller. He then sends validation errors using flash to the user.
However in my project, the registration files lies in the root folder and I assign the routes from the routes.js file like so
module.exports.routes = {
'/': {
view: 'index'
},
'/register': {
view: 'register'
}
};
Now the problem is to using flash to show users the validation errors while registration. I have used the following in the user controller (create) and it doesn't seem to work
if (err){
err.success = 0;
console.log(err);
req.session.flash = {
err: err
}
req.flash('error', req.session.flash);
return res.redirect('/register');
}
Any suggestions?
Sails.js version < 0.10.x based on his other thread output here
EDIT: See the Sails documentation here for more information - basically, since you're using static routing, no policies are applied before rendering the view, hence the flash policy isn't working. I'd recommend adding a register action in your UserController and just calling res.view() from there. There's also a StackOverflow post that discusses this, if you want more information.
I do have an alternative I developed for my own project that you can try out (also requires non-static routing).
In your api/policies folder, create a policy flash.js:
// flash.js policy
module.exports = function(req, res, next) {
res.locals.messages = { success: [], error: [], warning: [] };
if(!req.session.messages) {
req.session.messages = { success: [], error: [], warning: [] };
return next();
}
res.locals.messages = _.clone(req.session.messages);
// Clear flash
req.session.messages = { success: [], error: [], warning: [] };
return next();
};
This policy allows for three different flash types: success, warning, and error. It'll create an empty dictionary for each session and clear it on page loads.
I created a service FlashService.js in api/services to more easily flash messages:
// FlashService.js
module.exports = {
success: function(req, message) {
req.session.messages['success'].push(message);
},
warning: function(req, message) {
req.session.messages['warning'].push(message);
},
error: function(req, message) {
req.session.messages['error'].push(message);
}
}
Then, in your config/policies.js config file, make sure to assign the flash policy to the controller actions that you want to use flash with:
// config/policies.js
module.exports.policies = {
'*': [true, 'flash'],
'UserController': {
'register': ['flash'],
// any future actions that want flash
},
'AnotherController': {
'someAction': ['flash', 'somePolicy'],
}
}
To flash a message in your register action, just use FlashService.success(req, message). Alternatively, you can use FlashService.error and FlashService.warning, depending on how your UI styling works and how much you want to differentiate your flash messages.
In your view, you can put something like this:
<% if (messages && messages['error'].length > 0) { %>
<div class="alert alert-danger">
<% messages['error'].forEach(function(message) { %>
<%= message %>
<br>
<% }); %>
</div>
<br>
<% } %>
<% if (messages && messages['warning'].length > 0) { %>
<div class="alert alert-warning">
<% messages['warning'].forEach(function(message) { %>
<%= message %>
<br>
<% }); %>
</div>
<br>
<% } %>
<% if (messages && messages['success'].length > 0) { %>
<div class="alert alert-success">
<% messages['success'].forEach(function(message) { %>
<%= message %>
<br>
<% }); %>
</div>
<br>
<% } %>
Of course, you'll have to change the div classes to whatever classes are relevant to your UI.
In a normal mvc like rails or cakephp handling flash is something like this: $this->Flash->error('An Error Occurred Message'); then displayed it in an element.
In sails it should be simple as that.
In Controller: req.flash('error', 'An Error Occurred Message');
In View Partial: flash.ejs
<% if (req.session.flash) { %>
<% if (req.session.flash.success) { %>
<div data-alert class="alert-box success radius">
<%= req.flash('success') %>
×
</div>
<% } %>
<% if (req.session.flash.warning) { %>
<div data-alert class="alert-box warning radius">
<%= req.flash('warning') %>
×
</div>
<% } %>
<% if (req.session.flash.error) { %>
<div data-alert class="alert-box alert round">
<%= req.flash('error') %>
×
</div>
<% } %>
<% } %>
On Layout:
<%- partial('partials/flash') %>
Handling Error Message per field should be part in a Validation and Html Helper. (Like for example: https://gist.github.com/mikermcneil/8366092)
You can add this export:
module.exports = function flash(req, res, next) {
if (!req.session.flash) {
req.session.flash = {};
req.session.flash['success'] = [];
req.session.flash['warning'] = [];
}
next();
};
and then in the view:
<% if (req.session.flash['success'].length > 0) { %>
<div class="alert alert-success">
<%= req.session.flash['success'].pop() %>
</div>
<% } %>
<% if (req.session.flash['warning'].length > 0) { %>
<div class="alert alert-warning">
<%= req.session.flash['warning'].pop() %>
</div>
<% } %>
You can create messages in the controller like this:
req.session.flash['success'].push('Updated successfully');
Related
I am trying to set validation of an array using express validator and ejs method and I keep getting the error that says
C:\Users\ADLIMITED\Desktop\favourite\node_modules\express-validator\src\chain\validators-impl.js:25
this.lastValidator.message = message;
^
TypeError: Cannot set property 'message' of undefined
at ValidatorsImpl.withMessage (C:\Users\ADLIMITED\Desktop\favourite\node_modules\express-validator\src\chain\validators-impl.js:25:36)
at module.exports (C:\Users\ADLIMITED\Desktop\favourite\routes\index.js:35:33)
this is the array that contains the validation methods:
router.post(
'/contact',
[
check('name').trim().isLength({ min: 3 }).escape().withMessage('A name is required'),
check('email').trim().isEmail().normalizeEmail().withMessage('A valid email is required'),
check('service').escape().withMessage('Select a service'),
check('budget').escape().withMessage('Choose a budget'),
check('message').trim().isLength({ min: 3 }).escape().withMessage('Leave us a message'),
],
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
req.session.feedbackContact = {
errors: errors.array(),
};
return res.redirect('/contact');
}
console.log(req.body);
return res.send('feedback form posted');
}
);
and the html page with the details to be validated alongside the error msg
<div class="container-content100">
<div class="content-wrapper100">
<% if(locals.errors) {%>
<div class="alert alert-danger">
<% errors.forEach(function(error){ %>
<li><% error.msg %></li>
<% }) %>
</div>
<% } %>
<form class="" method="POST" action="/contact">
Please help
I found my mistake where I was assigning .withMessage to option input and the ejs page was meant to have the assignment symbol in the list element otherwise the message wouldn't show up.
The right code looks like down below:
the array that contains the validation methods:
'/contact',
[
check('name').trim().isLength({ min: 3 }).escape().withMessage('A name is required'),
check('email').trim().isEmail().normalizeEmail().withMessage('A valid email is required'),
check('service').escape(),
check('budget').escape(),
check('message').trim().isLength({ min: 3 }).escape().withMessage('Leave us a message'),
],
the html page with the details to be validated alongside the error msg
<div class="container-content100">
<div class="content-wrapper100">
<% if(locals.errors) {%>
<div class="alert alert-danger">
<% errors.forEach(function(error){ %>
<li><%= error.msg %></li>
<% }) %>
</div>
<% } %>
<form class="" method="POST" action="/contact">
I had that error, when in a validation chain I tried to chain a .optional() method with a .withMessage() method.
I had the following:
body("email")
.optional()
.withMessage("Enter a valid email") // this causes the error
So I removed the .withMessage method:
body("email")
.optional()
I have a collection called events that I am currently using in my footer partial, however, I would like to show only the ones with a date greater than today. I have achieved this on mongo shell like this:
db.events.find({date: {$gte: new Date()}})
I don't understand how to use this query in footer partial where I am running a loop to show the events, I would like to save this query in a variable that I can use in the loop, and it would be visible in every page of the website as they all have the footer.
To be able to use this collection in every page I am using this code in my app.js
// passing courses to every EJS template
app.use(function (req, res, next) {
Event.find({}, function (err, allEvents) {
if (err) {
console.log(err);
res.locals.events = [];
next();
} else {
res.locals.events = allEvents;
next();
}
});
});
and this is where I am doing my loop in footer partial
<div id="event-footer" class="col-12 mt-4 mt-md-5 text-center col-md-4 text-md-left">
<div class="">
<h4 class="pb-1 pb-md-5">PROXIMOS EVENTOS</h4>
<% var count = 0; %>
<% events.forEach(function(event) { %>
<% if (count <= 2 ) { %>
<div class="eventDisplay text-center text-md-left">
<h6><%=moment(event.date).format('dddd, D MMMM, h:mm a')%></h6>
<p> <%= event.name %> </p>
</div>
<% } %>
<% count++; }); %>
</div>
</div>
Try this ...
Event.find({date: {$gte: new Date()}}, function (err, allEvents) { ...
I want to display another view if there is :id in Semantic URL
app.get(['/topic', '/topic/:id'], function(req, res){
id = req.params.id
if(id){
res.render('view',{topics:rows, topic:row[0]}
}else{
res.render('view', {topics:rows}
}
}
and view.ejs
for(var i = 0; i < topics.length; i++){
<li><%= topics[0].title%></li>
}
<% if(topic) {%>
<%= topic.description %>
<%} else {%>
<h1>Welcome</h1>
<% } %>
it did works correctly when i connect to localhost:3000/topic/1
but when i connect to localhost:3000/topic
console said topic is not defined
what is the problem?
When i use
res.render('views', {topics:rows, topic:''}
it did works correctly
Do i have to use like this?
In view.ejs it is best to check the if condition as follows. Not only in ejs but in entire javascript world
<% if(typeof topic !== 'undefined') {%>
<%= topic.description %>
<%} else {%>
<h1>Welcome</h1>
<% } %>
And you can use also use res.render('views', {topics:rows, topic:''}
This might sound really easy. There is a very similar
answer but somehow my code is behaving abruptly and driving me crazy. I have spent last 4-5 hours on this. Any help would be great.
I am building a Socket.IO chat app using Node + Express + Passport. I want to compare the userId of the current user with that of other users connected. I want to show him all users except himself to chat with in Socket.IO.
Read comments on code for clarity :
users.js
app.get('/users', function (req, res) {
var connectedUsers = require('../app').chatUsers; // => This is an array of all logged in user and I am getting this all correctly.
if (req.isAuthenticated()) {
res.render('users', {
currentUser: req.user,
users: connectedUsers,
title: 'OnLineUsers'
});
}
});
users.ejs
<h3>CurrentUser : <%- currentUser.id %></h3> // => This Prints like **abcdefg**
<% for(var i = 0;i < users.length ; i++){ %>
<h3>UserID: <%- users[i].id %></h3> // => This also prints the same **abcdefg** if only one user is connected.
<% if( currentUser.id !== users[i].id){ %> // => But this Conditional Operator does not works and show the current user himself.
<ul>
<li>
<h3> <%= users[i].name %></h3>
<a href="/socket" id="chatRoomUrl">
<button type="submit" class="btn-success">Chat</button>
</a>
</li>
</ul>
<% } %>
<% } %>
Thanks in advance. If there is any silly mistake on my part, please excuse me for wasting your time.
Your code is fine. Maybe there is space in two property. Try :
currentUser.id.trim() !== users[i].id.trim()
Change your following line, notice the change:
<h3> <%= users[i].name %></h3>
to this:
<h3> <%- users[i].name %></h3>
I am a newbie in NodeJS. I have a problem that I am unable to show flash message in my view.
Here is my Controller,
index : function(req, res){
res.locals.flash = _.clone(req.session.flash);
res.locals.layout = false;
res.view('login');
},
login : function(req, res){
....
if(!admin){
req.session.flash = {
err : 'User is not found.' // My flash message
}
res.locals.layout = false;
res.redirect('login');
return;
}
.....
}
Here is my view,
<% if(flash && flash.err) { %>
<div class="alert alert-danger">
<% JSON.stringify(flash.err) %>
</div>
<% } %>
When login is false, it show only an empty alert box.
And I have a second problem. When I refresh page, The alert box isn't disappeared.
Could someone help me please.
Thanks a lot.
The alert box keeps on appearing because the req.session.flash object persists the session, so you need to null that out once it's used, or you can just simply use req.flash(), which does that for you. So change your index method to something like this:
index: function(req, res) {
// req.flash() returns the contents of req.session.flash and flushes it
// so it doesn't appear on next page load. No need to clone.
res.locals.flash = req.flash();
res.locals.layout = false;
res.view('login');
},
Now, onto the second problem. The error messages aren't appearing because you aren't using the proper EJS syntax to output escaped values into the view. All you need to do is change your code to this:
<% if(flash && flash.err) { %>
<div class="alert alert-danger">
// Change <% to <%=
<%= flash.err %>
</div>
<% } %>
No need to JSON.stringify, unless you like the quotes. Notice that I changed the <% to <%=, which in EJS means "escape this and output it". It's not template HTML or anything like that, so it's okay to escape it anyway.