I am developing a MEAN stack application. I am trying to simply create a record in MongoDB from a form. I have verified in the debugger that the data binding is working between the view and the controller. In the server side controller code, checking the req.body before trying to save the record returns "undefined" (see below in the code). In the Angular controller, I have examined the "results" value in the callback when the announcement.$save function is executed and it shows the heading and details values to be populated as they should. However, the data is not persisted to the database and I get the following error:
{ [ValidationError: Announcement validation failed]
message: 'Announcement validation failed',
name: 'ValidationError',
errors:
{ details:
{ [ValidatorError: Path `details` is required.]
properties: [Object],
message: 'Path `details` is required.',
name: 'ValidatorError',
kind: 'required',
path: 'details',
value: undefined },
heading:
{ [ValidatorError: Path `heading` is required.]
properties: [Object],
message: 'Path `heading` is required.',
name: 'ValidatorError',
kind: 'required',
path: 'heading',
value: undefined } } }
What am I missing? Here is my code:
The form in my html file:
<form ng-submit="AnnouncementsVm.createAnnouncement()">
<fieldset class="form-group">
<label for="heading">Heading:</label>
<textarea class="form-control" id="heading" rows="1"
ng-model="AnnouncementsVm.announcementHeading"></textarea>
</fieldset>
<fieldset class="form-group">
<label for="details">Details:</label>
<textarea class="form-control" id="details" rows="3"
ng-model="AnnouncementsVm.announcementDetails"></textarea>
</fieldset>
<p><input type="submit" value="Submit →"><p>
</form>
The angular route for this page partial is defined as:
$routeProvider.
when('/announcements', {
templateUrl: '/views/partials/announcements.html',
controller: 'Announcements.Controller',
controllerAs: 'AnnouncementsVm'
});
Here is my controller code:
angular.module('app').
controller('Announcements.Controller', AnnouncementsCtrl);
function AnnouncementsCtrl($log, $resource) {
$log.debug('Executing AnnouncementsCtrl');
var vm = this;
var Announcement = $resource('/api/announcements');
Announcement.query( function(results) {
vm.announcements = results;
});
vm.announcements = [];
vm.createAnnouncement = function() {
var announcement = new Announcement({
heading: vm.announcementHeading,
details: vm.announcementDetails
});
announcement.$save( function(result) {
vm.announcements.push(result);
vm.announcementHeading = '';
vm.announcementDetails = '';
});
};
}
The REST API route is defined as:
app.post('/api/announcements', announcementsController.create);
The server side controller (announcements-controller.js):
'use strict';
var Announcement = require('../models/Announcement.js');
module.exports.create = function(req, res) {
var announcement = new Announcement(req.body);
console.log(req.body); // returns "undefined"
announcement.save( function(err, result) {
if (err) console.log(err);
console.log('Save Announcement Result: ' + result);
res.json(result);
});
};
module.exports.list = function(req, res) {
Announcement.find({}, function (err, results) {
if (err) throw err;
res.json(results);
});
};
And finally, I am using this Mongoose model (Announcements.js)
'use strict';
var mongoose = require('mongoose');
var AnnouncementSchema = new mongoose.Schema({
heading: {type: String, required: true},
details: {type: String, required: true},
image: {type: String, required: false}
});
module.exports = mongoose.model('Announcement', AnnouncementSchema);
How is configured your routes in Angular? Are you passing the controller as 'AnnouncementsVm'?
Have you tried to access the values of the ng-models announcementHeading and announcementDetails from the controller?
Try to put
vm.createAnnouncement = function() {
$log.log(vm.announcementHeading);
$log.log(vm.announcementDetails);
});
};
And check if you are getting the correct values
Problem solved. I had not integrated the body-parser for the route so the request wasn't being populated correctly. Here is the updated ExpressJS route:
'use strict';
var bodyParser = require('body-parser');
var path = require('path');
var announcementsController = require('../controllers/announcements-controller.js');
module.exports = function(app) {
// create application/json parser
var jsonParser = bodyParser.json();
// create application/x-www-form-urlencoded parser
var urlencodedParser = bodyParser.urlencoded({ extended: false });
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, '../../client/views/index.html'));
});
// REST API
app.get('/api/announcements', announcementsController.list);
app.post('/api/announcements', jsonParser, announcementsController.create);
};
Related
I am trying to implement a simple edit function, and it won't work. My delete and get do work. I keep getting a 500 error on the put request. I have tried findIdAndUpdate and I hace tried FindOne as well. The error I get, is it fails to load the resource. But if I do a get request it works fine. If this makes a difference, the get request also returns a 304.
If I send the request in curl I get TypeError: Cannot read property 'id'
Controller and Service
app.factory('gameService', function($resource){
return $resource('/api/games/:id', {id:'#id'},
{'update': {method:'PUT'}}
);
});
app.controller("gameController", function($scope, $http, gameService){
$scope.games = [];
$scope.newGame = {name: '', platform: ''};
$scope.editMode = false;
$scope.games = gameService.query();
$scope.edit = function(game){
$scope.editMode = true;
$scope.newGame = gameService.get({id: game._id});
};
$scope.update = function(){
gameService.update({id: $scope.newGame._id}, function(response){
$scope.games = gameService.query();
$scope.newGame = {name: '', platform:''};
});
$scope.editMode = false;
};
});
API/Express/Mongoose
router.put('/games/:id', function(req, res, next) {
Game.findById(req.parms.id, function (err, game) {
if (err) {
return res.send(err);
}
game.name = req.body.name;
game.platform = req.body.platform;
game.save(function(err){
if (err) {
return res.send(err);
}
res.json({message:'Game Updated'});
});
});
});
HTML
<input required type="text" placeholder="Game Name" ng-model="newGame.name" /> <br/><br/>
<input required type="text" placeholder="Platform" ng-model="newGame.platform" /> <br/><br/>
<input class="button" type="submit" value="Post" ng-click="post()" ng-hide="editMode" />
<input class="button" type="submit" value="Update" ng-click="update()" ng-show="editMode"/>
Model
var mongoose = require ('mongoose');
var GameSchema = new mongoose.Schema({
name: String,
platform: String
});
mongoose.model ('Game', GameSchema);
req.parms.id should be req.params.id in your Mongoose/Express API route.
I want to ask you for help in this code.
Edit and delete works well but I can't add an article to the database (mongodb) .
*errors in browser console:
POST http://localhost:5000/articles 500 (Internal Server Error)
Article is not a function
TypeError: Article is not a function in articles.js
I don´t know what is wrong with object (Article) . Example is of course: Projects in AngularJS - Learn by building 10 Project - Section 7: KnowledgeBase. Please help me to solve this code. I would like to understand why it didn't work.
Form html
<div ng-controller="ArticlesCreateCtrl">
<h3>Nowy</h3>
<form ng-submit="addArticle()">
<div class="form-group">
<label>Title</label>
<input type="text" class="form-control" ng-model="title" name="title">
<label>Category</label>
<select class="form-control" ng-model="category" name="category">
<option ng-repeat="category in categories" value={{category.name}}">
{{category.name}}</option>
</select>
<label>Body text</label>
<textarea class="form-control" ng-model="body" name="body"></textarea>
</div>
<button type="submit" class="btn btn-default">Submit</button>
Canel
</form>
</div>
ArticlesCreateCtrl controller
.controller('ArticlesCreateCtrl', ['$scope', '$http', '$routeParams', '$location',
function($scope, $http, $routeParams, $location) {
$http.get('/categories').success(function(data){
$scope.categories = data;
});
$scope.addArticle = function(){
var data = {
title: $scope.title,
body: $scope.body,
category: $scope.category
};
$http.post('/articles', data).success(function(data, status){
console.log(status);
});
$location.path('/articles');
}
}])
articles.js // Error here
var express = require('express');
var router = express.Router();
var Article = require('../models/article');
router.post('/', function(req, res, next) {
var title = req.body.title;
var category = req.body.category;
var body = req.body.body;
###error in this line ###
var newArticle = new Article({
title: title,
category: category,
body: body
});
Article.createArticle(newArticle, function(err, article) {
if(err) {
console.log(err);
}
res.location('/articles');
res.redirect('/articles');
});
});
module.exports = router;
article.js
var mongoose = require('mongoose');
var artSchema = mongoose.Schema({
title: {
type: String,
index: true,
required: true
},
body: {
type: String,
required: true
},
category:{
type: String,
index: true,
required: true
},
date:{
type:Date,
default: Date.now
}
});
var Article = module.exprots = mongoose.model('Article', artSchema);
module.exports.createArticle = function(newArticle, callback){
newArticle.save(callback);
};
console.log(Article)
before var Article = require('../models/article');
undefined
and after var Article = require('../models/article')
{ getArticles: [Function],
getArticleById: [Function],
getArticlesByCategory: [Function],
createArticle: [Function],
updateArticle: [Function],
removeArticle: [Function] }
OK Friends - works
I don't know if it's the best way, but only I changed this line:
var Article = module.exprots = mongoose.model('Article', artSchema);
on
var Article;
module.exports = Article = mongoose.model('Article', artSchema);
This solved my problem and hope this will help you in future.
I went through hundreds of pages for several days without success and here is my problem.
I use the MEAN stack and at this point I have a simple form that works very well to save a "name" field to a MongoDB collection. Now, I would like, client-side, add an image upload and, on form submit, store the image on my server and finally save the "name" field and the image path to the MongoDB collection.
AngularJS side, I tried using ng-file-upload with multer server-side. I have done well to operate for the upload of the file but only that. But after hundreds of tests, I despair. Here is an extract of my original code without file upload.
Server side
sections.server.model
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var SectionSchema = new Schema({
name: {
type: String,
default: '',
trim: true,
required: true,
unique: true
},
image: {
type: String,
default: ''
}
});
mongoose.model('Section', SectionSchema);
sections.server.controller
exports.create = function (req, res) {
var section = new Section(req.body);
section.save(function (err) {
if (err) {
return res.status(400).send({
message: getErrorMessage(err)
});
} else {
res.json(section);
}
});
};
sections.server.routes
var sections = require('../../app/controllers/sections.server.controller');
module.exports = function (app) {
app.route('/api/sections')
.post(sections.create);
};
Client side
sections.client.module
'use strict';
var sections = angular.module('sections', []);
sections.client.controller
'use strict';
angular.module('sections')
.controller('SectionsController',
['$scope', '$routeParams', '$location', 'Sections'
function ($scope, $routeParams, $location, Sections) {
$scope.create = function () {
var section = new Sections({
name: this.name
});
section.$save(function (response) {
$location.path('sections/' + response._id);
}, function (errorResponse) {
$scope.error = errorResponse.data.message;
});
};
}]);
sections.client.routes
angular.module('sections').config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/sections', {
controller: 'SectionsController',
templateUrl: 'sections/views/list-sections.client.view.html'
})
.when('/sections/create', {
controller: 'SectionsController',
templateUrl: 'sections/views/create-section.client.view.html'
})
.otherwise({
redirectTo: '/'
});
}]);
sections.client.service
'use strict';
angular.module('sections').factory('Sections', ['$resource', function ($resource) {
return $resource('api/sections/:sectionId', {
sectionId: '#_id'
}, {
update: {
method: 'PUT'
}
});
}]);
create-section.client.view
<section>
<h1>New Article</h1>
<form data-ng-submit="create()" novalidate>
<div>
<label for="name">Nom du rayon</label>
<div>
<input type="text" data-ng-model="name" id="name" placeholder="Name" required>
</div>
</div>
<div>
<input type="submit">
</div>
<div data-ng-show="error"><strong data-ng-bind="error"></strong></div>
</form>
</section>
Now, from this can anyone help me to add the image upload in the form and then save the field name and the image path in MongoDB.
Note that I want to reuse the upload mecanism in other forms of my app.
I had the idea of switching a generic middleware function in the road-side server wich call multer and return the image path to my sections.create function for MongoDB storing, something like that :
module.exports = function (app) {
app.route('/api/sections')
.post(uploads.upload, sections.create);
But I've never managed to pass the file in the POST from AngularJS request.
Thank you so much for all your ideas, your help and possibly an example of code that works.
I'm using the mean stack and I can´t figure out how to pass params to mongoose query from the angular controller.
From the mean stack (https://github.com/meanjs/mean) example, we have:
On the server side
an article model
/**
* Article Schema
*/
var ArticleSchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true,
required: 'Title cannot be blank'
},
content: {
type: String,
default: '',
trim: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Article', ArticleSchema);
an article controller with a function to obtain a list of all articles and another function to obtain an article by Id
/**
* List of Articles
*/
exports.list = function(req, res) {
Article.find().sort('-created').populate('user', 'displayName').exec(function(err, articles) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(articles);
}
});
};
/**
* Article middleware
*/
exports.articleByID = function(req, res, next, id) {
Article.findById(id).populate('user', 'displayName').exec(function(err, article) {
if (err) return next(err);
if (!article) return next(new Error('Failed to load article ' + id));
req.article = article;
next();
});
};
and the articles routes
/**
* Module dependencies.
*/
var users = require('../../app/controllers/users.server.controller'),
articles = require('../../app/controllers/articles.server.controller');
module.exports = function(app) {
// Article Routes
app.route('/articles')
.get(articles.list)
.post(users.requiresLogin, articles.create);
app.route('/articles/:articleId')
.get(articles.read)
.put(users.requiresLogin, articles.hasAuthorization, articles.update)
.delete(users.requiresLogin, articles.hasAuthorization, articles.delete);
// Finish by binding the article middleware
app.param('articleId', articles.articleByID);
};
on the client side
we have an articles module with a routes config file
// Setting up route
angular.module('articles').config(['$stateProvider',
function($stateProvider) {
// Articles state routing
$stateProvider.
state('listArticles', {
url: '/articles',
templateUrl: 'modules/articles/views/list-articles.client.view.html'
}).
state('viewArticle', {
url: '/articles/:articleId',
templateUrl: 'modules/articles/views/view-article.client.view.html'
});
}
]);
an articles controller
angular.module('articles').controller('ArticlesController', ['$scope', '$stateParams', '$location', 'Authentication', 'Articles',
function($scope, $stateParams, $location, Authentication, Articles) {
$scope.authentication = Authentication;
$scope.find = function() {
$scope.articles = Articles.query();
};
$scope.findOne = function() {
$scope.article = Articles.get({
articleId: $stateParams.articleId
});
};
}
]);
and a list view
<section data-ng-controller="ArticlesController" data-ng-init="find()">
<div class="page-header">
<h1>Articles</h1>
</div>
<div class="list-group">
<a data-ng-repeat="article in articles" data-ng-href="#!/articles/{{article._id}}" class="list-group-item">
<small class="list-group-item-text">
Posted on
<span data-ng-bind="article.created | date:'mediumDate'"></span>
by
<span data-ng-bind="article.user.displayName"></span>
</small>
<h4 class="list-group-item-heading" data-ng-bind="article.title"></h4>
<p class="list-group-item-text" data-ng-bind="article.content"></p>
</a>
</div>
<div class="alert alert-warning text-center" data-ng-if="articles.$resolved && !articles.length">
No articles yet, why don't you create one?
</div>
My question is:
If I want to find all the article of a user, how can I pass a variable param to the find() function in the angular view?
I thought that the Articles.query() in the angular controller works as a mongodb or mongoose command, but I wasn't able to implement it.
Pass an object in query() method and it will be sent to server as query variables. In server use req.query to get those variables:
Client:
$scope.articles = Articles.query({user: 'user_id'});
Server:
Article.find({user: req.query.user}).sort('-created').populate('user', 'displayName').
This is my jade file (basically # stand for id and . for class) and I want to send this to mongodb and save user.activity
textarea#post-form.form-control(
rows="4",
placeholder="Share to world of LinkMe",
ng-model='user.activity'
)
button#post-btn.btn.btn-primary.pull-right(ng-click="post(user)") Link
Here is my code for the controller:
$scope.post = function(user){
auth.post(user).then(function(){
$scope.activity = user.activity;
})
}
And the auth.post function :
post: function(user){
var deferred = $q.defer();
var updatedUser = new UsersResource(user);
updatedUser._id = identity.currentUser._id;
updatedUser.$update().then(
function(){
identity.currentUser.activity = updatedUser.activity;
deferred.resolve();
},
function(response){
deferred.reject(response);
}
);
return deferred.promise;
}
Good thing is I see the change when this happens, but it is not saved in the database.
This is the userSchema:
var userSchema = mongoose.Schema({
username: {type: String, require: '{PATH} is required' , unique: true},
firstName: {type: String, require: '{PATH} is required'},
lastName: {type: String, require: '{PATH} is required'},
profilePic: {type: String, default: 'imgs/default.jpg'},
activity: String,
salt: String,
hashPass: String,
roles: [String]
});
I really appreciate help :)
P.P. I have this on server now :
app.put('/collections/:collectionName/:id', function(req, res, next) {
req.collection.updateById(req.params.id, {$set:req.body}, {safe:true, multi:false}, function(e, result){
if (e) return next(e)
res.send((result===1)?{msg:'success'}:{msg:'error'})
})
})
Problem now is that I have model of User from userSchema but I didn't declare any collections. What should I do ?
You'll need a REST interface on the server side to work with Mongo, take a look at this:
http://docs.mongodb.org/ecosystem/tools/http-interfaces/#HttpInterface-RESTInterfaces
Let's say you are using Node.js with Express and Mongoose, you will have to set up a REST api along the lines of:
var app = express();
var mongoose = require('mongoose');
var database_url = "mongodb://localhost:27017/Database";
var listen_port = 8080; // example port
var collections = ["collection1",...];
mongoose.connect(database_url);
var Model1 = mongoose.model('collection1', userSchema); // <--- your user schema
// example of a getter by id
app.get('/api/get/:id', function(req, res) {
Model1.find({ _id: req.params.id }, function(err, post){
res.send(post);
});
});
app.listen(listen_port);
and set up dependencies in your package.json file, like for instance
"dependencies" : {
"express" : "~3.4.4",
"mongoose" : "~3.6.2"
}
And then make use of Angular $http service in order to perform async calls to the API.
https://docs.angularjs.org/api/ng/service/$http