I'm using the handlebars.js hbs wrapper in express.js. I have templates working fine, but I'm needing to add in partials to be rendered with my views.
I'd like to do something like this:
hbs.registerPartial('headPartial', 'header');
// where "header" is an .hbs file in my views folder
However, it's throwing a "header partial can not be found".
I can make the registerPartial work if I pass a string of html to the second param, but I'd like to use separate view files for my partials.
I haven't found any documentation on this, but hoping I may just be missing something easy.
Does anyone know how to use view files in the registerPartial method? If so, how do I implement this?
UPDATE
To give more context, let me add more code.
Here is my "server" file - app.js
var express = require('express')
, routes = require('./routes')
, hbs = require('hbs');
var app = module.exports = express.createServer();
// Configuration
app.configure(function(){
app.set('views', __dirname + '/views');
app.set('view engine', 'hbs');
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
app.configure('development', function(){
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
app.use(express.errorHandler());
});
// this is the line that generates the error
hbs.registerPartial('headPartial', 'header');
// What I'm expecting is for "headPartial" to be a compiled template partial
// of the template within views/header.hbs, but it is not loading this way.
// If I do something like hbs.registerPartial('headPartial', '<p>test</p>');
// then it does work. I need to know how to pass an .hbs file to the
// registerPartial method.
// Routes
app.get('/', routes.index);
app.listen(3000);
And here is my routes.index file:
exports.index = function(req, res){
res.render('index', { title: 'Express' })
};
In my views folder, I have three templates:
views/
header.hbs (this is my partial)
index.hbs
layout.hbs
In my index.hbs file, I'm calling the 'headPartial' partial with:
{{> headPartial}}
Any help is greatly appreciated.
For convenience, registerPartials provides a quick way to load all partials from a specific directory:
var hbs = require('hbs');
hbs.registerPartials(__dirname + '/views/partials');
Partials that are loaded from a directory are named based on their filename, where spaces and hyphens are replaced with an underscore character:
template.html -> {{> template}}
template 2.html -> {{> template_2}}
login view.hbs -> {{> login_view}}
template-file.html -> {{> template_file}}
Cheers!
This code loads all the partial templates in a directory and makes them available by filename:
var hbs = require('hbs');
var fs = require('fs');
var partialsDir = __dirname + '/../views/partials';
var filenames = fs.readdirSync(partialsDir);
filenames.forEach(function (filename) {
var matches = /^([^.]+).hbs$/.exec(filename);
if (!matches) {
return;
}
var name = matches[1];
var template = fs.readFileSync(partialsDir + '/' + filename, 'utf8');
hbs.registerPartial(name, template);
});
Looks like creating a variable and pulling in the template code manually works:
var hbs = require('hbs')
, fs = require('fs')
, headerTemplate = fs.readFileSync(__dirname + '/views/_header.hbs', 'utf8');
and later:
hbs.registerPartial('headPartial', headerTemplate);
For me I had template file my-partial.hbs
Then I tried to access them via:
{{> my-partial }}
But the partial was stored in hbs as my_partial regardless of the filename.
This is thanks to hbs version 3.1.0 line 218
.slice(0, -(ext.length)).replace(/[ -]/g, '_').replace('\\', '/');
This is in the readme
For me, I have a function like:
var hbs = require('hbs');
var fs = require('fs');
var statupfunc = {
registerHbsPartials : function(){
//this is run when app start
hbs.registerPartials(__dirname + "/" + resource.src.views + '/partials');
},
registerOneHbsPartials : function(event){
//this is run for gulp watch
if(event.type == 'deleted')
{
return;
}
var filename = event.path;
var matches = /^.*\\(.+?)\.hbs$/.exec(filename);
if (!matches) {
return;
}
var name = matches[1];
var template = fs.readFileSync(filename, 'utf8');
hbs.registerPartial(name, template);
}
};
Run statupfunc.registerHbsPartials at app startup and then register gulp watch with statupfunc.registerOneHbsPartials to register partials on new creation
gulp.task('watch', function() {
gulp.watch(resource.src.views + '/partials/*.*', statupfunc.registerOneHbsPartials);
});
My app structure (using ExpressJS & HBS-NPM) is:
APP FOLDER
-src
app.js
- routers
- views
-- partials
header.hbs
const hbs = require('hbs')
hbs.registerPartials(path.join(__dirname,'views','partials'))
The above does the job of loading all partials in a single shot. Use this as long as you believe it doesn't impact your performance (loading all partials, even if not required).
And then, use this partial in any HBS file as follows:
{{> partial_file_name_without_hbs_extension}}
Example
{{> header}
Related
I'm getting ReferenceError: main is not defined
when I open up the http://localhost:3000/
Here I try to open up the main.html located in views directory
This is my app.js
const express = require('express'),
app = express(),
config = require('./config/index'),
routes = require('./routes/route');
app.use(express.static(`${__dirname}/public`));
app.use(express.static(`${__dirname}/views`));
app.use('/',routes);
app.listen(config.port,()=>{
console.log(`Listing at port ${config.port}`)})
This is my route.js
const express = require('express'),
router = express.Router(),
helpers = require('../helpers/index');
router.route('/')
.get(helpers.index)
module.exports = router
This is my helpers/index.js
var user = require('../user/user');
exports.index = (req,res)=>{
if(user.name == ''){
res.sendFile(main.html);
}
else{
res.sendFile(chat.html)
}
}
module.exports = exports;
Directory Structure
>helpers
>index.js
>routes
>route.js
>user
>user.js
>views
>main.html
>chat.html
app.js
pacakage.json
Change:
res.sendFile(main.html);
to:
res.sendFile("main.html");
Without the quotes, it's trying to interpret main as a Javascript object which it looks for the .html property on. But, there is apparently no object named main so you get ReferenceError: main is not defined. You want to pass a string here instead.
Same for res.sendFile("chat.html");
If the files are not local to this module's directory, then you need to build a more complete path that specifies their location. Given the file hierarchy you show, I think that might be something like this:
const path = require('path');
const options = {root: path.join(__dirname, "../views")};
res.sendFile("main.html", options);
var user = require('../user/user');
var path = require('path');
exports.index = (req,res)=>{
if(user.name == ''){
res.sendFile(path.resolve('views/main.html'));
}
else{
res.sendFile(path.resolve('views/chat.html'))
}
}
module.exports = exports;
In addition to jfriend00's answer, you must also, build the correct absolute path, using the global __dirname variable in node.
So your path will be something like: res.sendFile(__dirname + "/main.html")
or, depending on your folder structure: res.sendFile(__dirname + "/someadditionalfoldrs/main.html")
or, construct the path using "./" if applicable, like "./main.html";
I am using node.js to launch a serve so that my html can communicate with R code. But I am facing a problem on node.js. In my html page, I have a browse selection button, user can use it to choose the data file they want to read into html and node.js will pass the file name to R, so R code will read data from the selected data file and then run the analytics model. But as i only have very basic knowledge of Node.js, so currently, r code would run only when I open the followling link "localhost:3000/vis/rio". So my question is how to make node.js run the R code in background automatically when the data file has been selected. Thank you very much for your help in advance.
Here are my codes:
Javascript-(sending the file name to node.js):
var dataset,availableTags;
function handleFileSelect(evt) {
var file = evt.target.files[0];
$.ajax({ //getting the file name and update to node.js
type:'post',
url:"/getFileName",
data:{filename:file.name}
});
Papa.parse(file, { //papa is a library I used to read csv file
header: true,
dynamicTyping: true,
complete: function(results) {
dataset=results.data;
}
});
}
$(document).ready(function(){
$("#csv-file").change(handleFileSelect);
});
Node.js script:
serve.js:
var express=require('express');
var path = require('path');
var vis = require('./routes/vis');
var index = require('./routes/index');
var bodyParser=require('body-parser');
var app=express();
require('./routes/main')(app);
app.get('/vis/rio',vis.rio); **//this is a package used to get connection with Rserve**
app.set('views',__dirname + '/views');
app.set('view engine', 'ejs');
app.engine('html', require('ejs').renderFile);
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.urlencoded());
app.post("/getFileName",index.getFileName); **//this is the script to get the file name, it is from index.js**
var server=app.listen(3000,function(){
console.log("Express is running on port 3000");
});
index.js // this is the js file for getting file name
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res) {
res.render('index', { title: 'Express' });
});
getFileName=function (req,res){
global.filename=req.body.filename; **//this is the global variable which stores the file name**
res.send('true');
};
module.exports = {router:router,getFileName:getFileName};
vis.js // this is the file used to connect with Rserve and pass the name to R code
var rio = require("rio");
var arg={};
exports.rio = function(req, res){
arg={names:[global.filename]};
console.log(arg);
options = {
entryPoint:"nameoffile",
data: arg,
host : "127.0.0.1",
port : 6311,
callback: function (err, val) {
if (!err) {
console.log("RETURN:"+val);
return res.send({'success':true,'res':val});
} else {
console.log("ERROR:Rserve call failed")
return res.send({'success':false});
}
},
}
rio.enableDebug(true);
rio.sourceAndEval(__dirname + "/Rcode/test.R",options);
};
It looks like you aren't calling out to /vis/rio at any point when you make the call out to your server.
You'll either need to make a second call on the client side to /vis/rio or if you want to use that section, you can import/require the module in index.js and include it in getFileName and just call out to the function there before you return the file. I'm not super familiar with rio, but I don't see any access point in your code to that function.
Edit: If I understand what you're trying to do correctly, you can always make a second request (to /vis/rio) in the success callback of your first ajax call.
I am new to nodejs. I want to create a very simple website which has 3 pages. In every page I want to display an image to make the pages look uniform.
My code looks like this:
/**
* Module dependencies.
*/
var express = require('express');
var routes = require('./routes');
var user = require('./routes/user');
var http = require('http');
var path = require('path');
var fs = require('fs');
var mail = require("nodemailer").mail;
/*List of variables*/
var app = express();
// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
app.get('/main', function(req, res) {
fs.readFile('./home.html', function(error, content) {
if (error) {
res.writeHead(500);
res.end();
}
else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(content, 'utf-8');
}
});
});
/* After this I have code for post event - all these navigation works perfectly fine*/
in home.html file I have an image to display:
/*Header part of HTML file*/
<body>
<img class="logo" src="./Heading.png" alt="My_Logo">
console.log (src);
<h1 class="center">Welcome to message reprocessing</h1>
</br>
</body>
This image is not displayed in my browser. When I check my console I have this error:
GET http://localhost:3000/Heading.png 404 (Not Found)
Please help, thanks
First, you have to set the public (folder) as static under server.js file
server.js
// configure our application
const Express = require('express');
const app = new Express();
.......
.......
app.use(Express.static(__dirname+'/public'));
.......
then your img source will be
<img class="logo" src="/images/Heading.png" alt="My_Logo">
and here the folders structure would be -
project(root folder) \ public \ images\ Heading.png
Thanks for your answers.I had tried using absolute path already with no luck. However one of my friend suggested as below:
<img class="logo" src="http://localhost:3000/images/Heading.png" alt="My_Logo">
and keep the Heading.png file in public/images directory.
Thanks all.
Okay, its not with express.js, but here is a snippet of my code that handles images and external javascripts, but has a protection:
function handler (req, res){
var pathname = url.parse(req.url).pathname;
var isImage = 0, contentType, fileToLoad;
var extension = pathname.split('.').pop();
var file = "." + pathname;
var dirs = pathname.split('/');
if(pathname == "/"){
file = "index.html";
contentType = 'text/html';
isImage = 2;
}
else if(dirs[1] != "hidden" && pathname != "/app.js"){
switch(extension){
case "jpg":
contentType = 'image/jpg';
isImage = 1;
break;
case "png":
contentType = 'image/png';
isImage = 1;
break;
case "js":
contentType = 'text/javascript';
isImage = 2;
break;
case "css":
contentType = 'text/css';
isImage = 2;
break;
case "html":
contentType = 'text/html';
isImage = 2;
break;
}
}
if(isImage == 1){
fileToLoad = fs.readFileSync(file);
res.writeHead(200, {'Content-Type': contentType });
res.end(fileToLoad, 'binary');
}
else if(isImage == 2){
fileToLoad = fs.readFileSync(file, "utf8");
res.writeHead(200, {'Content-Type': contentType });
res.write(fileToLoad);
res.end();
}
This assuming that you have your app.js and index.html in your root, you can have folders like /css, /img, /js etc. But the app.js and content inside your /hidden folder is unable to reach, but files in your root is still accessable.
If you want a quick solution, do this: instead of this <img class="logo" src="./Heading.png" alt="My_Logo"> use:
<img class="logo" src="your file path /Heading.png" alt="My_Logo">
Still not working?
check image spelling, file extension(png) because png and PNG are different in live server(case sensitive).
and verify
path.join(__dirname, 'public')
too
Index.js
Inside this file, add the code below to give the directory name to your index.js (server), in a way it is based on your actual directory path.
app.use(express.static(path.join(__dirname, "/public")));
Directory Structure
now inside Index.ejs (you HTML page) add the code below to insert your image and make it visible.
<div id="main-content">
<img src="img\navthome" />
</div>
Notice inside IMG tag we set the SRC as "img\navthome.gif", here we don't need to declare all the path since we already declared the __dirname, and the PUBLIC folder inside index.js
I hope it can helps you with your issue :D
One of the problems I had was that I wanted to define a few specific paths and I found the best answer in the link below.
https://www.geeksforgeeks.org/how-to-fetch-images-from-node-js-server/
// Requiring module
const express = require('express');
// Creating express object
const app = express();
// Defining port number
const PORT = 3000;
// Function to serve all static files
// inside public directory.
app.use(express.static('public'));
app.use('/images', express.static('images'));
// Server setup
app.listen(PORT, () => {
console.log(`Running server on PORT ${PORT}...`);
})
I did the following in my program:
Shows the structure of folders and files here
root
> client
> node_modules
> folder_1
> folder_2
> package.json
> server.js
And on my server I define that folder_1 in fl1 path and folder_2 in fl2 path each have a separate static path. And the interesting thing is that I do not use the original name of those folders and I give it my own route.
// Requiring module
const express = require('express');
const path = require('path');
// Creating express object
const app = express();
// http://localhost:3000/fl1/...
app.use('/fl1',express.static(path.resolve(__dirname,'folder_1')));
// http://localhost:3000/fl2/...
app.use('/fl2',express.static(path.resolve(__dirname,'folder_2')));
// Server setup
app.listen(3000, () => {
console.log(`Running server on port 3000`);
})
After running the server to access this path is enough to use the following path to access its contents.
Note that you must specify the internal routes accurately. For example, the subfolders and file name must be specified. main path /folder_1/img/image_1.png in url /fl1/img/image.png.
I'm working on a project, which will use given coordinates from a txt file and graph them.
My problem right now: I'm trying to use ejs to render the coordinates into my html file, but it just isn't working right. Ejs always just renders: undefined.
Here is the code:
var http = require('http'),
fs = require('fs'),
express = require('express'),
app = express();
app.use(express.bodyParser());
app.set('view engine', 'ejs');
app.engine('html', require('ejs').renderFile);
app.use(express.static(__dirname + '/public'));
//Readerfunction
function readLines(input, done) {
//.....
function done(arr) {
var obj = {};
var key1 = arr[0][0];
var key2 = arr[0][1];
obj[key1] = [];
obj[key2] = [];
arr.shift();
arr.forEach(function (item) {
obj[key1].push(item[0]);
obj[key2].push(item[1]);
});
console.log('X:', obj[key1]); // all the variables are logged correctly.
console.log('Y:', obj[key2]);
app.get('/', function(req, res) {
res.render('graph.html', {cordinates: obj});
});
app.listen(8080, function() {
console.log('Server running at http://127.0.0.1:8080/');
});
}
In the html file:
<%= cordinates.obj %>
I hope you can help me, solve this problem! :-)
Greetings,
JS
Well, I think here is the problem: you are passing obj to render as coordinates, so you should use coordinates variable in your template directly, no it's obj property, which is non-existent. I'm not sure if it will be rendered as proper array though, maybe you'll need a loop to print it's elements.
I'm really a fan of the simplicity PHP offers in serving pages, everything is based on the filesystem. I want to do the same thing with Node. I tried one routing setup that worked like this for views, but broke my public folder:
//using express:
app.get('*', function(req, res) {
file = req.params[0].substr(1, req.params[0].length);
console.log('requesting: ' + file);
res.render(file, {locals: {
req: req,
params: req.query
}});
});
So...What's the best way to setup filesystem based/php style routing in Node?
I think that I build exactly what you are looking for. I use this to serve up .jade files, obviously you can tweak this for your use case.
var url = require('url');
var express = require('express');
var app = express.createServer();
var fs = require('fs');
app.set("view engine", "jade");
app.use(app.router);
app.use(express.static(__dirname + '/public'));
/**
* Generic "get" attempts to route to known JADE files.
* If no known JADE files, then we pass routing to next() (should be static).
*/
app.get('*', function(req, res, next) {
var pathname = url.parse(req.url).pathname.toLowerCase(); // make matching case insenstive
// First case: with no path name, render the default index.jade
if(!pathname) {
res.render('index', {});
}
// Second case: path ending in '/' points to a folder, use index.jade from that folder
else if (pathname === '/' || pathname.charAt(pathname.length-1) === '/' ){
res.render(__dirname + '/views' + pathname + 'index.jade', {});
}
// Third case: looks like an actual file, attempt to render
else {
// Attempt to find the referenced jade file and render that. Note 'views' is default path.
fs.stat( (__dirname + "/views" + pathname + '.jade'), function(err, stats){
// There was an error, the file does not exist pass control to the static handler
if(err || !stats) {
next();
}
// We found the file, render it.
else{
res.render(pathname.substring(1), {});
}
});
}
});
app.listen(port);
Note, there should be more app.use() statements there for handling cookies, parsing the body etc. Also, the second param of the render is always empty. You may want to fill this out with things like {layout: xyz} or generic variables that need to come in to the rendered pages.
You can use express.static()
For examples:
app.configure(function(){
app.use(express.static(__dirname + '/public'));
});
app.configure(function(){
app.use('/uploads', express.static(PATH_TO_UPLOAD_FOLDER));
});