I'm just getting into this whole node.js business and like it so far; however I've run into an issue involving connect/mustach.
Here's the code for a simple one page app; at this point I'm really just trying to get the app to use my mustache templates so that I can take it from there.
var connect = require("connect"),
fs = require("fs"),
mustache = require("mustache");
connect(
connect.static(__dirname + '/public'),
connect.bodyParser(),
function(req, res){
var data = {
variable: 'Some text that I'd like to see printed out. Should in the long run come from DB.'
},
htmlFile = fs.createReadStream(
__dirname + "/views/index.html",
{ encoding: "utf8" }
),
template = "",
html;
htmlFile.on("data", function(data){
template += data;
});
htmlFile.on("end", function(){
html = mustache.to_html(template, data);
})
res.end(html);
}
).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
My problem here is that the above code generates a blank webpage.
If I log the html-variable I get two outputs of the html with the variable text attached in it, so the to_html-function seems to do it's job. And if I do res.end('some string'); the string is displayed in the browser as it should.
The template is a plain old .html-file with a <p>{{variable}}</p>-tag in its body.
Any idea what's wrong?
Your problem is that you're not using async code correctly. By the time res.end(html) gets called the file is not yet read. Correct usage:
htmlFile.on("end", function(){
html = mustache.to_html(template, data);
res.end(html);
})
Also you should take care of the syntax error: variable: 'Some text that I'd like to see printed out. Should in the long run come from DB.'
(misuse of ')
Related
Here is my code i am facing issue in my get function where i write my code in forEach loop it gave an error of quotation(Unterminated String Literal).
var express = require('express');
var app = express();
var dataFile = require('./data/friend.json');
app.set('port', process.env.PORT || 5000);
app.get("/" , function(req , res ){
var data = '';
dataFile.friends.forEach(function(item){
data +='
<div>
<li>
<h1>${item.name}</h1>
<h2>${item.Class}</h2>
</li>
</div>';
});
res.send("Welcome");
});
app.listen(app.get('port') , function (err){
console.log("Server is running at "+ app.get('port'));
});
You can not have line breaks in JavaScript strings without concatenating or "escaping" them, unless using template literals (using the back-tick).
There are three ways to go about doing this.
Concatenation:
dataFile.friends.forEach(function(item){
data +='<div>'+
'<li>' +
'<h1>${item.name}</h1>' +
'<h2>${item.Class}</h2>' +
'</li>' +
'</div>';
});
Escaping Line Breaks:
dataFile.friends.forEach(function(item){
data +='<div>\
<li>\
<h1>${item.name}</h1>\
<h2>${item.Class}</h2>\
</li>\
</div>';
});
Template Literals:
dataFile.friends.forEach(function(item){
data +=`<div>
<li>
<h1>${item.name}</h1>
<h2>${item.Class}</h2>
</li>
</div>`;
});
Reference: SyntaxError: unterminated string literal
** It is also important to note that creating a string of elements may not instantiate them in to the DOM correctly. This will cause JavaScript to be unable to interact with these elements. DigitalOcean has a tutorial on this.
Alternatively, you can make use of template literals as mentioned above and available in the above reference link, this will instantiate elements inside the DOM.
I'm trying to create a Node.js application on Heroku that will output 10 different ASCII faces (I already have the module needed for that). Using the Node tutorial on Heroku, I've set it up to output 10 faces. However, when I try to actually run the code, it puts all of the faces inline with each other. How should I try to make it so that the faces are outputted (if that's even a word) on their own lines?
My current index.js is as follows:
var express = require('express');
var app = express();
var cool = require('cool-ascii-faces');
app.set('port', (process.env.PORT || 5000));
app.get('/', function(request, response) {
var result = ''
var times = process.env.TIMES || 5
for (i=0; i < times; i++)
result += cool();
response.send(result);
});
app.listen(app.get('port'), function() {
console.log("Node app is running on port: " + app.get('port'))
})
I have a .env file already set up for Foreman to use (when testing locally) that contains the following:
TIMES=9
If you want to have a look at the output, head on over here.
TL;DR: How do I use newlines in Node?
I visited the site in question and did a View -> Source. There's no markup of any kind like HTML, BODY, etc. I assume that the browser then would interpret the output as HTML. This ought to work since we're using an HTML tag and a pair of PRE tags to indicate that if we see a hard return ('\n') then the browser should display it.
app.get('/', function(request, response) {
var result = '<html><pre>';
var times = process.env.TIMES || 5
for (i=0; i < times; i++)
result += cool() + '\n';
result += '</pre></html>';
response.send(result);
});
By default, the browser assumes that web servers send HTML.
HTML ignores newlines.
If you aren't sending HTML, you need to specify that by setting a different type:
response.header("Content-Type", "text/plain");
To actually send a newline, just concatenate '\n' to your string.
Need help.
I use gulp-conect and it livereload method. But if I build a few template in time, get a lot of page refresh. Is any solution, I want to build few templates with single page refresh?
So, I reproduce the problem you have and came accross this working solution.
First, lets check gulp plugins you need:
gulp-jade
gulp-livereload
optional: gulp-load-plugins
In case you need some of them go to:
http://gulpjs.com/plugins/
Search for them and install them.
Strategy: I created a gulp task called live that will check your *.jade files, and as you are working on a certain file & saving it, gulp will compile it into html and refresh the browser.
In order to accomplish that, we define a function called compileAndRefresh that will take the file returned by the watcher. It will compile that file into html and the refesh the browser (test with livereload plugin for chrome).
Notes:
I always use gulp-load-plugin to load plugins, so thats whay I use plugins.jad and plugins.livereload.
This will only compile files that are saved and while you have the task live exucting on the command line. Will not compile other files that are not in use. In order to accomplish that, you need to define a task that compiles all files, not only the ones that have been changed.
Assume .jade files in /jade and html output to /html
So, here is the gulpfile.js:
var gulp = require('gulp'),
gulpLoadPlugins = require('gulp-load-plugins'),
plugins = gulpLoadPlugins();
gulp.task('webserver', function() {
gulp.src('./html')
.pipe(plugins.webserver({
livereload: true
}));
gulp.watch('./jade/*.jade', function(event) {
compileAndRefresh(event.path);
});
});
function compileAndRefresh(file) {
gulp.src(file)
.pipe(plugins.jade({
}))
.pipe(gulp.dest('./html'))
}
Post edit notes:
Removed liveReload call from compileAndRefresh (webserver will do that).
Use gulp-server plugin insted of gulp-connect, as they suggest on their repository: "New plugin based on connect 3 using the gulp.src() API. Written in plain javascript. https://github.com/schickling/gulp-webserver"
Something you can do is to watch only files that changes, and then apply a function only to those files that have been changed, something like this:
gulp.task('live', function() {
gulp.watch('templates/folder', function(event) {
refresh_templates(event.path);
});
});
function refresh_templates(file) {
return
gulp.src(file)
.pipe(plugins.embedlr())
.pipe(plugins.livereload());
}
PS: this is not a working example, and I dont know if you are using embedlr, but the point, is that you can watch, and use a callback to call another function with the files that are changing, and the manipulate only those files. Also, I supposed that your goal is to refresh the templates for your browser, but you manipulate as you like, save them on dest or do whatever you want.
Key point here is to show how to manipulate file that changes: callback of watch + custom function.
var jadeTask = function(path) {
path = path || loc.jade + '/*.jade';
if (/source/.test(path)) {
path = loc.jade + '/**/*.jade';
}
return gulp.src(path)
.pipe(changed(loc.markup, {extension: '.html'}))
.pipe(jade({
locals : json_array,
pretty : true
}))
.pipe(gulp.dest(loc.markup))
.pipe(connect.reload());
}
First install required plugins
gulp
express
gulp-jade
connect-livereload
tiny-lr
connect
then write the code
var gulp = require('gulp');
var express = require('express');
var path = require('path');
var connect = require("connect");
var jade = require('gulp-jade');
var app = express();
gulp.task('express', function() {
app.use(require('connect-livereload')({port: 8002}));
app.use(express.static(path.join(__dirname, '/dist')));
app.listen(8000);
});
var tinylr;
gulp.task('livereload', function() {
tinylr = require('tiny-lr')();
tinylr.listen(8002);
});
function notifyLiveReload(event) {
var fileName = require('path').relative(__dirname, event.path);
tinylr.changed({
body: {
files: [fileName]
}
});
}
gulp.task('jade', function(){
gulp.src('src/*.jade')
.pipe(jade())
.pipe(gulp.dest('dist'))
});
gulp.task('watch', function() {
gulp.watch('dist/*.html', notifyLiveReload);
gulp.watch('src/*.jade', ['jade']);
});
gulp.task('default', ['livereload', 'express', 'watch', 'jade'], function() {
});
find the example here at GitHub
I have an issue with outputting the readable stream to the http response.
behind the scenes there is a regular request and response streams coming from the generic http createServer. I check to see if the 'req.url' ends in css, and I create a readable stream of this file. I see the css contents in the console.log, with the right css code I expect. Then, I try to pipe the readable css file stream to the response, but in Chrome, the file response is blank when I inspect the response. It is a 200 response though. Any thoughts at first glance? I've tried different variations of where I have code commented out.
router.addRoute("[a-aA-z0-9]{1,50}.css$", function(matches){
var cssFile = matches[0];
var pathToCss = process.cwd() + "/" + cssFile;
// takes care of os diffs regarding path delimiters and such
pathToCss = path.normalize(pathToCss);
console.log(matches);
console.log("PATH TO CSS");
console.log(pathToCss)
var readable = fs.createReadStream(pathToCss);
var write = function(chunk){
this.queue(chunk.toString());
console.log(chunk.toString());
}
var end = function(){
this.queue(null);
}
var thru = through(write,end);
//req.on("end",function(){
res.pipe(readable.pipe(thru)).pipe(res);
//res.end();
//});
});
you need to pipe your readable stream into your through-stream, and then pipe it into the response:
readable.pipe(thru).pipe(res);
edit: for preparing your css path, just use path.join instead of concatenating your path and normalizing it:
var pathToCss = path.join(process.cwd(), cssFile);
I separated out this route (css) from my normal html producing routes, the problem I had was that my normal routes in my router object returned strings, like res.end(compiled_html_str), and the css file readable stream was going through that same routing function. I made it separate by isolating it from my router.
var cssMatch = [];
if(cssMatch = req.url.match(/.+\/(.+\.css$)/)){
res.writeHead({"Content-Type":"text/css"});
var cssFile = cssMatch[1];
var pathToCss = process.cwd() + "/" + cssFile;
// takes care of os diffs regarding path delimiters and such
pathToCss = path.normalize(pathToCss);
console.log(cssMatch);
console.log("PATH TO CSS");
console.log(pathToCss)
var readable = fs.createReadStream(pathToCss);
var cssStr = "";
readable.on("data",function(chunk){
cssStr += chunk.toString();
});
readable.on("end",function(){
res.end(cssStr);
});
}
I have the following code:
Meteor.methods({
saveFile: function(blob, name, path, encoding) {
var path = cleanPath(path), fs = __meteor_bootstrap__.require('fs'),
name = cleanName(name || 'file'), encoding = encoding || 'binary',
chroot = Meteor.chroot || 'public';
// Clean up the path. Remove any initial and final '/' -we prefix them-,
// any sort of attempt to go to the parent directory '..' and any empty directories in
// between '/////' - which may happen after removing '..'
path = chroot + (path ? '/' + path + '/' : '/');
// TODO Add file existance checks, etc...
fs.writeFile(path + name, blob, encoding, function(err) {
if (err) {
throw (new Meteor.Error(500, 'Failed to save file.', err));
} else {
console.log('The file ' + name + ' (' + encoding + ') was saved to ' + path);
}
});
function cleanPath(str) {
if (str) {
return str.replace(/\.\./g,'').replace(/\/+/g,'').
replace(/^\/+/,'').replace(/\/+$/,'');
}
}
function cleanName(str) {
return str.replace(/\.\./g,'').replace(/\//g,'');
}
}
});
Which I took from this project
https://gist.github.com/dariocravero/3922137
The code works fine, and it saves the file, however it repeats the call several time and each time it causes meteor to reset using windows version 0.5.4. The F12 console ends up looking like this: . The meteor console loops over the startup code each time the 503 happens and repeats the console logs in the saveFile function.
Furthermore in the target directory the image thumbnail keeps displaying and then display as broken, then a valid thumbnail again, as if the fs is writing it multiple times.
Here is the code that calls the function:
"click .savePhoto":function(e, template){
e.preventDefault();
var MAX_WIDTH = 400;
var MAX_HEIGHT = 300;
var id = e.srcElement.id;
var item = Session.get("employeeItem");
var file = template.find('input[name='+id+']').files[0];
// $(template).append("Loading...");
var dataURL = '/.bgimages/'+file.name;
Meteor.saveFile(file, file.name, "/.bgimages/", function(){
if(id=="goodPhoto"){
EmployeeCollection.update(item._id, { $set: { good_photo: dataURL }});
}else{
EmployeeCollection.update(item._id, { $set: { bad_photo: dataURL }});
}
// Update an image on the page with the data
$(template.find('img.'+id)).delay(1000).attr('src', dataURL);
});
},
What's causing the server to reset?
My guess would be that since Meteor has a built-in "automatic directories scanning in search for file changes", in order to implement auto relaunching of the application to newest code-base, the file you are creating is actually causing the server reset.
Meteor doesn't scan directories beginning with a dot (so called "hidden" directories) such as .git for example, so you could use this behaviour to your advantage by setting the path of your files to a .directory of your own.
You should also consider using writeFileSync insofar as Meteor methods are intended to run synchronously (inside node fibers) contrary to the usual node way of asynchronous calls, in this code it's no big deal but for example you couldn't use any Meteor mechanics inside the writeFile callback.
asynchronousCall(function(error,result){
if(error){
// handle error
}
else{
// do something with result
Collection.update(id,result);// error ! Meteor code must run inside fiber
}
});
var result=synchronousCall();
Collection.update(id,result);// good to go !
Of course there is a way to turn any asynchronous call inside a synchronous one using fibers/future, but that's beyond the point of this question : I recommend reading this EventedMind episode on node future to understand this specific area.