Options found in locals object in ejs - node.js

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.

Related

pug : TypeError: View is not a constructor

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.

Angular not updating html template variables when served via NodeJS server

This is a bit of a specific question, but I'm at a bit of a loss for an answer.
First, a little background. I've been trying to learn angular, and I wanted to start using Node as the backend. I currently have a working tutorial app that I can run locally that just returns data that is hard coded into the main controller.
When I moved the files to my NodeJS server, it stopped working though. Here is what works:
The files load correctly - there are no console errors, and I can view each of the files in the source (index.html, app.js, maincontroller.js)
The scope exists, and the variables are defined. I put a console.log($scope) inside the mainController.js file, and I can see all of the variables defined correctly.
Non-angular javascript works - I can place alerts outside/inside the mainController, and they all work correctly (also, console.log obviously works)
I am serving the files via a simple Node.js server. I am using express and hbs. I was originally using compression, and 0 cache length, but have since removed those with no change in the result.
The specific issue I'm having is that none of the template variables update. I've simplified it down to the following code for testing. When viewed locally, the page says 'I now understand how the scope works!', when served from Cloud 9, the structure exists, but the {{understand}} variable in the template doesn't work.
index.html
<!DOCTYPE html>
<head>
<title>Learning AngularJS</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script src="js/app.js"></script>
<script src="js/maincontroller.js"></script>
</head>
<body>
<div id="content" ng-app="MyTutorialApp" ng-controller="MainController">
{{understand}}
</div>
</body>
app.js
var app = angular.module('MyTutorialApp',[]);
maincontroller.js
app.controller("MainController", function($scope){
$scope.understand = "I now understand how the scope works!";
});
server.js (Node server on Cloud 9)
var express = require('express');
var app = express();
var hbs = require('hbs');
app.set('view engine','html');
app.engine('html',hbs.__express);
app.configure(function() {
app.set('views', __dirname);
});
//app.use(express.compress());
app.use('/js',express.static(__dirname + '/client/js'));
app.use('/css',express.static(__dirname + '/client/css'));
app.use('/img',express.static(__dirname + '/client/img'));
//router
app.get('/',function(req,res){
res.render('client/index.html');
return;
});
//404 responses
app.use(function(req, res, next){
res.status(404);
// respond with html page
if (req.accepts('html')) {
res.render('client/404.html', { url: req.url });
return;
}
// respond with json
if (req.accepts('json')) {
res.send({ error: 'Not found' });
return;
}
// default to plain-text. send()
res.type('txt').send('Not found');
});
app.listen(process.env.PORT);
console.log('listening on port '+process.env.PORT);
everythin became clear when i read
"Handlebars.js is an extension to the Mustache templating language"
what this menas is that hbs uses {{}} as delimiters as well as angular so the {{understand}} in your html never gets to angular because is first parsed and substituted by hbs. if you want to use hbs with angular youll need to change your delimiters using your angulars $interpolateProvider in your app configuration something like
$interpolateProvider.startSymbol('{/{');
$interpolateProvider.endSymbol('}/}');
You can use \{{understand}} as this will counter your hbs and put your angular on top.

Node.js - socket.io web app

I've created a basic node.js server program and used socket.io to pass some field data from a client (see below). Pretty chuffed as I'm new to this business. I liked this node-express-socket.io approach as its all Javascript and is apparently usable by most browsers (incl' mobile). The problem is I've kind of fumbled my way through and do not not fully understand what I have created! Two questions...
1) Do I need to use the "//ajax.googleapis.com...jquery..."? This is annoying as the browser will need to have an internet connection to work. Is there another way to access the html doc elements without needing an internet connection?
2) What does the "app.use(express.static...." line do? The "app.get..." function seems to require this to work.
If there are any other general comments about my code please let me have it!
Cheers,
Kirbs
Client side code:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect(document.location.protocol+'//'+document.location.host);
function clicked(){
$(function(){
var makeInput=$('.app').find('#make').val();
var modelInput=$('.app').find('#model').val();
socket.emit('make', makeInput);
socket.emit('model', modelInput);
});
};
</script>
Server side code:
var express = require('express');
var http = require('http');
var socketio = require('socket.io');
var app = express();
var server = http.createServer(app);
var io = socketio.listen(server);
app.use(express.static(__dirname));
app.get('/', function (req, res) {
res.render(__dirname + '/index.html');
});
io.sockets.on('connection', function (socket) {
socket.on('make', function (make) {
socket.on('model',function (model){
console.log('recieved message:', make+','+model);
});
});
});
server.listen(8000);
1) As you have setup a static web server (see answer 2), you could simply download the jquery source and serve the .js file from there.
2) "app.use(express.static...." configure a static webserver and setting up the http root directory to the directory that your node.js script lives, as indicated by the __dirname variable. For more detail, see app.use API reference.
As result, I would recommend you change you app.use to:
app.use(express.static(__dirname + '/public'));
and place all your static files, including your jquery file(s), under a public subdirectory.
Also, your server side code has a dependency on sequence of make and model which should be changed. For example, if you switch the emit order to model then make, you should see that your server's console.log will be picking up the make from the previous call.
Instead, try something like:
// On server:
socket.on('info', function (info) {
console.log('recieved message:', info.make+','+info.model);
});
// On client:
socket.emit('info', { make: makeInput, model: modelInput })
1) You can serve the jQuery library also from your server if you like that better. You should put it in the public/vendor or public/js folder in your project.
2) This is a middleware call from Express framework, which uses in turn the Connect middleware stack. Read up on this here.

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