Node.js and Express - Regarding rendering pages in a directory with one call - node.js

Hopefully I'm not duplicating a question here, but I looked around and didn't see anything that addressed this specific issue.
I'm constructing some pages by creating a bunch of unique content blocks that I store in a folder - things like this:
h4 Content Example
p.description.
Lorem ipsum dolor sample text etc etc
in, say, pages\contentBlocks\Example\contentExample.jade.
These content blocks are added to a mixin (pages\mixins.jade) that looks like this:
mixin contentBlock(title)
.content-block(id=title.toLowerCase())
h3 #{title}
if block
.content
block
and I call this mixin within a larger page (pages\portfolio.jade) like this:
.subsection
//- Expects a list of objects of the format { title, html }
each val in pageBlocks
+contentBlock(val.title)
!= val.html
(the intention is to later enhance this with other variables that can be passed down into the mixin for in-place formatting, like background images)
I would like to make use of the ability of Express to automatically render all of the pages in a directory in a single call. Like so (index.js):
var pageBlocks = [];
res.render('pages/contentBlocks/Example/*', function (err, html) {
if (err) throw err;
pageBlocks.push({ html: html });
});
However, I'd also like to be able to reference the title of the file, for example for setting the class of the block that I put it in. Ideally, it would look something like this:
res.render('pages/contentBlocks/Example/*', function (err, html) {
if (err) throw err;
pageBlocks.push({ title:functionToGetFileName(), html: html });
});
Is there any way to do this without manually iterating through the files in that directory?
Or, since this method seems needlessly convoluted (given the presence of mixins, includes, extends, and partials, it feels like I ought to be able to do this without extra render calls), is there a better way to do this?
Edit: I've done a bit more looking around and code testing, and now I'm unsure. Can Express implicitly render all of the files in a directory? Based on this question, I assumed it could, but I might have misread. The formatting of res.render('path/to/directory/', callback) doesn't seem to be working - I get the following error:
Error: Failed to lookup view "path/to/directory/" in views directory "/path/to/app/views"

For a temporary solution, I am using the following pattern:
exampleBlocks = [];
eb_dir = "pages/contentBlocks/Example";
fs.readdirSync("./views/" + eb_dir).forEach(function(file) {
res.render(eb_dir + "/" + file, function (err, html) {
if (err) throw err;
exampleBlocks.push({title: file, html:html})
});
});
Which is concise enough for my tastes, though calling res.render() so many times rustles my jimmies. I'd rather it be a single batch render, or better yet, solved through clever use of jade structures - but I still haven't thought of a way to do the latter.
Edit:
I decided I'd rather not simply enumerate the files in the folder - I want more control of the order. Instead of using an each loop in my page, I'm simply going to call the mixin with included content, like this (pages\portfolio.jade in my previous examples):
.subsection
+contentBlock("Block 1")
include blocks/block1
+contentBlock("Block 2")
include blocks/block2
I will leave the question here, however, as others may encounter a similar issue.

Related

Where should I put custom errors in sails.js?

I was wondering what's the best practice and if I should create:
a directory in which declare statically all the errors my application uses, like api/errors/custom1Error
declare them directly inside the files
or put the files directly inside the dir that needs that error, like api/controller/error/formInvalidError
other options!?
A neat way of going about this would be to simply add the errors as custom responses under api/responses. This way even the invocation becomes pretty neat. Although the doc says you should add them directly in the responses directory, I'm sure there must be a way to nest them under, say, responses/errors. I'll try that out and post an update in a bit.
Alright, off a quick search, I couldn't find any way to nest the responses, but you can use a small workaround that's not quite as neat:
Create the responses/errors directory with all the custom error response handlers. Create a custom response and name it something like custom.js. Then specify the response name while calling res.custom().
I'm adding a short snippet just for illustration:
api/responses/custom.js:
var customErrors = {
customError1: require('./errors/customError1'),
customError2: require('./errors/customError2')
};
module.exports = function custom (errorName, data) {
var req = this.req;
var res = this.res;
if (customErrors[errorName]) return customErrors[errorName](req, res, data);
else return res.negotiate();
}
From the controller:
res.custom('authError', data);
If you don't need logical processing for different errors, you can do away with the whole errors/ directory and directly invoke the respective views from custom.js:
module.exports = function custom (viewName, data) {
var req = this.req;
var res = this.res;
return res.view('errors/' + viewName, data);//assuming you have error views in views/errors
}
(You should first check if the view exists. Find out how on the linked page.)
Although I'm using something like this for certain purposes (dividing routes and so on), there definitely should be a way to include response handlers defined in different directories. (Perhaps by reconfiguring some grunt task?) I'll try to find that out and update if I find any success.
Good luck!
Update
Okay, so I found that the responses hook adds all files to res without checking if they are directories. So adding a directory under responses results in a TypeError from lodash. I may be reading this wrong but I guess it's reasonable to conclude that currently it's not possible to add a directory there, so I guess you'll have to stick to one of the above solutions.

Scraping URLs from a node.js data stream on the fly

I am working with a node.js project (using Wikistream as a basis, so not totally my own code) which streams real-time wikipedia edits. The code breaks each edit down into its component parts and stores it as an object (See the gist at https://gist.github.com/2770152). One of the parts is a URL. I am wondering if it is possible, when parsing each edit, to scrape the URL for each edit that shows the differences between the pre-edited and post edited wikipedia page, grab the difference (inside a span class called 'diffchange diffchange-inline', for example) and add that as another property of the object. Right not it could just be a string, does not have to be fully structured.
I've tried using nodeio and have some code like this (i am specifically trying to only scrape edits that have been marked in the comments (m[6]) as possible vandalism):
if (m[6].match(/vandal/) && namespace === "article"){
nodeio.scrape(function(){
this.getHtml(m[3], function(err, $){
//console.log('getting HTML, boss.');
console.log(err);
var output = [];
$('span.diffchange.diffchange-inline').each(function(scraped){
output.push(scraped.text);
});
vandalContent = output.toString();
});
});
} else {
vandalContent = "no content";
}
When it hits the conditional statement it scrapes one time and then the program closes out. It does not store the desired content as a property of the object. If the condition is not met, it does store a vandalContent property set to "no content".
What I am wondering is: Is it even possible to scrape like this on the fly? is the scraping bogging the program down? Are there other suggested ways to get a similar result?
I haven't used nodeio yet, but the signature looks to be an async callback, so from the program flow perspective, that happens in the background and therefore does not block the next statement from occurring (next statement being whatever is outside your if block).
It looks like you're trying to do it sequentially, which means you need to either rethink what you want your callback to do or else force it to be sequential by putting the whole thing in a while loop that exits only when you have vandalcontent (which I wouldn't recommend).
For a test, try doing a console.log on your vandalContent in the callback and see what it spits out.

express-resource custom mapping

I can't find any decent documentation on this that contains any depth when requiring with app.resource.
The only information that I can find is creating the variable within the current file. Here
The current code that I have is below:
var favs = app.resource('favs', require('./modules/favs'));
favs.map('get', '/user', favs.buses);
However it comes back saying that it is undefined?
In the module favs I have.
exports.buses = function (req, res) {
res.render('favs/buses', {
title: 'Bus Stops'
});
});
You have a syntax error in favs.js: The last right bracket ) is redundant.
Apart from that, you seem to conflate the actual module with the resource object you get back from app.resource. You want to pass a reference to the request handler you want to invoke when a visitor hits the path (in your case, /favs/user). So what you want is something like:
var favs = require('./modules/favs'),
favsResource = app.resource('favs', favs);
favsResource.map('get', '/user', favs.buses);
If you feel a little lost dealing with express-resource, I encourage you to begin with plain express, and only start using express-resource when you are more familiar with how express works. TJ's helper modules have a tendency to be lacking in documentation, and you should use them only if you feel comfortable reading the code, IMO.

Passing raw Markdown text to Jade

I'm playing around with my first Node.js Express application, and as every programmer knows, the first thing you should build when testing out a new framework is a blog! Anyway, I'd like to write the articles in Markdown and then render it in the view. I saw that Jade allows for this to be done inside the view itself, using filters, but I can't get that working.
To simplify the situation, here's an example of what I'm talking about.
//app.js
res.render("article", {
md : "Hello World!\n\n*Woo*"
});
//article.jade
section
:markdown
#{md}
But, that outputs this: <section><h1>{md}</h1></section>... it isn't substituting in the variables I've passed to it.
Then I tried this:
//article.jade
section
:markdown
!{md}
And the output is this:
<section><p>Hello World!
*Woo*</p></section>
So, now it's not parsing the markdown!
I have been able to get this to work by parsing the markdown in the app.js file and then passing the HTML to the view to display, but I don't know, that seems a bit messier.
Is there a way to pass variables into Jade filters?
You can do this with a function passed in to jade from node:
var md = require("node-markdown").Markdown;
Then pass it into the view as a local:
res.render('view', { md:md, markdownContent:data });
Then render it in the jade view by calling the function:
!= md(markdownContent)
The node module node-markdown is deprecated. The marked is advanced new version. You can try like this
var md = require('marked');
Inside your router
res.render('template', { md: md });
Inside your jade template
div!= md(note.string)
I don't think jade can do this out of the box. One way to accomplish it that might feel slightly cleaner than pre-rendering the markdown is to create a helper function called markdown that takes a markdown string and returns HTML. Then you could do something like
section
!= markdown(md)
The markdown function should be included in the locals data when you render the jade template and can directly use a markdown library to convert the markdown syntax to HTML.
If you are using Scalate's Jade support you can enter:
section
:&markdown
#{md}
You can also import external files with:
section
:&markdown
#{include("MyFile.md")}

Render template to variable in expressjs

Is there a way to render template to a variable instead to output?
res.render('list.ejs', {
posts: posts
});
something like this
var list = render('list.ejs', {
posts: posts
});
The easiest way to do that is to pass a callback to res.render, in your example:
res.render('list.ejs', {posts: posts}, function(err, list){
//
});
But if you want to render partial templates in order to include them in another template you definitely should have a look at view partials.
I am quite a newbie on express.js, anyway I am not sure you can access the rendered string that way, although if you look at express' "view.js" source on github (here) you see that it's accepting a callback as second argument, if that may help: you may access the rendered string there.
Otherwise, I think it's quite easy to patch the code to add a method returning the rendered string without sending it: on line #399 you have the very call that gives the string you are looking for.
This wasn't the question originally asked, but based on comments from the OP and others, it seems like the goal is to render a partial via json (jsonp), which is something I just had to do.
It's pretty easy:
app.get('/header', function (req, res)
{
res.render('partials/header', { session: req.session, layout: null }, function (err, output)
{
res.jsonp({ html: output });
});
});
Note: In my case, the header partial required the session, and my template library (express-hbs) needed layout: null to render the partial without using the default layout.
You can then call this from Javascript code in the client like any other JSONP endpoint.

Resources