I want to render a specific view in a sailsjs controller/action which should be sent out as email.
I have the following sample action:
function registerAction(req, res) {
// handle user registration
// email user
sendEmail({
to: newUser.email,
subject: "Welcome",
text: /* VIEW RENDER HERE */
});
// render view to the user
return res.view({
user: newUser
});
}
How can I render a view-template with the tools sailsjs provides so that I don't need to hardcode an email text or use other libraries?
Thanks in advance!
Express' res.render() is still accessible in your res object.
function registerAction(req, res) {
// handle user registration
res.render('/path/to/view', function (err, html) {
// email user
sendEmail({
to: newUser.email,
subject: "Welcome",
text: html
});
}
// render view to the user
return res.view({
user: newUser
});
}
To improve a little on crzrcn's answer, to ensure
function registerAction(req, res) {
// handle user registration
// don't include the .ejs of the end of your view, this assumes a file in path/to/view.ejs
res.render('path/to/view', function (err, html) {
// email user
sendEmail({
to: newUser.email,
subject: "Welcome",
html: html // rather than text in crzrcn's answer
});
}
// render view to the user
return res.view({
user: newUser
});
}
Related
I made a form using NodeJs, i made some validations of input that show errors when user enter wrong values, the problem here is that the error appear on a new blank page but i need the error to appear on the main html file itself with cool styling
here's the live site http://mido.sundays.org.uk
I tried to make post request on the same route to see if the error will appear on the same page or not but the page turned to white blank page with the text inside
app.post('/', function (req, res) {
const SchemaValidation = {
name: joi.string().min(4).required().error(() => {
return {
message: 'Name is required. (min:4 chars)',
};
}),
email: joi.string().email().error(() => {
return {
message: 'Email field can\'t be Empty',
};
}),
phone: joi.string().min(8).max(14).required().error(() => {
return {
message: 'Valid Phone number is Required (min:8 characters - max: 14 characters)',
};
}),
university: joi.string().required().error(() => {
return {
message: 'University Field is Required',
};
}),
faculty: joi.string().required().error(() => {
return {
message: 'Faculty Field is Required',
};
}),
academicyear: joi.string().required().error(() => {
return {
message: 'Academic Year Field is Required and should range from 1-6',
};
}),
workshop: joi.array()
.items(joi.string().error(() => {
return {
message: 'You Should pickup 2 Committees',
};
})),
first_choice: joi.string().required().error(() => {
return {
message: 'You should pickup first choice',
};
}),
second_choice: joi.string().required().error(() => {
return {
message: 'You should pickup second choice',
};
}),
};
joi.validate(req.body,SchemaValidation,(err, result) => {
if(err) {
res.send(`<p style='color:red; text-align:center; margin-top:20px;'>${err.details[0].message}</p>`);
return; // don't try saving to db if the schema isnt valid
}
else
res.send(`<p style='color:green; text-align:center; margin-top:20px;'>Successfully Posted Data</p>`);
})
});
All what i need is to show the error in the same page and prevent the submit..
To solve this problem, I highly racommand you to use Pug.js (Template Engine), because you can't pass parameters into a specific page. By using a template engine, you can pass data as object, and render them with res.render('index', {error: res.error.message}).
You will be able to output into the page from server side the error object displaying whatever you needed to display!
res.send('content') basically it's a document.write()
https://pugjs.org/api/getting-started.html
Is there a reason you can't do the validation on the front end before submitting the form? That is usually the preferred way. You can have some backend validation so that you don't get bad data into your db but once you send a request you need to send a response back and in your case the response is the message not the entire HTML page. You can create a validation by adding an event listener to your submit button then using Ajax once you validate to send the data to the backend or you can use Bootstrap's built in validation and not mess with Ajax just through the form action. https://getbootstrap.com/docs/4.0/components/forms/#validation
I have a collections for Errors that displays to the user. I want to insert into this collection whenever a user receives an error, so it can be displayed in a template.
I have a few hooks on my collections that will reject it.
// only admins can create and update plans
Plans.allow({
insert: function(userId, doc) {
return Roles.userIsInRoles(userId, 'admin');
},
update: function(userId, doc) {
return Roles.userIsInRoles(userId, 'admin');
}
});
// Can only have one active plan currently
Plans.deny({
update: function(userId, doc) {
var now = new Date();
Plans.find({
active: true,
_id: { $in: doc.planIds },
dateStart: { $gt: now },
dateEnd: { $lt: now }
}).count() > 0;
}
});
My question is; can I listen to these events and, when rejected, take a particular action on the client and server?
You can insert on the collection via the callback function on whatever insert/update/remove you have.
If you want to do to on the server way (sing Meteor.methdos/Meteor.call), this is the workflow.
JS
//server
Meteor.method({
insertDoc:function(doc){
Plans.insert(doc)
}
})
//Client
Errors = new Mongo.Collection(null) //client side only
Meteor.call('insertDoc',{test:doc},function(err,result){
if(err){
Error.insert({error:err.reason}) //if there is a error lets insert it
}
})
//and the helper to show the error.
Template.example.helpers({
showError:function(){
return Error.find();
}
})
HTML
<template name="example">
<span>Sorry there was an error: {{error}}</span>
</template>
You got the idea.
i need to provide something like an association in my Model. So I have a Model called Posts with an userid and want to get the username from this username and display it.
So my ForumPosts.js Model looks like the following:
module.exports = {
schema: true,
attributes: {
content: {
type: 'text',
required: true
},
forumTopicId: {
type: 'text',
required: true
},
userId: {
type: 'integer',
required: true
},
getUsername: function(){
User.findOne(this.userId, function foundUser(err, user) {
var username = user.username;
});
console.log(username);
return username;
}
}
};
I know that this return will not work because it is asynchronus... But how can i display it in my view? At the Moment i retrive the value with:
<%= forumPost.getUsername() %>
And for sure get an undefined return...
So the question is: How can I return this value - or is there a better solution than an instanced Model?
Thanks in advance!
Off the top of my head, you can just load associated user asynchronously before rendering:
loadUser: function(done){
var that = this;
User.findOne(this.userId, function foundUser(err, user) {
if ((err)||(!user))
return done(err);
that.user = user;
done(null);
});
}
then in your controller action:
module.exports = {
index: function(req, res) {
// Something yours…
forumPost.loadUser(function(err) {
if (err)
return res.send(err, 500);
return res.view({forumPost: forumPost});
});
}
}
and in your view:
<%= forumPost.user.username %>
This is kind of a quick and dirty way. For a more solid and long-term solution (which is still in development so far) you can check out the alpha of Sails v0.10.0 with the Associations API.
So this particularly case of associations between your models. So here you have a User model and ForumPost model and you need the user object in place of your user_id as user_id works as a relationship mapping field to your User model.
So if your are using sails V0.9.8 or below you need to handle this logic in your controller where ever you want to access User model attributes in your view.
In your controller write your logic as:
model.export = {
//your getForumPosts method
getForumPosts : function(req,res){
var filters = {};
forumPost.find(filters).done(function(err,posts){
if(err) return res.send(500,err);
// Considering only one post obj
posts = posts[0];
postByUser(posts.user_id,function(obj){
if(obj.status)
{
posts.user = obj.msg;
delete posts.user_id;
res.view({post:posts});
}
else
{
res.send(500,obj.msg);
}
});
}
}
}
function postByUser(user_id,cb){
User.findOne(user_id).done(function(err,user){
if(err) return cb({status:false, msg:err});
if(user){
cb({status:true, msg:user});
}
}
}
and then you can access your post object in your view.
Or else you can keep watch (at GitHub) on next version of sails as they have announced associations in V0.10 n it is in beta testing phase as if now.
I have a REST API that read/save data from a MongoDB database.
The application I use retrieves a form and create an object (a job) from it, then save it to the DB. After the form, I have a button which click event triggers the saving function of my controller, then redirects to another url.
Once I click on the button, I am said that the job has well been added to the DB but the application is jammed and the redirection is never called. However, if I reload my application, I can see that the new "job" has well been added to the DB. What's wrong with this ??? Thanks !
Here is my code:
Sample html(jade) code:
button.btn.btn-large.btn-primary(type='submit', ng:click="save()") Create
Controller of the angular module:
function myJobOfferListCtrl($scope, $location, myJobs) {
$scope.save = function() {
var newJob = new myJobs($scope.job);
newJob.$save(function(err) {
if(err)
console.log('Impossible to create new job');
else {
console.log('Ready to redirect');
$location.path('/offers');
}
});
};
}
Configuration of the angular module:
var myApp = angular.module('appProfile', ['ngResource']);
myApp.factory('myJobs',['$resource', function($resource) {
return $resource('/api/allMyPostedJobs',
{},
{
save: {
method: 'POST'
}
});
}]);
The routing in my nodejs application :
app.post('/job', pass.ensureAuthenticated, jobOffers_routes.create);
And finally the controller of my REST API:
exports.create = function(req, res) {
var user = req.user;
var job = new Job({ user: user,
title: req.body.title,
description: req.body.description,
salary: req.body.salary,
dueDate: new Date(req.body.dueDate),
category: req.body.category});
job.save(function(err) {
if(err) {
console.log(err);
res.redirect('/home');
}
else {
console.log('New job for user: ' + user.username + " has been posted."); //<--- Message displayed in the log
//res.redirect('/offers'); //<---- triggered but never render
res.send(JSON.stringify(job));
}
});
};
I finally found the solution ! The issue was somewhere 18inches behind the screen....
I modified the angular application controller like this :
$scope.save = function() {
var newJob = new myJobs($scope.job);
newJob.$save(function(job) {
if(!job) {
$log.log('Impossible to create new job');
}
else {
$window.location.href = '/offers';
}
});
};
The trick is that my REST api returned the created job as a json object, and I was dealing with it like it were an error ! So, each time I created a job object, I was returned a json object, and as it was non null, the log message was triggered and I was never redirected.
Furthermore, I now use the $window.location.href property to fully reload the page.
I have a partial view containing my login form. I would like to render it from an ajax call to my controller.
This is the sample where i would return my partial view:
postlogin: function (req,res) {
var username = req.param('username');
var password = req.param('password');
User.find({
username: username,
password: password.salt()
}).done(function(err, users){
if(users.length == 1){
// Here I want to return a partial view, not a view
res.view('home/login', {message: 'Login success!'});
}else{
// Here I want to return a partial view, not a view
res.view('home/login', {message: 'Login failed!'});
}
});
},
Ah! Found it!
If your view is a partial view, simply specify layout: null:
res.view('home/login', {message: 'Login failed!', layout: null});
Bottom of the page: http://sailsjs.org/#!documentation/views
What about using multiple layouts?
Express 3 removed native support for layouts. In Sails, we've managed to keep this around, but we don't officially support multiple layouts.
That said, at least in EJS, instead of indicating your custom layout with the layout local, you must use _layoutFile:
/**
* HomeController
*/
module.exports = {
index: function (req, res) {
res.view({
_layoutFile: '../layouts/other.ejs'
});
},
};
Sails v0.9.7