I have only just started trying out NodeJS. Having a little experience with Angular, I thought of trying out a MEAN stack to-do app tutorial from scotch.
I understand what is happening and how angular, node and my view are supposed to be working together. But they just aren't. I've made sure not to miss anything. Here is my code.
Server.js is in the root folder
// server.js
// set up ========================
var express = require('express');
var app = express(); // create our app w/ express
var mongoose = require('mongoose'); // mongoose for mongodb
var mongodb = require('mongodb');
var morgan = require('morgan'); // log requests to the console (express4)
var bodyParser = require('body-parser'); // pull information from HTML POST (express4)
var methodOverride = require('method-override'); // simulate DELETE and PUT (express4)
// configuration =================
mongoose.connect('mongodb://<user>:<pass>#proximus.modulusmongo.net:27017/uwa8sIje'); // connect to mongoDB database on modulus.io
app.use(express.static(__dirname +'/public')); // set the static files location /public/img will be /img for users
app.use(morgan('dev')); // log every request to the console
app.use(bodyParser.urlencoded({'extended':'true'})); // parse application/x-www-form-urlencoded
app.use(bodyParser.json()); // parse application/json
app.use(bodyParser.json({ type: 'application/vnd.api+json' })); // parse application/vnd.api+json as json
app.use(methodOverride());
// creating mongoose model ================================
var Todo = mongoose.model('Todo', {
text: String
});
// Todo is the mongo db. Creating API for CRUD in the db
//============================================================
app.get('/api/todos', function(req, res) {
Todo.find(function (err, todos) { //within 'get' we are looking for all the entries in the db
if(err) {
res.send(err) //checking for errors
}
res.json(todos); //response sends all listed todos in JSON
})
})
app.post('/api/todos', function(req, res) { //to post a new todo
Todo.create({ //creating a new post. information comes from AJAX request from Angular
text: req.body.text,
done: false
}, function(err, todo) { //checking errors
if(err) {
res.send(err);
}
Todo.find(function (err, todos) { //after post is added, find and display all existing todos again
if(err) {
res.send(err)
}
res.json(todos);
})
})
})
app.delete('/api/todos/:todo_id', function(req, res) { //delete a todo
Todo.remove({ //remove a todo from database
_id: req.params.todo_id, //todo id to be removed is provided by the request url(params)
}, function(err, todo) {
if(err) {
res.send(err);
}
Todo.find(function (err, todos) {
if (err) {
res.send(err)
}
res.json(todos);
})
})
})
//======================================================================
app.get('*', function(req,res) {
res.sendfile('./public/index.html') //load this single view file. angular will handle the
//page changes on the front end
})
// listen (start app with node server.js) ======================================
app.listen(8080);
console.log("App listening on port 8080");
My angular controller lies in the public folder along with my view
var Todoz = angular.module('Todoz', []);
function mainController($http, $scope) {
$scope.formData = {};
$http.get('/api/todos')
.success(function (data) {
$scope.todos = data;
console.log(data);
})
$scope.createTodo = function() {
$http.post('/api/todos', $scope.formData)
.success(function (data) {
$scope.formData = {};
$scope.todos = data;
})
.error(function (data) {
console.log('Error' + data)
})
}
$scope.deleteTodo = function(id) {
$http.delete('/api/todos/' + id)
.success(function (data) {
$scope.todos = data;
console.log(data);
})
.error(function (data) {
console.log(data)
});
};
Todoz.controller("mainController", mainController);
}
and my minimal view
<head>
<title>
To-Do-and-Node-To-Do
</title>
</head>
<body ng-app="Todoz">
<h1>The To-do-ist</h1>
<div ng-controller="mainController">
<div>
<input type="text" ng-model="formData.text"></input>
<button type="submit" ng-click="createTodo()">Submit</button>
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<script src="./core.js"></script>
</body>
When I start the server with 'nodemon server.js' my console logs a /GET request and the app shows on my port 8080. However when I try to post a new item from my text-box and expect it to console.log, it doesn't do anything. My browser console returns this
POST http://localhost:8080/api/todos net::ERR_CONNECTION_REFUSED angular.js:8632
Error core.js:23
POST http://localhost:8080/api/todos net::ERR_CONNECTION_REFUSED angular.js:8632
Error core.js:23
Please help. I have no clue what is going wrong.
Related
I have a simple node.js app which displays user login information stored in a SQLite database, with a radio button within each row. When the user selects a record and clicks the delete button, the appropriate record should be deleted.
The problem seems to be that I can't work out how to return the value of the selected radio button, they are all showing as 'undefined'. I have tried numerous different methods and am going around in circles. Help appreciated!
Edit: I think the issue is that I am using one GET method for everything, so the req.body is empty. Not sure how to fix it though!
var express = require('express');
var app = express();
var bodyParser = require("body-parser")
var path = require('path');
var sqlite3 = require("sqlite3").verbose();
// Open persistent file database "loginDB"
let db = new sqlite3.Database('./loginDB', sqlite3.OPEN_READWRITE, (err) => {
if (err) {
console.error(err.message);
}
console.log('Connected to the user database.');
});
// Route files in public
app.use(express.static('public'));
// Configure to use body-parser
app.use(bodyParser.urlencoded({extended: false}));
// Function to check if radio button is selected
function checkSelected(item) {
return item != "undefined";
}
// Function to identify ID of record selected for deletion
function getSelected(selections) {
var userID = selections.findIndex(checkSelected);
return userID;
}
// REST endpoint for displaying data
app.get("/delete", function(req, res, next) {
var radioArray = [];
// Query database to retrieve records
db.all(`SELECT * FROM UserLogin`, function(err, rows) {
if (err) {
console.error(err.message);
};
// Generate table with user data
res.write('<html><head>');
res.write('<meta charset="utf-8">');
res.write('<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">');
res.write('<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>');
res.write("</head><body>");
res.write("<h2> The User Information Table</h2><br>");
res.write("<table class = 'table table-bordered table-striped'><thead><tr>");
res.write("<strong><th> </th>");
res.write("<th>ID</th>");
res.write("<th>Username</th>");
res.write("<th>Password</th></strong></tr></thead><tbody>");
rows.forEach(function(row) {
res.write(`<tr><td><input type='radio' value='${row.ID}' id='optradio' name='optradio'></td><td>` +row.ID+ `</td><td>` +row.Username+ `</td><td>` +row.Password+ `</td></tr>`);
radioArray.push(req.body.optradio);
});
res.write("</tbody></table></body></html><br>");
res.write(`<button class="btn btn-default" type="submit" value="delete" class="btn btn-primary">Delete</button>`);
var whichRadio = getSelected(radioArray);
db.run(`DELETE FROM UserLogin WHERE ID = ?`, whichRadio, function(err) {
if (err) {
return console.error(err.message);
} console.log("Deleted ID " + whichRadio);
});
res.send();
});
});
app.listen(3000, function() {
console.log("Web server running at: http://localhost:3000");
console.log("Type Ctrl+C to shut down the web server");
});
I'm posting an answer to my question to help others with the same issue. Adding in form tags and setting up a separate POST method allowed me to get rid of the untidy code I was using to try and retrieve the radio button value, so the end result is neater.
var express = require('express');
var app = express();
var bodyParser = require("body-parser")
var path = require('path');
var sqlite3 = require("sqlite3").verbose();
// Open persistent file database "loginDB"
let db = new sqlite3.Database('./loginDB', sqlite3.OPEN_READWRITE, (err) => {
if (err) {
console.error(err.message);
}
console.log('Connected to the user database.');
});
db.serialize(function() {
db.run(`SELECT * FROM UserLogin`);
});
// Route files in public
app.use(express.static('public'));
// Configure to use body-parser
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
// REST endpoint for displaying data
app.get("/delete", function(req, res, next) {
// Query database to retrieve records
db.all(`SELECT * FROM UserLogin`, function(err, rows) {
if (err) {
console.error(err.message);
};
// Generate table with user data
res.write('<html><head>');
res.write('<meta charset="utf-8">');
res.write('<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">');
res.write('<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>');
res.write("</head><body>");
res.write("<h2> The User Information Table</h2><br>");
res.write(`<form id="deleteUserForm" action="/inputForm" method="POST"</form>`);
res.write("<table class='table table-bordered table-striped'><thead><tr><strong>");
res.write("<th> </th>");
res.write("<th>ID</th>");
res.write("<th>Username</th>");
res.write("<th>Password</th></strong></tr></thead><tbody>");
rows.forEach(function(row) {
res.write(`<tr><td><input type='radio' form="deleteUserForm" value='${row.ID}' id='optradio' name='optradio'></td><td>` +row.ID+ `</td><td>` +row.Username+ `</td><td>` +row.Password+ `</td></tr>`);
});
res.write("</tbody></table><br>");
res.write(`<button class="btn btn-default" type="submit" form="deleteUserForm" value="delete" class="btn btn-primary">Delete</button>`);
res.write(`</body></html>`);
res.send();
});
});
// REST endpoint for getting value of selected record ID and deleting the user
app.post('/inputForm', function(req, res) {
//res.send(req.body.optradio);
var userToDelete = req.body.optradio;
db.run(`DELETE FROM UserLogin WHERE ID = ?`, userToDelete, function(err) {
if (err) {
return console.error(err.message);
} res.redirect('/delete'); //Redirect to user information page
});
});
app.listen(3000, function() {
console.log("Web server running at: http://localhost:3000");
console.log("Type Ctrl+C to shut down the web server");
});
The body of the request being sent is empty according to req.body in my express route.
My main node file is as follows -
var express = require('express');
var bluebird = require('bluebird')
const bodyParser = require('body-parser');
const cors = require('cors');
/*initializations*/
global.mongoose = require('mongoose');
mongoose.Promise = bluebird
global.app = express();
global.config = require('./config/config');
global.jwt = require('jsonwebtoken');
app.use(bodyParser.json({ type: 'application/json' }))
app.use(bodyParser.urlencoded({ extended: true }));//accept strings, arrays and any other type as values
app.disable('x-powered-by');
require('./routes/auth.routes');
//DB connection
app.listen(config.port, function(){
console.log("Express started on " +config.base_url +' in '+config.env +' environment. Press Ctrl + C to terminate');
mongoose.connect(config.db.uri, config.db.options)
.then(()=> { console.log(`Succesfully Connected to the Mongodb Database at URL : `+config.db.uri)})
.catch((error)=> { console.log(error)})
});
The auth.routes file has the signup route and this is where the req.body is empty but it does not hit the if statement that checks, but when i console.log(re.body), it gives me that - {}
app.post('/signup', function(req,res,next){
if (!req.body||req.body=={}){
return res.status(400).send("Bad Request")
}
var user = new User(req.body);
user.password = bcrypt.hashSync(req.body.password, 10);
User.create(user, function(err,new_user){
if (err) {
console.log('A Big Error');
return res.status(500).send("There was a problem registering the user.")
}
//success code
})
});
And the request from the angular 4 app is
signup(user:User):Observable<boolean>{
return this.http.post(this.signup_url,JSON.stringify(user),
{
headers: new HttpHeaders().set('Accept', "application/json;q=0.9,*/*;q=0.8").set('Content-Type', "x-www-form-encoded")
})
.map((response: Response) => {
if(response){
if(response.json() && response.json().token&&response.json().user&&response.json().expires){
this.setSession(response.json());
return true;
}
else{
return false;
}
}
else{
return false;
}
});
}
I am certain the Angular 4 app is sending the right data to the server and that its not empty - checked chromes network request body.
I have tried the following links but none worked.
Express app empty request body with custom content type headers
Express receiving empty object
Node.js: Receiving empty body when submitting form.
Also tried with postman and the result is the same - which means the problem is from the express server and not the client side.
There is no need to stringify the posted data, the body-parser middleware will be responsible for parsing the data into object:
return this.http.post(this.signup_url, user, { ... }).map( ... );
One other thing, In the post handler, you might want to use .save() method instead of .create() because you already create a model instance, Remember that the .save() method is available on the model instance, while the .create() is called directly from the Model and takes the object as a first parameter
Example with .save() method:
app.post('/signup', function(req,res,next) {
if (!req.body){
return res.status(400).send("Bad Request");
}
var user = new User(req.body);
var salt = bcrypt.genSaltSync(saltRounds);
user.password = bcrypt.hashSync(req.body.password, salt);
user.save(function( err ) {
if (err) {
console.log('A Big Error');
return res.status(500).send("There was a problem registering the user.");
}
//success code
res.json({ success: true });
})
});
Example with .create() method:
router.post('/signup', function(req,res,next){
if (!req.body){
return res.status(400).send("Bad Request")
}
var salt = bcrypt.genSaltSync(saltRounds);
req.body.password = bcrypt.hashSync(req.body.password, salt);
User.create ( req.body, function( err, new_user) {
if (err) {
console.log('A Big Error');
return res.status(500).send("There was a problem registering the user.")
}
//success code
res.json({ success: true });
});
});
I'm learning how to use templatesjs from:
https://www.npmjs.com/package/templatesjs
They have an example of using include html file in other html file (using <%include%> tag)
When I'm trying to build my own example, it doesn't work (The screen is empty, with no errors):
var express = require('express');
var app = express();
var fs = require('fs');
var bodyParser = require('body-parser');
app.use(bodyParser.json())
var templatesjs = require('templatesjs');
// FILES
var MAIN_FILE = '/main.html';
/*
* Home page
*/
app.get('/', function (req, res) {
fs.readFile(__dirname + MAIN_FILE, function(err,data){
if(err) throw err;
templatesjs.set(data, function(err,data){
if(err) throw err;
res.send();
});
});
})
/*
* Startup
*/
var server = app.listen(8082, function () {
var host = server.address().address
var port = server.address().port
// start
console.log("App listening at http://%s:%s", host, port)
})
the main html.file looks:
<html>
<title> Tutorial -> Templates Js Server </title>
<head>
</head>
<body>
<div>
<%include Top.html%>
</div>
<div>
</div>
</body>
</html>
and Top.html file looks:
<p>TOP</p>
(I have tried to add <html> tag into Top.html, but same results);
The problem is that the web screen I'm getting is empty (with no errors at Node.js)
What am I doing wrong ?
It's because you doesn't send back any data to incoming request! your res.send() is empty. you should send something back if you really want to show it. for example: res.send('hello world').
If you want to render your template with your data, you could use templatesjs.renderAll() method to populate your html template with desired data as follows:
// set default directory for html partials
templatesjs.dir = "./public/partials/";
app.get('/', function(req, res) {
fs.readFile(__dirname + MAIN_FILE, function(err, data) {
if (err) throw err;
templatesjs.set(data, function(err, data) {
if (err) throw err;
var list = { // this is your data
name: 'your name'
};
templatesjs.renderAll(list, function(err, data) {
if (err) throw err;
res.send(data);
});
});
});
})
Top.html:
<p>Hello, my name is <%name%></p>
and this file should reside in ./public/partials/ directory as we set default include directory to this path;
as a project I'm trying to convert my todo application to save the todos in a mongdb database instead of a json file. So I set up the backend and changed my functions in angular to make requests to the backend but now I've got a few problems.
The boxes where the todos are posted are empty, even though I specified a name value in the database for them
Whenever I load the page I get the data from the database. As a test I also print what I get to the console. Right now I'm getting five empty todo boxes while in the database I have to objects. I can also see that I have two objects from the console.
When I do a post request using postman I specify the name of the todo task but in the database the task only has the parameters that are automatically generated from the mongodb database.
Here is my server.js file:
//connect to database
mongoose.connect('mongodb://localhost:27017/myTodoApp');
// set static files location
// used for requests that our frontend will make
app.use(express.static(__dirname + '/public'));
app.use(bodyParser.json());
//define our model for the todos
var Todo = mongoose.model('Todo', {
name: String,
});
//when I get a get request I'm going to send
//the index.html file
app.get('/', function(req, res){
res.sendFile( __dirname + '/public/index.html');
});
//get all the todos from the database
app.get('/api/todos', function(req, res){
Todo.find(function(err, todos){
if(err)
res.send(err)
res.json(todos);
});
});
//create a todo
app.post('/api/todos', function(req,res){
Todo.create({
name: req.body.text,
checked: false
}, function(err, todo){
if(err)
res.send(err);
Todo.find(function(err, todos){
if(err)
res.send(err)
res.json(todos);
});
});
});
app.delete('/api/todos/:todo_id', function(req, res){
Todo.remove({
_id: req.params.todo_id
}, function(err, todo){
if(err)
res.send(err);
Todo.find(function(err, todos){
if(err)
res.send(err);
res.json(todos);
});
});
});
app.listen(3000);
And here is my controller from which I connect the frontend to the backend:
var app = angular.module('myApp', ['navigationDirective']);
app.controller('MainController', ['$scope','$http', function($scope, $http){
$scope.formData = {};
//return the data from the json file
$scope.loadData = function(){
$http.get('/api/todos')
.then(function(data){
$scope.todos = data;
console.log(data);
})
};
//call the loadData function that returns the data from the json file
$scope.loadData();
$scope.addTodo = function() {
$http.post('/api/todos', $scope.formData)
.success(function(data) {
$scope.formData = {}; // clear the form so our user is ready to enter another
$scope.todos = data;
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
/*$scope.addTodo = function(){
$scope.todos.push({name: $scope.newTodo, checked: false});
$scope.updateData({name: $scope.newTodo, checked: false});
$scope.newTodo = '';
console.log('Works');
};*/
}]);
Any ideas of what I'm doing wrong?
I uploaded the main files that I'm using for this problem on plunkr
If you want to save more fields, you need to define them in your Todo Model Schema. If the field isn't defined, Mongoose won't save it.
var Todo = mongoose.model('Todo', {
name: String,
checked: Boolean,
date: {
type: Date,
default: Date.now
}
});
When creating an object from a post request, both of the fields that come from the request body are being set to the field name itself. I am not getting any errors but I am also not getting the expected JSON object of the request body back in the response. I was using a local mongodb instance but now I am using a mongolab database, thought it would maybe solve the problem but nothing changed.
var express = require("express");
var router = express.Router();
var Post = require("../app/models/post.js");
/*
//Drop Post collection
Post.remove({}, function(err, num_docs) {
if (err) {
res.send(err);
} else {
console.log("Collection dropped, documents deleted: " + num_docs);
}
});
*/
// Middleware for all routes.
router.use(function(req, res, next) {
console.log("API request made.");
next(); // Go to next routes, don't stop here
});
// Test route to ensure routing is working
router.get("/", function(req, res) {
res.json({
message: "Hooray! Welcome to the API!"
});
});
// On routes that end in /posts
router.route("/posts")
// Create post. (Accessed at POST http://localhost/api/posts)
.post(function(req, res) {
var post = new Post(); // Create new instance of post model
if (req.body.title !== undefined) {
post.title = req.body.title; // Set title (from request)
}
if (req.body.content !== undefined) {
post.content = req.body.content; // Set content (from request)
}
// Save the post, and check for errors.
post.save(function(err) {
if (err) {
res.send(err);
} else {
res.json(req.body);
}
});
})
.get(function(req, res) {
Post.find({}, { "__v": 0, "_id": 0 }).exec(function(err, posts) {
if(err) {
res.send(err);
} else {
res.json(posts);
}
});
});
module.exports = router;
Request JSON:
{
"title": "sample title",
"content": "sample content"
}
Output:
[
{
"lastUpdated": "2016-01-29T07:26:01.810Z",
"uploadDate": "2016-01-29T07:26:01.810Z",
"content": "content",
"title": "title"
}
]
You can't use req.body without including body parser and expect req.body to produce the expected result in express. Did you do npm install body-parser. I don't see you requiring body-parser in your code. Maybe you included body-parser in your main file. I believe that if you don't use it req.body will be empty.
add this below your other requires
var bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({extended : false}));
app.use(bodyParser.json())
and don't forget to do npm install body-parser in your terminal.