I'm trying to break a moustache template up into various components so I can reuse them, and get the assembled text returned through node.js. I cannot find anyone that has done this.
I can return must ache pages imply with:
function index(request, response, next) {
var stream = mu.compileAndRender('index.mu',
{name: "Me"}
);
util.pump(stream, response);
}
I just cannot figure out how to render a template and use it in another template. I've tried rendering it separately like this:
function index(request, response, next) {
var headerStream = mu.compileAndRender('header.mu', {title:'Home page'});
var headerText;
headerStream.on('data', function(data) {
headerText = headerText + data.toString();
});
var stream = mu.compileAndRender('index.mu',
{
heading: 'Home Page',
content: 'hello this is the home page',
header: headerText
});
util.pump(stream, response);
}
But the problem is that the header is not rendered before the page and even if I get that to happen. The header is seen as display text rather than html.
Any help appreciated.
You need to put the lines var stream = ... and util.pump(... in headerStream.on('end', function () { /* here */ });, so that they are ran after the headerStream has sent all the data to your data listener.
To include raw HTML you have to use the triple braces: {{{raw}}} in your template as http://mustache.github.com/mustache.5.html states.
The moustache guys came back with the answer. I can do it by using partials like this:
{{> index.mu}}
Inside the moustache template rather than trying to do it in node.js.
Related
I have a link on my site. When clicked it'll call a function that does a mongoose query.
I'd like the results of that query to be sent to the same page in a variable without reloading the page. How do I do that? Right now it is just rendering the page again with new query result data.
// List comments for specified chapter id.
commentController.list = function (req, res) {
var chapterId = req.params.chapterId;
var query = { chapterId: chapterId };
Chapter.find({ _id: chapterId }).then(function (chapter) {
var chapter = chapter;
Comment.find(query).then(function (data) {
console.log(chapter);
Chapter.find().then(function(chapters){
return res.render({'chapterlinks', commentList: data, user: req.user, chapterq: chapter, chapters:chapters });
})
});
})
};
You just need to make that request from your browser via AJAX:
https://www.w3schools.com/xml/ajax_intro.asp
This would be in the code for your client (browser), not the code for your server (nodejs).
UPDATE:
Here's a simple example, which uses jQuery to make things easier:
(1) create a function that performs and handles the ajax request
function getChapterLinks(chapterId) {
$.ajax({
url: "/chapterLinks/"+chapterId,
}).done(function(data) {
//here you should do something with data
console.log(data);
});
}
(2) bind that function to a DOM element's click event
$( "a#chapterLinks1" ).click(function() {
getChapterLinks(1);
});
(3) make sure that DOM element is somewhere in you html
<a id="chapterLinks1">Get ChapterLinks 1</a>
Now when this a#chapterLinks1 element is clicked, it will use AJAX to fetch the response of /chaptersLink/1 from your server without reloading the page.
references:
http://api.jquery.com/jquery.ajax/
http://api.jquery.com/jquery.click/
I have a checkbox that when pressed call a function that does a GET request. Based on the selection I want to display extra checkboxes on the same page. At the moment this is the code I have so far:
Client side
function selectedHouse(house)
{
if(house.checked){
$.get('/', {data: house.value});
}
}
Server side
var routing = function (nav, houses) {
router.route('/')
.get(function (req, res) {
var rooms = [];
rooms = getRooms(req.query.data);
console.log(rooms);
res.render('index', {
title: 'Independent cleaner',
nav: nav,
houses: houses,
roomsForHouse: rooms
});
});
return router;
};
The first time the page loads, it loads with the correct title, nav and houses. When the function is executed on client side, I get back the related rooms for the house and I'm trying to populate the roomsForHouse variable which I'm displaying on the view.
The problem is that the view doesn't render the roomsForHouse variable. So the GET request is called once the page loads and a second time when the function executes. Can this be achieved?
It's a bit more complex. You'll need to use ajax for this. EJS is server side templates (as you are using them) so you'll want to use jQuery to make the call and update your already rendered page.
Server
Your server will need a route that delivers JSON data. Right now you are rendering the entire page. So:
app.get('/rooms/:id', function (req, res) {
// Get the house info from database using the id as req.params.id
...
// Return the data
res.json({
rooms: 2
...
});
});
Client
Using jQuery make a call to your json route once the user selects the house.
function selectedHouse(house)
{
if(house.checked){
// Pass some identifier to use for your database
$.ajax({
type: 'GET',
url: '/rooms/' + house.id,
success: function(data) {
// Update the element - easiet is to use EJS to make sure each house has an id/class with the id in it
// Given an ID of 2 this says find the div with class house_2 and updates its div with class room to the number of rooms
$('.house_' + house.id + ' .rooms').text(data.rooms);
});
}
}
This is more of pseudo code then anything but should put you on the right track.
res.render can't rerender view, for refresh page in second time you need use javascript to replace html. This is not good solution
$.get("/",function(html){
document.open();
document.write(html);
document.close();
});
For better you should use another router to render DOM you want to change
I'm trying to generate a zip archive of icons on-the-fly and stream the response to the user to download, via a JSON POST request.
The zip itself is created and the response is returned, but the client-side is not prompted to download the file, and the response is garbled (which I assume is the contents of the archive).
app.post('/download', function(request, response) {
var icons = request.body;
var filename = 'icons.zip';
response.attachment(filename);
var zip = Archiver('zip');
zip.on('finish', function(error) {
return response.end();
});
zip.pipe(response);
for (var i = 0; i < icons.length; i++) {
var icon = getIcon(icons[i]);
zip.append(fs.createReadStream('public/' + icon.svg), { name: icon.title + '.svg' });
}
zip.finalize();
});
I'm wondering if there's anything missing from the server-side code that's preventing the download on the client-side, but from the example I've followed (https://github.com/archiverjs/node-archiver/blob/master/examples/express.js), it doesn't seem to be the case.
Here's some screenshots of the request made and the response received:
AJAX calls don't trigger file downloads in a browser, so you need to work around that.
One possibility is to change the request from a POST to a GET and put the names of the icons in the URL as parameters.
Your Express route would look like this:
app.get('/download/*?', function(request, response) {
// Make sure icons names were provided:
if (! request.params[0]) {
return response.sendStatus(400);
}
// Split on `/`, which is used as separator between icon names:
var icons = request.params[0].split(/\//);
// The rest can be the same
...
});
Client-side, you would use this:
location.href = 'http://your-server/download/Chevron%20Down/Close/Trash';
(obviously you can also generate that URL dynamically based on user input, as long as you make sure that the icon names are properly URL-encoded)
I would like to scrape the http://www.euromillones.com.es/ website to get the last winning 5 numbers and two stars. It can be seen on the left column of the website. I've been reading tutorials, but I am not capable of achieving this.
This is the code I wrote so far:
app.get('/winnernumbers', function(req, res){
//Tell the request that we want to fetch youtube.com, send the results to a callback function
request({uri: 'http://www.euromillones.com.es/ '}, function(err, response, body){
var self = this;
self.items = new Array();//I feel like I want to save my results in an array
//Just a basic error check
if(err && response.statusCode !== 200){console.log('Request error.');}
//Send the body param as the HTML code we will parse in jsdom
//also tell jsdom to attach jQuery in the scripts and loaded from jQuery.com
jsdom.env({
html: body,
scripts: ['http://code.jquery.com/jquery-1.6.min.js ']
}, function(err, window){
//Use jQuery just as in a regular HTML page
var $ = window.jQuery;
res.send($('title').text());
});
});
});
I am getting the following error:
Must pass a "created", "loaded", "done" option or a callback to jsdom.env.
It looks to me that you've just used a combination of arguments that jsdom does not know how to handle. The documentation shows this signature:
jsdom.env(string, [scripts], [config], callback);
The two middle arguments are optional but you'll note that all possible combinations here start with a string and end with a callback. The documentation mentions one more way to call jsdom.env, and that's by passing a single config argument. What you are doing amounts to:
jsdom.env(config, callback);
which does not correspond to any of the documented methods. I would suggest changing your code to pass a single config argument. You can move your current callback to the done field of that config object. Something like this:
jsdom.env({
html: body,
scripts: ['http://code.jquery.com/jquery-1.6.min.js'],
done: function (err, window) {
//Use jQuery just as in a regular HTML page
var $ = window.jQuery;
res.send($('title').text());
}
});
I have been working through the tutorial by Alex Young on using flash messages. According to this tutorial, one is able to override the default flash messages formatting using dynamicHelpers. Unfortunately there are no details provided on what is going on, and it is not possible to post any comments on the tutorial page to ask relevant questions.
What I fail to see is the relationship between the call to req.flash() in the file 'app.js', and the FlashMessage object exported in file 'helpers.js'. Why would a regular call to req.flash(), which is a standard function in express.js, link to this FlashMessage prototype in the first place? I can't see how this is happening when I look at the code.
At first I thought the FlashMessage object might have been provided to req.flash() by express.js, in which case we are just extending or overriding it in our helper file. The problem with this is that I cannot find any reference to FlashMessage in the source code of express.js.
I would be really grateful if someone could explain it to me.
The flash message is set in file 'apps.js' by calling:
req.flash('info', 'Document created.');
The FlashMessage obect is exported 'helpers.js':
FlashMessage.prototype = {
// Get css definition string for icon.
get icon() {
switch (this.type) {
case 'info':
return 'ui-icon-info';
case 'error':
return 'ui-icon-alert';
}
},
// Get css class for message container.
get stateClass() {
switch (this.type) {
case 'info':
return 'ui-state-highlight';
case 'error':
return 'ui-state-error';
}
},
// Returns HTML formatted message.
toHTML: function() {
return '<div class="ui-widget flash">' +
'<div class="' + this.stateClass + ' ui-corner-all">' +
'<p><span class="ui-icon ' + this.icon + '"></span>' + this.messages.join(', ') + '</p>' +
'</div>' +
'</div>';
}
};
exports.dynamicHelpers = {
flashMessages: function(req, res) {
var html = '';
['error', 'info'].forEach(function(type) {
var messages = req.flash(type);
if (messages.length > 0) {
html += new FlashMessage(type, messages).toHTML();
}
});
return html;
}
};
In app.js file the full routing function which calls req.flash is as follows:
// Attach dynamicHelpers to app.
app.dynamicHelpers(require('./helpers.js').dynamicHelpers);
// Routing function which calls req.flash.
app.post('/documents', loadUser, function(req, res) {
// Create Document object and assign value.
console.log('Document content: %s', req.body['document']);
var document = new Document(req.body['document']);
document.save(function() {
// Redirect to another page.
req.flash('info', 'Document created.');
res.redirect('/documents');
}
});
});
There are 2 different things here:
a) req.flash() which is implemented by Express itself - not by you, you're just using that function
b) your dynamic helper:
From the Express guide:
Dynamic view helpers are simply functions which accept req, res, and
are evaluated against the Server instance before a view is rendered.
The return value of this function becomes the local variable it is
associated with.
app.dynamicHelpers({
session: function(req, res){
return req.session;
}
});
Let's "translate" that into your code:
// Attach dynamicHelpers to app.
app.dynamicHelpers(require('./helpers.js').dynamicHelpers);
That means that when you call the variable flashMessages in your view code, you'll get the html representation of those flash variables defined.
So the most important thing here is to consider you are only using req.flash(), not implementing it. You are implementing a helper that is using that function.