I have a working template engine (pug) to fill it's website with content depending from the situation. Acutally that template is rendered for the site '/show'.
Now I also need to change the url of this website depending from the content. That means I need the same template with new content for sites like: '/tree', '/house', '/urban' an do so. '/show' is the starting point, I need to change it's url with the new content.
I'm sure there is an easy answer, but I can't find the fitting question for that. So I can't find the right answer per searchengine. (Express.js res.render() and res.redirect() was my closest success, but it is not helpful for me.
I know, the following code is incorrect, at least because of the two resp.
server.get('/show', (req, resp) => {
loadContent(function(err, content){
if(content){
resp.location('/tree');
resp.render('myTemplate', content);
} else{
console.log(err);
}
})
});
How can I send my content to the template and replace the url to see both on the browser?
to send data to your pug template with express js use this syntax
const router = require('express').Router();
server.get('/show', (req, res, next) => {
loadContent(function(err, content){
if(content){
res.render('myTemplate', { content: content });
} else{
console.log(err);
}
})
and you will get it
script.
var content = !{content};
Well, I've found my problem. My approach was incorrect.
With
server.get('/:kindOfSite', function(req, resp){...});
I'm able to load the same template for different sites.
Learning can get hard sometimes...
Remember that your express route handlers are just functions. There's nothing that forces you to use an anonymous function. You can just use a regular function:
function handleRequest (req, resp) {
loadContent(function(err, content){
if(content){
resp.location('/tree');
resp.render('myTemplate', content);
} else{
console.log(err);
}
})
}
server.get('/show', handleRequest);
server.get('/tree', handleRequest);
server.get('/house', handleRequest);
server.get('/urban', handleRequest);
Indeed, you can do a bit of metaprogramming and call server.get() in a loop:
['/show','/tree','/house','/urban].forEach(route => {
server.get(route,handleRequest)
});
In fact, Express accepts regular expressions as route paths:
server.get(/\/(show|tree|house|urban)/, (req, resp) => {
loadContent(function(err, content){
if(content){
resp.location('/tree');
resp.render('myTemplate', content);
} else{
console.log(err);
}
})
});
Related
I want to access my DB in my EJS header file, which is a partial that is added to every page.
I have a Schema called Category and I want to get the name for the categories which will be in my header dynamically from the db.
I am tring to run the following commmand:
<% Category.find({}, name, function(err, names) { %>
<% if(err) { console.log(err); } %>
<% console.log("Names: " + names); %>
<% }); %>
But of course the header ejs file doesn't have access to Category.
I know normaly to access my DB in a ejs file I query the DB in the route and then pass the data to the ejs, but here since it is the header that will be added to every page I can't really do this operation in the route unless I do it in every route which does seem like such a good idea.
How can I get this data here?
Thanks
Database requests shouldn't be performed directly in view. This is prescribed by separation of concerns principle that stands behind MV* patterns.
Express route handlers act as MVC controllers, their purpose is to provide data from models to views.
Mongoose supports promises, so using callback-based API just complicates everything. Common data like could be provided as a separate function that returns a promise of data, e.g.:
function getPageData() { ... }
async function routeHandler(req, res, next) {
try {
const pageData = await getPageData();
res.render('index', {
...pageData,
/ * etc */
});
} catch (err) {
next(err);
}
};
routeHandler itself can be refactored to helper function that accepts view, view variables, req, res, and next.
Another approach is to make page data available globally in all or most views with additional middleware, as described in this related question, e.g.:
app.use(async function (req, res, next) {
try {
const pageData = await getPageData();
Object.assign(res.locals, pageData);
next();
} catch (err) {
next(err);
}
});
I am relatively new to web development and am trying to figure out how to publish the server environment (dev, test, prod, etc) to the front-end.
I am using Node.js with Express, but the following code is closer to pseudo-code because is this is more of a design pattern question.
One way to publish the server environment with Express is to add this to the middleware:
app.use(function(req,res,next){
res.locals.env= {}; //we add the property env to locals
});
so now in a front-end template we can access the 'env' locals variable:
<div>
<% app.env = env %> //hopefully 'env' will be available in a front-end template
</div>
I am not sure if the above is standard, but I feel like it certainly isn't ideal.
So I was thinking, perhaps we could do this instead, for either the first HTTP request, first socket request, or all requests(?):
//pseudo-code below
app.get('/', function(req,res,next){
if(req.isAuthenticated()){
socket.on('received-env-info', function(err,msg){
res.render('index',{auth:true});
}
socket.emit('send-env-info', env);
}
else{
res.render('index',{auth:false});
}
});
in this way we can be assured that the client knows what the environment is (or any other server variables), before any html is sent to the server. (We assume there is some socket.io handler on the client that subsequently sets the global env on the client to the variable that was sent from the server).
is this a good design pattern, or a bad one?
extra credit: we could take the same idea, and use it for authentication too:
app.get('/', function(req,res,next){
var infoForClient = {auth:null,env:env}; //should probably set auth to false for security but using 'null' for illustration purposes
if(req.isAuthenticated()){
infoForClient.auth = true;
}
else{
infoForClient.auth = false;
}
socket.on('received-info-from-client', function(msg){
if(msg === 'success'{ //some success message or another
res.render('index',infoForClient);
}
else{
infoForClient.auth = false;
res.render('index',infoForClient);
}
}
socket.emit('send-info-to-client', infoForClient);
}
});
I just need to know if this is a sane approach or not
This would work, but what's the gain vs
// B
app.get('/', function(req, res) {
res.json({
info: info,
html: template.render(info)
});
});
or
// C
socket.on('get-index', function () {
socket.emit('index', {
info: info,
html: template.render(info)
});
});
or with the template moved client side, simply return the info.
Once an intelligent client is in the picture, rendering the template server-side is no longer needed in the first place.
So looking at the client code
// A
socket.on('send-info-to-client', function (infoForClient) {
handleInfo(infoForClient);
socket.emit('recieved-info-from-client', true);
});
request('/', function (err, res, body) {
if (err) { return handleErr(err) }
handleHtml(body);
});
handle info and handle html end up split in two.
// B
request('/', function (err, res, body) {
if (err) { return handleErr(err) }
handleInfo(body.info);
handleHtml(body.html);
});
// C
socket.emit('get-index');
socket.on('index', function (msg) {
handleInfo(msg.info);
handleHtml(msg.html);
});
the handlers can be unified
and in the final case
// D
request('/', function (err, res, body) {
if (err) { return handleErr(err) }
handleInfo(body.info);
handleHtml(template.render(body.info));
});
Says we have a personal blog page, once the user visit '/home', the server returns all the blog posts he has written, meanwhile I also want to get data from the server so I can handle the data with template in front-end. In Express, is there any way to perform action like this:
app.get('/home', function () {
Post
.getAll()
.then(function (posts) {
res.send(posts)
})
res.render('home')
})
The reason I want to do this is to minimal ports and also gathering them up by functionalities, or have I define data-ports for each one?
Thanks 4 help
No, you can't do that. Separate your data and template providing logic like this sample:
app.get('/home', function () {
res.render('home');
});
app.get('/api/posts', function () {
Post
.getAll()
.then(function (posts) {
res.send(posts);
});
});
The following code is the user-facing part of a new node app we are building:
var loadInvoice = function(req, res, next) {
Invoice.findById(req.params.invoiceId, function (err, invoice) {
if (err) {
res.send(404, 'Page not found');
} else {
req.invoice = invoice;
next();
}
});
};
app.namespace('/invoices/:invoiceId', loadInvoice, function () {
app.get('', function(req, res){
var templateVals = {
//some template data
};
res.render('paymentselection', templateVals);
});
app.post('', function(req, res){
var data = {
// some data for the apiCall
};
someAPI.someRequest(data, function(err, data) {
console.log(res.status());
res.redirect(data.url);
});
});
});
The first method returns a confirmation page where the user presses a button to post to the same url, which triggers a redirect to an external website.
This all works exactly once. Every second request will crash the app with the message Cant set headers after they are sent. After carefull inspection of the code I could find no reason for this to happen so I added the console.log line which indeed confirms the location header has been set. But it is set to the value i got from someAPI on the previous request not the current one.
This makes absolutely no sense to me. I do not store this value anywhere nor do I do caching or persistence of this data in any way.
Does anybody know what could be causing this?
I use express, express-namespace, mogoose and swig
I found out the problem was being caused bij the 'Restler' libaray used within 'someAPI'. I have no idea how this is possible but swapping it out with something else fixed the problem.
This is a pure best practice question. I am pretty new to Node and Mongoose. I absolutely love the technology and have been cranking away on a project to build a JSON-backed API for an app that I'm building.
I am finding that I am continuously repeating code when I fetch objects from my database. For example:
Playlist.findById(req.params.id, function(err,playlist){
if (err)
return res.json({error: "Error fetching playlist"});
else if (!playlist)
return res.json({error: "Error finding the playlist"});
//Actual code being performed on the playlist that I'm fetching
});
The error handling at the top of the function call is annoying because I have to repeat that code for every call to the database... or so I think.
I thought about using a callback like:
var fetchCallback = function(err,objOrDoc,callback){
//Handle the error messages
callback(objOrDoc);
};
However, this approach would mess up my sequential flow since I would have to define the callback function before I performed the fetch. So, if I had a lot of database queries chained together, I would have to place the callbacks in reverse order, which is far from ideal in a clean-coding perspective.
I'm wondering if anyone has run into this issue and has any best practices for cutting down on the repetition.
I'm also using the express framework, so if there's a helpful way to handle it in express, I'd be interested to know, too.
There are a couple interesting approaches you could try here.
At the most simple, you could simply have a function that loads up an object and handles the output in an error condition.
fetchResource = function(model, req, res, callback) {
model.findById(req.params.id, function(err, resource) {
if (err)
return res.json({error: "Error fetching " + model.toString()});
else if (!playlist)
return res.json({error: "Error finding the " + model.toString()});
callback(resource);
});
};
app.on('/playlists/1', function(req, res) {
fetchResource(Playlist, req, res, function(playlist) {
// code to deal with playlist.
});
});
That's still quite a bit of duplication, so I might try to move this out into a middleware.
Route Middleware
Routes may utilize route-specific middleware by passing one or more additional callbacks (or arrays) to the method. This feature is extremely useful for restricting access, loading data used by the route etc.
Now I haven't tested this and it's a bit hand-wavey (read: pseudocode), but I think it should serve as a decent example.
// assuming a URL of '/playlist/5' or '/user/10/edit', etc.
function loadResource(model) {
return function(req, res, next) {
model.findById(req.params.id, function(err, resource) {
if (err)
return res.json({error: "Error fetching " + model.toString()});
else if (!resource)
return res.json({error: "Error finding the " + model.toString()});
req.resource = resource;
next();
});
}
}
app.get('/playlist/:id', loadResource(Playlist), function(req, res) {
var playlist = req.resource;
...
});
app.get('/user/:id', loadResource(User), function(req, res) {
var user = req.resource;
...
});
The express source contains a pretty good example of this pattern, and the middleware section in the docs (specifically under 'Route Middleware') details it further.