Unable to call views - node.js

I'm using Sails and when I attempt to call the login action of my UsersController the view response is not getting loaded. I know my routing is working, because the console.log successfully logs both the loginpassword and loginname. However, the res.view() doesn't work. Neither does returning res.view().
module.exports = {
create: function(req, res) { },
destroy: function(req, res) {
},
login: function(req, res) { var loginname = req.param("loginname");
var loginpassword = req.param("loginpassword");
console.log(loginname + ' ' + loginpassword);
res.view();
},
logout: function(req, res) {
},
_config: {}
};
I have a /views/user/login.ejs and all it currently contains is a header block with some test text, but I can't get that to render at all. Any thoughts?
Edit:
I've noticed that when I look at my Chrome developer tools and I view the Response portion of the Network tab, I've actually getting my view loaded back into the layout <%-body%> but the page itself is not loading the returned view. Is this a bug or am I doing something stupid? I'm on the latest stable.
Here's the code: https://github.com/FreefallGeek/SailtsTest

The problem seems to be that you are using a jquery.post which is a ajax request and you simply don't render the response.
http://api.jquery.com/jquery.post/
You might want to use a form and optionaly submit the form by js/jquery.
Alternatively you need to render just partial html in the success function of jquery.post

Related

Mongoose redirect not waiting for findByIDAndDelete

I have created a to-do list app using Node, Express and Mongoose:
To delete a task, the user hits the cross button on the right hand side. This sends a POST request with the task ID to the /delete_task endpoint. The router for this endpoint is /routes/delete_task.js:
var express = require('express');
const Task = require('../models/task');
var router = express.Router();
express.json();
router.post('/', async (req, res, next) => {
const deleted_task = await Task.findByIdAndDelete(req.body.taskID);
console.log('Deleted task: \n', deleted_task);
res.redirect('..');
}
);
module.exports = router;
The router performs a findByIdAndDelete, and then redirects to the home directory. The router for the home directory renders a view of all the existing tasks in the collection, and looks like:
var express = require('express');
const Task = require('../models/task');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
Task.find({}, function (err, result) {
if (err) {console.error(err)};
if (result) {
return res.render('index', {title: 'To-do list', tasks: result})
};
});
});
module.exports = router;
My problem is that when deleting a task, the findByIdAndDelete successfully deletes the task, but this is not reflected in the redirected home page. The deleted task only disappears once I refresh the page. This suggests that it's some kind of async issue, and that the redirect is happening before the findByIdAndDelete query has finished executing.
To address this, I have made the router.post() callback an async function and am using await on the findByIdAndDelete, and I have also tried placing the res.redirect('..') in a callback function of the findByIdAndDelete, which also does not fix the problem:
router.post('/', (req, res, next) => {
Task.findByIdAndDelete(req.body.taskID, (err, result) => {
if (err) {
console.error(err)
};
if (result) {
console.log(result)
};
res.redirect('..');
});
});
I have looked for other questions on stackoverflow, all of which seem to suggest that this is an async issue caused by the redirect happening before the query has finished executing. The suggested solutions I have found were to make the router.post(...) callback an async function and await the result of the Mongoose query, or to place the res.redirect('..') in the callback of the findByIdAndDelete so that the redirect happens after the query has finished executing. I have tried both of these but the problem remained.
The only other thing I can think of is that I am trying to redirect from within a POST request, and I don't know if this is legit. It seems to work fine looking at the log (see last 2 lines where the GET request to / follows the POST request to /delete_task):
New task submitted: cake
New task created successfully: cake
POST /new_task 302 29.747 ms - 46
GET / 200 4.641 ms - 1701
GET /stylesheets/style.css 304 0.849 ms - -
GET /javascripts/delete_task.js 304 0.479 ms - -
Deleted task:
{
_id: new ObjectId("636a993ca0b8e1f2cc79232a"),
content: 'cake',
completed: false,
__v: 0
}
POST /delete_task 302 10.358 ms - 24
GET / 200 3.867 ms - 1348
This is where I've hit a brick wall and I can't see what might be causing the issue. Really appreciate any help or suggestions anyone might have - cheers.
I don't think this is a problem with asynchronousness, because you wait properly before responding to the POST request.
But the res.redirect makes sense only if hitting the cross button navigates from the To-do list page to the /delete_task page and from there back, by virtue of the redirection. This would be possible only with an HTML <form> element that is submitted upon hitting the button.
Is that how you have implemented it? You say that you "send a POST request", but is this through a <form>, or rather through an axios.post or a similar Javascript method? In the latter case, the following would happen:
The Javascript client sends the POST request and the deletion is carried out on the database.
The Javascript client receives a redirection response and sends the GET request.
The Javascript client receives the HTML page for the to-do list as response, but does nothing with it.
In other words: the To-do list page would not be reloaded by this axios.post request. If you want this to happen, don't respond to the POST request with a redirection, but simply with 200 OK, and have the Javascript client execute location.reload() when it receives this response.

showing a message as javascript alert after logout

app.get('/logout', function (req, res) {
delete req.session.auth;
res.status(200).redirect('/');
});
I am using this for redirecting page after logout. I need to show an alert box when logout is done.Please suggest
This depends on what packages your using. You don't state it so i'll give my two cents with the EJS approach
if you use connect-flash its as simple as :
app.get('/logout', function(req, res) {
delete req.session.auth;
req.flash('message', 'YourMessageHere')
res.redirect('/login');
});
And then when you render it check for the variable like this:
<div>
<%if(message){%>
<%= message%>
<% } %>
</div>
Or you can render the page and pass data along as a json object:
app.get('/logout', function(req, res) {
delete req.session.auth;
res.render('./link/to/html/file', {message: 'Your Message here'});
});
And again in the html check for the variable being passed in.
Look at the res object and see what its capable of and whatever frameworks your using look and see how data can be interpreted.
Maybe you can send a parameter to the redirected page, and, on that page evaluate if the parameter is present or not in order to display the corresponding alert:
app.get('/logout', function (req, res) {
delete req.session.auth;
res.status(200).redirect('/?msg=session%20destroyed');
});
in the '/' logic, you can do something like:
app.get('/', function(req, res) {
var message;
if(req.query.msg){
message = req.query.msg;
}
// other logic here...
res.send(message); // or res.render('index', { msg: message, //other properties...
});
}
You may adapt this depending on the view engine you're using.
Another option, instead of redirecting, send the logout request through ajax, in the server, after destroying the session, response with an 'ok' or '1' status. Then, evaluate the ajax response in order to display the alert in the same page and implement all the corresponding logic on the front (like clearing all forms, displayed info available only when an active session exists, etc).
Express:
app.get('/logout', function (req, res) {
delete req.session.auth;
res.status(200).send('1');
});
In the front (assuming you're using jQuery):
$.get('/logout', function(response){
if(response == '1'){
alert('Successfully logged out!');
// Destroy DOM objects that should not be still available.
}
});
It depends on what's more easy for you to acomplish, the logic in your page and your requirements.

Express router "res" object has to take me to other pages

See the example below:
var apiRouter = express.Router();
apiRouter.post('/api/postAgree', function(req, res, next){
userModel.findOneAndUpdate(
{profileID: req.session.facebookProfileId},
{$push:{postsAgreed: req.query.postID}},
{safe: true, upsert: true},
function(err, model) {
if (err){
console.log(err);
}
}
)
Now, the MongoDB operation is already done and I want to stay on the same page.
Will I be doing this:
res.render('theSamePageIamOn', {foo:bar});
I know this works but it seems like it is a very inefficient way of doing it.
So my question really is: If I have a button on a page which makes an API call but I want to stay on the same page, how will I do that? The res.(options) function sort of is made like it has to take me to other pages
Thanks to #robertklep and #GiladArtzi - it should be an AJAX call and the response should be in the form of:
res.json()
Then the response can be handled by the frontend using other tools like: Angular
I'm not sure what you're talking about, just call the function....
function doesSomething (args) {
console.log(args)
}
apiRouter.post('/api/postAgree', function(req, res, next){
doesSomething("HELLO")
});
Function calls don't expects the user to go to another page each time an API call is handled.

Change the order of a routes variable in the front end NodeJs/Express/Jade

Novice to NodeJS and Express but lets say I have this route for mywebsite.com/tournaments.
router.get('/tournaments', function (req, res) {
TournamentController.getAllTournaments(function (err, docs) {
if (err) {
//render error
} else {
res.render('tournaments', {
data: {
title: 'mysite',
command: 'tournaments',
user: req.session.user,
tournaments: docs
}
});
}
});
});
data.tournaments is an array of tournaments in order of their date. Lets say in the front end I have a select/option form where the user can choose date/prize/players as the order to sort the tournaments by. How can I sort the data.tournaments without having to call another route or refresh the page? I'm using Jade on the front end.
You can always sort them directly in the Browser via Javascript, either do it yourself or use a plugin like datatables.
If you don't wanna do that but do it on the server, you'll need an ajax call for that and a route that handles the sorting (based on the parameters you pass), and afterwards change the DOM according to the response. This goes without refreshing the page, but you'll need a route for that, or change the existing route and extend your controller to take optional parameters, something like
router.get('/tournaments/:sort?', function (req, res) {
TournamentController.getAllTournaments(req.param('sort'), function (err, docs) {
/* ... */
});
});

Serving dynamic URLs with express and mongodb

I'm building a site that has somewhat reddit-like functionality. I want user-submitted content to get its own page. Each submission is assigned a 5 character ID that I want to be in the URL for that page.
I've got this function in the router file which renders a page called titles:
exports.titles = function(req, res){
i = 0
read(function(post){
url = post[i].URL;
res.render('titles', {title: post[i].title, url: post[i].URL});
});
};
It is served by this statement in app.js:
app.get('/titles', home.titles); //home.js is the router file
The titles page has a link with the text post.title and the URL post.URL. When a user clicks on the link (e.g. domain.com/12345) they should be taken to a page called content with the content post.body.
How do I a)pass the URL back to my app.js file to include in an app.get, b) include the app.get function in this router file, or c) solve this in any other way?
Edit: I do have an object 'titles' that is a mongodb collection, but it is in a different module. No reason I can't add it to the router though.
Edit: I tried adding this to app.js to see if it would work:
app.get('/:id', function(req, res){
return titles.findOne({ id: req.params.id }, function (err, post) {
if (err) throw(err);
return res.render('content', {title: post.title, content: post.body});
});
});
Edit: I got it to work. All I did was format the title so that it would look like domain.com/titles/12345 and change app.get('/:id', to app.get('/titles/:id, ...
If I get you right I would do that the other way around.
Short version
I would get the id from the URL
Then I would pull from the database the data associated with this id
And use this data to build the final page.
You don't need to create a new route for each URL. An URL can contain some variable (here the id) and Express can parse the URL in order to get this variable. Then from this id you can get the data needed to build the proper page.
Long version
I assuming someone type in this URL: http://domain.com/1234.
I also assume that you have a variable titles which is a MongoDB Collection.
You can have a route defined like this:
app.get('/:id', function(req, res) {
// Then you can use the value of the id with req.params.id
// So you use it to get the data from your database:
return titles.findOne({ id: req.params.id }, function (err, post) {
if (err) { throw(err); }
return res.render('titles', {title: post.title, url: post.URL /*, other data you need... */});
});
});
Edit
I made some changes according to the last comments...

Resources