pug : TypeError: View is not a constructor - node.js

I'm trying to make a website using NodeJS with express and pug for templates.
I integrated Pug using the express docs, but I keep getting TypeError: View is not a constructor on the render function. I really looked, but the only people who had this problem got it with other templates, and were not doing it has simple.
So at the end I did it like this :
var express = require('express');
var server = express();
server.set('views', './views');
server.set('view engine', 'pug');
server.get('/', (req, res) => {
res.render('example', {title: "code", message: "message"});
});
require('./settings')(server); console.log('Settings initialized');
server.listen(server.settings.port);
console.log('Server listening on port', server.settings.port);
And in views/ the template looks like :
html
head
title = title
body
h1 = message
In package.json, I imported it like : "pug": "^2.0.0-rc.1"
Thank you for your tips ! And have a nice day.
-- EDIT --
So I made a little experiment by uninstalling pug ... I have the same result, so I guess I didn't install or parametrized well pug.
-- EDIT --
Made some other tests and it turns out without the require it works well. But I don't see why, and I need to import external files.

This could be happen for various reason one of the most frequent is that there is a typo in the way you use pug in your index.js or where the object is instanced.
After made sure to have installed it locally( check your package.json) and to have created a root a folder called "views", and in there same file that you use in your rooter, then to be sure to use the sintax:
server.set('view engine', 'pug');
server.set('views','./views');// default, but if you specify don't make mistake on this
In your case I believe it is the order that caused this error.
Another note, useful is that the pug sintax, is required that "tags" are followed without space by "=", like:
html
head
title= title
body
h1= message

I had the same error because of a typo in views
server.set('view', './views');
server.set('view engine', 'pug');
I fixed it by putting an s to views
server.set('views', './views');

(this discussion assumes app is express: var app = express();)
You're getting this error because you're stepping on app.settings, which contains a view property.
I understand the appeal of wanting to call app.settings, so you can use app.settings.custom instead, and it won't cause a clash:
settings.ts
module.exports = {
port: 3000
}
app.ts
// settings
app.settings.custom = require('./settings')
...
var server = await app.listen(app.settings.custom.port, async () => {
var address = server.address()
console.log(`app.js - server started at ${new Date().toLocaleTimeString()} in ${app.environment} - http://${address.address}:${address.port}`)
})
If you don't like .custom, you can use Object.assign(app.settings, mySettings), just make sure no property values clash.

Related

How do I get HAML layouts working with haml-coffee and Express 4?

I followed "NodeJS Express app generation with CoffeeScript and HAML" and the express-usage docs, but my layout is not displaying.
I can see the rendered index.hamlc OK, but it doesn't contain the layout. How do I get layout to work with HAML and Express 4?
server.js:
const express = require('express'),
partials = require("express-partials"),
...
app.set('views','app/views');
app.engine("hamlc", require("haml-coffee").__express)
app.use(partials())
app.set("view engine", "hamlc")
index.js:
router.get('/', (req, res) => {
res.render('index', {name: "User"})
app/views/layout.hamlc:
!!!
%head
%title Express App
%body
xxx
!= #body
I viewed the source in Chrome using view-source:http://127.0.0.1/ and it only shows the template contents but not the layout.
I moved the line
app.use('/', indexRouter)
below
app.engine("haml", require("haml-coffee").__express)
app.use(partials())
app.set("view engine", "haml")
and it worked. I didn't think the order would matter. (I also renamed the files .haml to match conventions and enable syntax hilighting.)

Options found in locals object in ejs

I got the following error when running the project from GitHub: "options found in locals object. The option(s) is copied to the option object. This behavior is deprecated and will be removed in EJS 3"
I tried to update the ejs and express modules to the newest versions but the notice persists. I googled, ofc, and the only thread about it is this, but it doesn't help.
Does anyone know more about this?
For reference, here is the whole important code:
app/views/index.ejs
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<h1><%= title %></h1>
<img src="img/logo.jpg" alt="Hack Hands logo">
</body>
</html>
app/controllers/index.server.controller.js
exports.render = function(req, res) {
res.render('index', {
title: 'MEAN MVC'
});
};
app/routes/index.server.route.js
module.exports = function(app) {
var index = require('../controllers/index.server.controller');
app.get('/', index.render);
};
app/config/express.js
var express = require('express');
module.exports = function() {
var app = express();
app.set('views', './app/views');
app.set('view engine', 'ejs');
require('../app/routes/index.server.routes.js')(app);
app.use(express.static('./public'));
return app;
};
server.js
var port = 1337;
var express = require('./config/express');
var app = express();
app.listen(port);
module.exports = app;
console.log('Server running at http://localhost:' + port);
tl;dr: Upgrade to the latest version of EJS. It removes all warnings about options and locals.
whoami
I'm a collaborator (or the collaborator in #micnic's comment above) in EJS v2. I only started maintaining EJS after version 2.0.3 (or something like that) was released, so I don't know a lot about how the API changes took place.
History
EJS v2's renderFile function, used by Express.js, now has the signature
function (path[, options[, locals]], cb)
But for compatibility with and Express.js, which calls all functions as
function (path, locals, cb)
with options mixed into the locals object, EJS automatically picks out the locals with option-y names and treat them as options.
But because the Express.js signature is also the function signature of EJS v1, we also print a warning if any option in locals is copied to options, urging developers to use the new signature with locals and options separated (it was actually me who added the warning).
However, Express.js users do not have a choice in terms of calling convention, so the warning is always present in Express.js.
Some users did complain: #34 #36.
At first, #mde (who is the main maintainer of EJS) pushed a fix, which correctly disables warnings on Express.js, and Express.js only.
But then, the person in #36 still complained, as he was using filename as the name of a local, and when the optiony local is copied to options a warning is printed.
At last, #mde was like "f*** this shit" and removed all the deprecation warnings, including an uncontroversial and legitimate one, and released version 2.2.4 (the legitimate warning was restored by me after the release).
Future
#dougwilson (an Express.js maintainer) said he was interested in a separation of options and locals in Express.js v5, just like in EJS v2. I did volunteer to make that change, but then I got busy so yeah.

hogan.js with master pages or layouts

Is it possible in any way to use hogan.js as template engine with layouts something like
"Razor or master pages in .NET"?
I would get a result like this:
layout.hjs:
contains "header" & "footer"
and
index.hjs:
will include layout.hjs and contain only page content.
sure:
layout.hjs:
{{> header}}
{{$content}}
default content
{{/content}}
{{> footer}}
index.hjs:
{{<layout}}
{{$content}}
your content goes here
{{/content}}
{{/layout}}
see the hogan test file for all it can do:
https://github.com/twitter/hogan.js/blob/master/test/index.js
btw. this is Hogan#3.0.0, get it with a git url with mpn
I'm not sure what you mean, "Razor or master pages in .NET"? What are you looking to do, use view partials?
But the basic way of setting up Hogan.js for Express is as follows:
var express = require('express');
var app = express();
app.set('views', __dirname + '/views');
app.set('view engine', 'hjs');
app.use(app.router);
app.use(express.static( __dirname + '/public' ));
app.get('/', function( req, res, next ) {
res.render('index');
});
app.listen(3000);
You will have to npm install express [--save], npm install hjs [--save], depending if it's inside your package.json already or not.
Then you just make a views directory and throw an index.hjs file and you're set.
Let me know what you want to do with your templates and we can work from there.

Express cannot PUT/DELETE method. What is going wrong?

Ok So I have a simple node.js / express.js / mongodb app set up here with my config as follows.
var express = require('express'),
mongoose = require('mongoose');
http = require('http');
var app = express();
app.configure(function(){
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
//middleware stack
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(__dirname + "/public"));
});
mongoose.connect("mongodb://localhost/hello");
The problem lies when I try to make PUT or DELETE requests. My form is this simple
<form method="POST" action="/users/#{user.name}">
<input type="hidden" name="_method" value="PUT"/>
</form>
Now my router catches the route with the express .put() method
app.put('/users/:name', function(req, res) {
var b = req.body;
Users.update(
{ name: req.user.name },
{ name: b.name, age: b.age, email: b.email },
function(err) {
res.redirect('/users/'+b.name);
});
})
When I make the request I simply get a "Cannot PUT" or "Cannot DELETE" error.
I have tried to make this same request via chomes RESTful client with the same result.
I have read a topic witch has the same problem as me although following the comments the answers did not solve my problem.
Questions I have looked into
expressjs support for method delete and put without the methodoverride
Are the PUT, DELETE, HEAD, etc methods available in most web browsers?
Along with a few others.
I have also referenced the express.js and mongo documentation several times.
I just cant think what could be going wrong.
Any help is appreciated.
Update
As Jonathan Lonowski pointed out PUT can also be used, so you can ignore my old answer.
Getting Cannot PUT or Cannot POST errors, means your callback is not executing successfully. My guess is that Users.update is failing, which is why it cannot POST or PUT. Can you check it.
Old answer
Try changing this line
app.put('/users/:name', function(req, res) {
to
app.post('/users/:name', function(req, res) {
since you are trying to submit the form
Is the <form> you listed in a view or a static file under __dirname + "/public"?
Within a static file, the #{user.name} probably isn't being replaced with the user's name and will be treated as a URL Fragment.
The <form> will actually submit to /users/ rather than /users/:name since that's the path:
console.log(url.parse('/users/#{user.name}'));
{ hash: '#{user.name}',
pathname: '/users/',
path: '/users/',
href: '/users/#{user.name}' }
The <form> should be generated from a view if it isn't since the action needs to be dynamic and data-driven. With Jade and assuming user is a member of locals, that would be:
form(method='POST', action='/users/' + user.name)
input(type='hidden', name='_method', value='PUT')
Unless there is strange magic at work, your form makes a POST request, not a PUT. If you want to PUT, I would suggest using the jQuery.ajax function with a type: 'PUT' parameter, like this answer, from a form handler, see jQuery.submit. Don't forget to return false so that the form doesn't submit twice.
If your using method override, make sure you have declared it before you use your routes. That was the problem I was having.
app.post("/movies/:id") is one solution.
If you still want to use app.put("/movies/:id") then try this:
Install method-ovveride from npm.
Require it in your app.js file.
Open the form from where you wanna invoke PUT request
make sure your form has the following attributes:
action="/movies/<%= movies._id %>?_method=PUT " method="POST" >
These two solutions worked for me. If you are following REST, then use the method-ovveride else app.post() will also do the trick
Change res.redirect('path') to res.redirect(303, 'path')
In Put and Delete, if you want to redirect to get address, you should pass 303 as first parameter. (source)
one solution is to use cors middleware for you PUT,PATCH and DELETE requests like this in your app.js file like this:
first install the cors package via npm :
npm i cors
then add the following code to your app.js:
const cors = require('cors')
app.use(cors())

How do I use Node and Express with coffeescript and requirejs?

Here's what I want.
A node application using the express webserver
Using coffeescript on the server and more importantly the client
Using require.js on the client (and eventually on the server)
The recommended way I've been able to find of hooking up coffeescript for the client is to use connect-assets. This seems to require using jade helpers to actually compile coffeescript eg.
!=js('monalisa.js')
seems to compile monalisa.coffee and generate the correct <script> tag. Now I want to use require.js and here I stumble. How do I ensure that connect-assets compiles everything correctly without using the jade helpers?
Here's my fairly simple app.js:
require('coffee-script');
var express = require('express')
, http = require('http')
, path = require('path')
, connectAssets = require('connect-assets');
var publicDir = path.join(__dirname, 'public');
var app = express();
app.configure(function(){
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use( connectAssets() );
app.use('/public', express.static(publicDir));
app.use(express.logger());
app.use(express.methodOverride());
app.use(app.router);
});
app.configure('development', function(){
app.use(express.errorHandler({
dumpExceptions: true,
showStack: true
}));
});
app.get('/', require('./routes').index);
app.get('/monalisa', require('./routes/monalisa').monalisa);
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
I've created a package to help solve this problem; it's called connect-assets-jspaths.
From the readme:
Installation
npm install connect-assets-jspaths
Note, there is a dependency on CoffeeScript.
Server Side Usage
assets = require "connect-assets"
jsPaths = require "connect-assets-jspaths"
# Snip ...
app.use assets()
# Exports the global function exportPaths() and jsUrl(); see below in View Helpers.
jsPaths assets
# Optionally, pass a log function to see progress
# jsPaths assets, console.log
Watch changes and re-compile
Now you can pass some additional callbacks in and it will monitor your connect assets directories for changes.
fileChangedCallback = (err, filePath) ->
console.log "File Changed: #{filePath}"
jsPaths assets, console.log, fileChangedCallback, (err, watcher) ->
console.log "Watcher initialized"
NOTE You'll probably want to disable this for production mode.
View Usage
This module exports two global functions exportPaths() and jsUrl().
// Using this in your view
!= exportPaths("jsPaths")
// Turns into this when rendered in production
<script type="text/javascript">
var jsPaths = { "main", "/builtAssets/js/main.13819282742.js" /* snip all the other file paths */ };
</script>
// Using this in your view
- var mainJsPath = jsUrl("/js/main.js")
script(type="text/javascript", data-main="#{mainJsPath}", src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.0.2/require.min.js")
// Turns into this when rendered in production
<script type="text/javascript" data-main="/builtAssets/js/main.13819282742.js" src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.0.2/require.min.js"></script>
Dynamic RequireJS Paths
Now that we have a variable with our requireJS friendly paths in it, we can set those paths in the RequireJS config
# Example main.coffee file in /assets/js folder
requirePaths =
paths:
jquery: "//cdnjs.cloudflare.com/ajax/libs/jquery/1.7.2/jquery.min"
underscore: "//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.3/underscore-min"
backbone: "//cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min"
text: "/js/lib/text"
handlebars: "/js/lib/handlebars"
if jsPaths
for own key, value of jsPaths
# Fix up the lib references
key = key.slice 4 if key.slice(0, 4) == "lib/"
requirePaths.paths[key] = value
require.config
paths: requirePaths.paths
shim:
jquery:
exports: "$"
underscore:
exports: "_"
backbone:
deps: ["underscore", "jquery"]
exports: "Backbone"
require ['app'], (App) ->
new App().initialize()
Try mimosa, it'll help you with each one of those things out of the box. http://www.mimosajs.com
mimosa new [name] will give you a starter project with all of it.
Sorry for the new answer, but I decided to go make an account. =)
Mimosa will give you a small Express application if you choose Express as part of the mimosa new workflow. And if you choose CoffeeScript it'll give you an Express app in CoffeeScript. And it'll have RequireJS included in the scaffolded application. So you should not need to rewrite anything. You just need to plug your stuff in. If anything the Express app it gives you will serve as an example for you to do it yourself without using Mimosa.

Resources