Right now, I have an ASP.NET application where, within the aspx files, at various points, I call a function which inserts standard template HTML. For example:
<html>
<head>
</head>
<body>
<%=SectionHeader('Section title 1') %>
some content for section 1
<%=SectionHeader('Section title 2') %>
some content for section 2
</body>
</html>
So wherever the SectionHeader function was called, it would read the passed in parameter, and insert the HTML for the section header, such as {title}. I'm trying to figure out how to accomplish the same thing in Node.
I understand how to do a basic token replacement - reading a static HTML file, looking for a token (such as {token1}) and replacing it with something. But short of using Regex and complex string manipulation, is there any way to accomplish the same thing in Node that I'm doing with ASP.NET?
I took the generated application skeleton, and modified the index.js and index.jade to pass a function into the template. I think is what you are asking for, but there may be opinions if this is a good architecture to have the template call back into logic.
index.js
exports.index = function(req, res){
var fn = function(initial) {
return initial + ". Tester";
};
res.render('index', { title: 'Express', fn : fn });
};
index.jade
block content
h1= title
p Welcome to #{title}
div Hello #{fn('A')}
Now, when I load http://localhost:3000/, this is what renders on the screen. Notice the "A" is being passed into the function to generate the string "A. Tester" for the output.
Express
Welcome to Express
Hello A. Tester
There are lots of templating engines for node, maybe you should try one of these. If you are searching for a web application framework express would be a good starting point, which has support for many templating enignes.
Of course you could do just string replacement, but a templating engine provides much more.
Related
hello first thank you for your help to my first question in stackoverflow.
my qeustion is "how to use render's second parameter 'locals' on script in pug file".
in index.js...
router.get('/', function(req, res, next) {
Movie.find({}).then((movies)=>{
res.render('index', {movies:movies});
}).catch((err)=>{
console.log(err);
});
};
in index.pug...
extends layout
block content
ul
each movie in movies
li
a(href='/edit/'+movie._id) #{movie.title}
script(type='text/javascript').
console.log(movie)
and the console.log output is "uncatched reference error: movie is not defiend".
on "a(href='/edit/'+movie._id) #{movie.title}" the movie is working but,
on "console.log(movie)" it's not.
i think scope is not matter and the declaration also no problem.
what is problem? and how can i use the locals on script in pug file?
one more time, really thank you for your help!!
Of course it's undefined. Understand what is happening :
Pug is rendering an HTML file. Meaning, it's a template, that you fill with values. Pug is going to "bake" all this and output an HTML file. Good.
This file contains a line of code, as text : console.log(movie). Very well.
Now, this HTML file is sent to the browser, where it is parsed, an the code executed.
The browser starts executing the code, sees console.log(movie) and says "What the heck is movie?" Crash : movie is undefined.
This is the difference between server-side and client-side environments. Your file is being created on the server, but executed in a browser, a totally different environment in a totally different machine. There's no way movie is defined there.
Imagine your user writes a post and includes a hyperlink or two somewhere within the text. The whole thing is saved as a string in the database. To render the content and make the links clickable, one would do the following:
const urlRegex = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/ig;
let content = userInput = "Hey everybody, look at this cool site I found: http://www.cool-site.com"
let url = content.match(urlRegex);
.postContents !{content.replace(url[0], `${url[0]}`)}
The marker !{ } makes sure that Pug treats html tags as what they are. However, this approach is not practical. A malicious user could easily post Click me! and Pug would take this string and render it because the content is being unescaped. If I escape the content, on the other hand, the recursively added tag blahblah will be treated as text and no links will be clickable. One solution I thought about is to decompose the whole string into an array of words to later recompose it again using #{ } for non-urls and !{ } for urls, although it seems rather complicated for a problem many other developers might have encountered already.
Note: I know the code is not looping through all the matched urls. I will figure that out later. My problem now is to find a way to "urlify" the text in a safe way.
You could leverage an existing package like linkify-urls, and pass that through to Pug to be able to use during compilation:
const linkifyUrls = require('linkify-urls');
app.get('/', function(req, res, next) {
res.render('index', { linkifyUrls });
});
.postContents!= linkifyUrls(userInput)
Is there any way to redirect to an HTML file from a Node.JS application with something like: res.sendFile of express and pass a JSON data along to the html file?
I know this is late but I wanted to offer a solution which no one else has provided. This solution allows a file to be streamed to the response while still allowing you to modify the contents without needing a templating engine or buffering the entire file into memory.
Skip to the bottom if you don't care about "why"
Let me first describe why res.sendFile is so desirable for those who don't know. Since Node is single threaded, it works by performing lots and lots of very small tasks in succession - this includes reading from the file system and replying to an http request. At no point in time does Node just stop what it's doing and read an entire from the file system. It will read a little, do something else, read a little more, do something else. The same goes for replying to an http request and most other operations in Node (unless you explicitly use the sync version of an operation - such as readFileSync - don't do that if you can help it, seriously, don't - it's selfish).
Consider a scenario where 10 users make a request for for the same file. The inefficient thing to do would be to load the entire file into memory and then send the file using res.send(). Even though it's the same file, the file would be loaded into memory 10 separate times before being sent to the browser. The garbage collector would then need to clean up this mess after each request. The code would be innocently written like this:
app.use('/index.html', (req, res) => {
fs.readFile('../public/index.html', (err, data) => {
res.send(data.toString());
});
});
That seems right, and it works, but it's terribly inefficient. Since we know that Node does things in small chunks, the best thing to do would be to send the small chunks of data to the browser as they are being read from the file system. The chunks are never stored in memory and your server can now handle orders of magnitude more traffic. This concept is called streaming, and it's what res.sendFile does - it streams the file directly to the user from the file system and keeps the memory free for more important things. Here's how it looks if you were to do it manually:
app.use('/index.html', (req, res) => {
fs.createReadStream('../public/index.html')
.pipe(res);
});
Solution
If you would like to continue streaming a file to the user while making slight modifications to it, then this solution is for you. Please note, this is not a replacement for a templating engine but should rather be used to make small changes to a file as it is being streamed. The code below will append a small script tag with data to the body of an HTML page. It also shows how to prepend or append content to an http response stream:
NOTE: as mentioned in the comments, the original solution could have an edge case where this would fail. For fix this, I have added the new-line package to ensure data chunks are emitted at new lines.
const Transform = require('stream').Transform;
const parser = new Transform();
const newLineStream = require('new-line');
parser._transform = function(data, encoding, done) {
let str = data.toString();
str = str.replace('<html>', '<!-- Begin stream -->\n<html>');
str = str.replace('</body>', '<script>var data = {"foo": "bar"};</script>\n</body>\n<!-- End stream -->');
this.push(str);
done();
};
// app creation code removed for brevity
app.use('/index.html', (req, res) => {
fs
.createReadStream('../public/index.html')
.pipe(newLineStream())
.pipe(parser)
.pipe(res);
});
You get one response from a given request. You can either combine multiple things into one response or require the client to make separate requests to get separate things.
If what you're trying to do is to take an HTML file and modify it by inserting some JSON into it, then you can't use just res.sendFile() because that just reads a file from disk or cache and directly streams it as the response, offering no opportunity to modify it.
The more common way of doing this is to use a template system that lets you insert things into an HTML file (usually replacing special tags with your own data). There are literally hundreds of template systems and many that support node.js. Common choices for node.js are Jade (Pug), Handlebars, Ember, Dust, EJS, Mustache.
Or, if you really wanted to do so, you could read the HTML file into memory, use some sort of .replace() operation on it to insert your own data and then res.send() the resulting changed file.
Well, it's kinda old, but I didn't see any sufficient answer, except for "why not". You DO have way to pass parameters IN static file. And that's quite easy. Consider following code on your origin (using express):
let data = fs.readFileSync('yourPage.html', 'utf8');
if(data)
res.send(data.replace('param1Place','uniqueData'));
//else - 404
Now for example, just set a cookie, in yourPage.html, something like:
<script>
var date = new Date();
document.cookie = "yourCookieName='param1Place';" +
date.setTime(date.getTime() + 3600) + ";path=/";
</script>
And you can plainly pull content of uniqueData from yourCookieName wherever you want in your js
I think the answer posted by Ryan Wheale is the best solution if you actually want to modify something within an HTML file. You could also use cheerio for working with complex logic.
But in regards to this particular question where we just want to pass some data to the client from the server, there's actually no need to read index.html into memory at all.
You can simply add the following script tag somewhere at the top of your HTML file:
<script src="data.js"></script>
And then let Express serve that file with whatever data needed:
app.get("/data.js", function (req, res) {
res.send('window.SERVER_DATA={"some":"thing"}');
});
This data can then easily be referenced anywhere in your client application using the window object as: window.SERVER_DATA.some
Additional context for a React frontend:
This approach is especially useful during development if your client and server are running on different ports such as in the case of create-react-app because the proxied server can always respond to the request for data.js but when you're inserting something into index.html using Express then you always need to have your production build of index.html ready before inserting any data into it.
Why not just read the file, apply transformations and then set up the route in the callback?
fs.readFile(appPath, (err, html) => {
let htmlPlusData = html.toString().replace("DATA", JSON.stringify(data));
app.get('/', (req, res) => {
res.send(htmlPlusData);
});
});
Note that you can't dynamically change data, you'd have to restart the node instance.
You only have one response you can return from the server. The most common thing to do would be to template your file on the server with nunjucks or jade. Another choice is to render the file on the client and then to use javascript to make an ajax call to the server to get additional data. I suppose you could also set some data in a cookie and then read that on the client side via javascript as well.
(Unless you want to template the html file to insert the json data into a script tag). You'll need to expose an api endpoint in express the send along the data to the page, and have a function on the page to access it. for example,
// send the html
app.get('/', (req, res) => res.sendFile('index'));
// send json data
app.get('/data', (req, res) => res.json(data));
Now on the client side you can create a request to access this endpoint
function get() {
return new Promise((resolve, reject) => {
var req = new XMLHttpRequest();
req.open('GET', '/data');
req.onload = () => resolve(req.response);
});
}
// then to get the data, call the function
get().then((data) => {
var parsed = JSON.parse(data);
// do something with the data
});
EDIT:
So arrow functions probably don't work client side yet. make sure to replace them with function(){} in your real code
This is pretty easy to do using cookies. Simply do this:
On the server side -
response.append('Set-Cookie', 'LandingPage=' + landingPageCode);
response.sendFile(__dirname + '/mobileapps.html');
On client side -
<!DOCTYPE html>
<html>
<body onload="showDeferredLandingPageCode()">
<h2>Universal Link Mobile Apps Page</h2>
<p>This html page is used to demostrate deferred deeplinking with iOS</p>
</body>
<script language="javascript">
function showDeferredLandingPageCode() {
alert(document.cookie);
}
</script>
</html>
This is a multi part question and I'm a complete newbie to Node so please be gentle:)
I have a very simple Node/express app set up returning an index.html without using routing...
var app = express();
var port = process.env.PORT || 1337;
app.use('/i', express.static(__dirname + '/i'));
app.use('/Stylesheets', express.static(__dirname + '/Stylesheets'));
app.use(express.static(__dirname));
app.listen(port);
console.log('listening on port ' + port);
The index.html is served as a static file.
My next job is to start returning a few pages with proper routing, I've got as far as working out I need to put my routes in a routes.js file and "require" that file in my server.js file but I can't get my head around setting the routes up and every example/demo I see online seems to do it a different way. Any definitive examples of how to do this would really be appreciated.
The next part of the problem is that I want to include dynamic pages but don't know where to go with templating engines. I would like to use something "unobtrusive" so that my original HTML files still make sense when viewed in a browser.
On the front-end I would simply inject HTML into the page by first using a selector and then using the .html() method to alter the html, I could bind JSON data with a template and then inject it into the right place by looking for a classname etc. THis would be totally unobtrusive and wouldn't require any ugly {} brackets, inline javascript or directives. Psuedo code...
var data = {"name":"John"};
var result = templateEngine.bind("/template.html", data)
$('.person').html(result);
That way, I could keep my original HTML clean and viewable, like this...
<div class="person">
My Name is FirstName
</div>
The closest thing I can find is PURE - http://beebole.com/pure - but I'm not sure how to get it working with NODE (or even if it's compatible).
To add more complexity, whatever templating engine I use needs to be able to use sub-templates(partials?) so that I can include a header/footer etc which is te same on every page. I assume this can be done recursively by referencing sub-templates from within each main template where needed?
If you're still reading this then clearly you'll have worked out that I'm floundering here with a new technology and any help would be really appreciated!
but I can't get my head around setting the routes up and every
example/demo I see online seems to do it a different way. Any
definitive examples of how to do this would really be appreciated.
Not sure what you have seen different in the examples, but the general pattern is like this:
app.**[HTTP VERB]**(**[URL]**, function(req, res){
res.end('<html>hello world</html>');
});
The following code will accept all HTTP GET requests to the root URL of your site:
app.get('/', function(req, res){
res.end('<html>hello world</html>');
});
While the following code will accept all HTTP GET request to /test in your site
app.get('/test', function(req, res){
res.end('<html>hello world from the test folder</html>');
});
It's common to have a separate route for HTTP POST requests (e.g. when the user submits data back to the server). In this case the HTTP verb is POST as in the following example.
app.post('/test', function(req, res){
res.end('<html>Thanks for submitting your info</html>');
});
In this case I am embedding the code to handle the request directly rather than referencing an external routes.js as you indicated just to make the examples cleaner in this question. In a real application you'll do it by referencing an external function so that your app.js stays lean and clean.
app.get('/test', routes.NameOfFunctionToHandleGetForSlashTest);
app.post('/test', routes.NameOfFunctionToHandlePostForSlashTest);
I know this is an old question, but I have explored this topic recently, and came up with the following solution:
my original question
I have placed the following configuration on ejs:
var ejs = require('ejs');
server.configure(function(){
server.set("view options", {layout: false});
server.engine('html', require('ejs').renderFile);
server.use(server.router);
server.set('view engine', 'html');
server.set('views', __dirname + "/www");
});
This sets your view engine as ejs, your view directory as your static public html directory and tells ejs to process .html files as opposed to .ejs.
The routes can be handles like this:
server.all("*", function(req, res, next) {
var request = req.params[0];
if((request.substr(0, 1) === "/")&&(request.substr(request.length - 4) === "html")) {
request = request.substr(1);
res.render(request);
} else {
next();
}
});
server.use(express.static(__dirname + '/www'));
This routes all html requests through the view engine, and passes all other requests on down the stack to be sent as static files.
Your html can now look something like:
<%include head.html%>
<%include header.html%>
<p class="well">Hello world!</p>
<%include footer.html%>
you can have nested includes, and pass variables down into your includes. So for instance your include head can call:
<title> <%= title %> </title>
and at the top of your index page you can include an object like:
{var title: "Home"}
Anyway, maybe this will help out someone who is looking for a ultra easy way to handle includes while sticking with normal html.
I'm currently creating my new website with express. The page is split up in different "sections" (for example a blog, information about my project and so on).
I'd like a have a "sidebare" next to my content thats different for each section. For example in the "blog"-Section id like to have a list of my tags or categorys and in the project section there should be a list of all projects. The content depends on the section and is the same for every "subpart" of this section.
One solution would be to create a function that creates the sidebars content and call this function in every function that handles a route. But I don't like this solution because it doesn't seems to "be the right one"...
Does anyone have a nice solution for this?
If I understand your question correctly, you want to know how to render multiple 'sections' using the same overall template, yes? Each section could differ depending on the content on the page and the main URL.
I can suggest two ways:
1) IMHO the right way - Define a base template (say index.jade if you're using jade/express) and you would create routes for say a homepage (/) and a blog page (/blog) using that:
# Home Page
app.get '/', (req, res) ->
# Do some simple DB transactions, etc, and get the main area content.
res.render 'index.jade', { json: json }
# Blog Page
app.get '/blog/:id', (req, res) ->
blog.grabPost req.params.id, (json) -> # Grab content of the blog post from the DB
res.render 'index.jade', { json: json }
Next, create a few 'partial' URL's for the remainder of the content:
# Sidebar Partial
app.get '/sidebar/:page/:id', (req, res) ->
sidebar.grabContent req.params.id, (json) ->
res.render '{#req.params.page}.jade', { json: json }
You would then call the partial client-side upon pageload via JQuery like so:
$ ->
$('.sidebar').load '/sidebar/blog/365' + id, (response, status, xhr) ->
2) Using Express 'view partials' which I'm much less familiar with. Documentation can be found here: http://expressjs.com/guide.html#view-partials
From the description: "The Express view system has built-in support for partials and collections, which are “mini” views representing a document fragment. For example rather than iterating in a view to display comments, we could use partial collection:"