I'm learning Node.js. At this time, I am using ES6, Node, and Express. I am concerned about the scope of my view model. At this time, I have the following:
server.js
// setup the routing
var routes = require('./routes');
app.use('/', routes.home);
The routes are properly registering. I have my routes defined in $projectDir/routes/home.js. That file looks like this:
home.js
var router = require('express').Router();
/* GET the main page. */
router.get('/', function(req, res) {
var viewModel = require('../viewModels/home/index');
res.render('home/index', viewModel);
});
/* GET the contact page. */
router.get('/contact', function(req, res) {
var viewModel = require('../viewModels/home/contact');
res.render('home/contact', viewModel);
});
/* POST contact information */
router.post('/contact', function(req, res) {
// ?
});
That approach works just fine. I've defined my view models at $projectDir/viewModels/home/index.js and $projectDir/viewModels/home/contact.js.
index.js
var viewModel = {
title: '',
constructor() {
this.title = 'Welcome';
}
};
module.exports = viewModel;
contact.js
var viewModel = {
title: '',
emailAddress: '',
constructor() {
this.title = 'Contact Us';
}
};
module.exports = viewModel;
I have concerns around the scope of view model. First, I'm using module.exports = viewModel; in two separate files. Is this ok? Or should I name the variables something like indexViewModel and contactViewModel? Also, when a POST happens, how do I populate use the view model with the values that the user entered into a form? For example their email address?
As far as node is concerned, you can use the same name viewModel in more than one file. But for your own sanity and clarity of thought I'd recommend using different names, as you suggested. When you asked the question, "how do I populate the view model?", I had to go back and read through the code for a couple of seconds to see which view model you meant. When you program grows from 40 lines to 4000 lines it will become a lot hard to keep the models straight if they all have the same name.
As for the POST data, you can access the POST data using the req.body property. This questions is already answered here: How do you extract POST data in Node.js?
Related
I have a backend service that I would like to use as a single point of entry for my web application, and dynamically assign a database path based on the user login.
I realize that this is not a scalable solution. I intend to use it during a testing period with several clients (accessing the ALPHA database), and also setting up a demo (accessing the SAND database).
I have the following module that I have written as a simple test to see if the login is for the demo user, all other logins will go to the other resource:
config.js
var express = require('express');
var app = express();
module.exports.dbPath = function (login){
console.log('login - ', login);
if (login === 'demo#mysite.com'){
return process.env.DB_SAND;
} else {
return process.env.DB_ALPHA;
}
};
My question is, how can I manage each unique login and assign a globally accessible reference for that session to direct each user session consistently to the correct database?
Am I overcomplicating this? If there is a different approach that would be a better practice I would welcome a suggestion in another direction.
I would use it as a middleware, and attach it to the req object for each user, something similar this:
module.exports = {
dbPath: function(req, res, next){
var login = req.body.login;
console.log('login - ', login);
if (login === 'demo#mysite.com'){
req.dbPath = 'DB_SAND';
} else {
req.dbPath = 'DB_ALPHA';
}
next();
}
};
I started taking some tutorials and some books about NodeJS, and started to test out stuff using Mongoose and Rest API requests.
The problem that I encountered is related to the creation of a constructor which will take a parameter (requestBody) and then implement the whole binding on the module (on my case user.js). That is to make the code more reusable and the user module to handle the bindings not the server module which will increase the source code a lot there.
Example
The code below is working as it should, but it's not efficient
var express = require('express')
, bodyParser = require('body-parser')
, expressValidator = require('express-validator')
, mongoose = require('mongoose');
var router = express.Router();
router.route('/users')
// Create a user (accessed at POST http://localhost:8080/api/users)
.post(function (req, res) {
// Bind user (repeating the same procedure for every User object)
var user = new User();
user.info.name = req.info.name;
user.info.surname = req.info.name;
.... // around 10 other properties
req.info.birthday = req.info.name;
// Save
user.save(function (err) {
.... // Handling completion
});
})
My approach
// app/models/user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
info: {
name: {type: String, required: true},
surname: {type: String, required: true},
......
birthday: {type: String, required: true}, // dd/mm/yyyy
},
likes: [Schema.Types.ObjectId],
dislikes: [Schema.Types.ObjectId]
});
function initUser(model) {
userSchema.info.name = model.info.name;
userSchema.info.surname = model.info.surname;
......
userSchema.info.birthday = model.info.birthday;
userSchema.likes = model.likes;
userSchema.dislikes = model.dislikes;
}
module.exports = mongoose.model('User', userSchema);
module.exports.initUser = initUser;
Based on this code, on the request I could easily use (Javascript beginner so explanation will be great)
var user = new User();
user.initUser(req);
but I come up with a really long error which causes the server to crash and the cleanest message is:
TypeError: Converting circular structure to JSON
Questions
Is there anyone who has solved this? I also took a look at Mongoose site, but there doesn't seem to have a specific thing like this.
I'm not sure about your circular json problem, but your initial problem is the way in which you are reading your model properties off your request.
If you post values to that endpoint, the json values you are looking for are going to be on the body property on the request. Pass your function this:
user.initUser(req.body)
You'll need to install a body parser in express to handle the value properly. Once you've done that and pass in request.body, your approach will work.
Pro tip: don't bother mapping the properties individually. If the keys are correctly named just use them to construct your model directly.
How can I send/get the querystring from one page to another?
I am using: node version v0.12.3000 express 3.20.3
I am trying this way:
page1.js
function p1(req, res){
res.render('page1');
};
exports.p1= p1;
exports.post_enviar = function(req, res){
var param1 = req.body.param1;
res.render('page2', { param1 });
}
page2.js
function p2(req, res){
res.render('page2');
};
exports.p2= p2;
exports.get_enviar = function(req, res){
var param1 = req.params.param1;
console.log("param1: "+param1);
}
Thanks!
Short answer: you can't do that.
Long(er) answer: what you're trying to do involves either queuing a message in one route handler that would be consumed by another route, or otherwise simply sharing state between them.
The post_enviar export is stateless. It doesn't reference any data outside itself.
To demonstrate: (warning, don't do this)... If you had two modules for route controllers like this:
// route1controller.js
var lastQuery = {};
module.exports.route1controller = function (req, res, next) {
lastQuery = req.params;
res.render(...);
}
module.exports.lastQuery = lastQuery;
and
// route2controller
var lastQuery = require('./path/to/route1controller').lastQuery;
module.exports.route2controller = function (req, res, next) {
res.send(lastQuery);
}
Then the page rendered by route2controller will get the object (or array) of parameters that was last sent in a request to the route1controller. This is just a crude way of sharing state between the two controllers, and you really don't want to do this. Consider a more manageable solution, like logging (writing to a flat file, or database) requests to a given route, and then parsing them in a human readable way on another page.
If you're looking to get real-time information on one page about requests to another page, you'll need to look into more sophisticated solutions like websockets, or a polling for current data.
I have a server in Node.js and I use Express to make a web app.
My server (app.js) gets data from a form (by Ajax post method) and I want that it proceeds this data by using the code of another file (anotherfile.js).
I used this solution: https://stackoverflow.com/a/950146/3303704 and I included this code in my app.js file:
app.post('/', function(req, res) {
$.getScript("anotherfile.js", function(){
alert("Script loaded and executed.");
});
});
But Node.js returns me that "has no method 'getscript'".
Idea of the cause and solution? Thanks!
You appear to be trying to use jQuery in node.
The solution you have linked to is a solution for front-end.
Use require() in node.
Try reading this article: Node.js, Require and Exports. It explains that for instance if you define this in one file called user.js:
var User = function(name, email) {
this.name = name;
this.email = email;
};
module.exports = User;
You can do this in another:
var user = require('./user');
var u = new user.User();
Fairly new to JavaScript so it might be a noobish question.
At the moment for my project I'm using NodeJS for my server and Backbone for the client. The client will send a request to the server and the server will send list of files in the server, my aim was to simply return the list of files and when user click on the file it will send another request to the server to load the file.
Currently in the client level my model and collection is defined something like:
app.MyFile = Backbone.Model.extend({
defaults: {
modifiedDate: new Date(),
path: '',
content: '' // content of the file
}
});
var MyFileList = Backbone.Collection.extend({
model: app.MyFile,
url: '/api/files'
});
// create global collection of files
app.MyFiles = new MyFileList();
app.AppView = Backbone.View.extend({
initialize: function () {
// fetch all files
app.MyFileList.fetch();
}
});
// app.js (point of entry)
$(function() {
// Kick things off by creating the **App**.
new app.AppView();
});
And my server code:
var application_root = __dirname,
express = require("express"),
...
app.get('/api/files', function(req, res) {
...
// return file list
}
app.get('/api/files/:id', function(req, res) {
...
// return file content?
}
Since it doesn't make sense to load all files in the directory and send it back to the client, what I did was I created the model in the server and fill up modifiedDate and path while leaving content to null. But the problem now is that how do I fill up the content when user clicks on the file? I'm not sure how to manually send an HTTP request from Backbone View or controller. Or is there any better way of doing this? One way that I can think of is to create another model that only keeps modifiedDate and path but to me this looks very verbose and repetitive.
Given what you have on the client side, you may not need anything more.
app.MyFiles = new MyFileList();
app.MyFiles.fetch().done(function() {
// your collection is fetched but each model's content is empty.
// now, I assume at this point you will show them in some view/views.
});
Now when one of those things is clicked on, you can fetch the content.
var model = app.MyFiles.get(id);
model.fetch().done(function() {
// now the model's content attribute will be set
});
This might work with no more code than what you showed. Because the url a model uses to fetch is created by default by appending the model's id to the end of its collection's url.
So from your server, you return a json array from '/api/files': [{id:1, path:'foo'}, {id:2, path:'bar'}]
Then from '/api/files/1': {id:1, path:'foo', content:'whatever'}
When user clicks the file, you can call backbone's fetch method on the model. Then your model will be filled with data from the server.
Note that for this to be working you should return collection from the server first, where models at least have id's. Every other field will be filled after fetch call. Also, you should override model url, if it differs from standard (which is collection/id).