Reputation: 11
I'm creating a MEAN stack app and I've been using the book 'Mean Web Development' by Amos Q. Haviv to help me along. I've run into a problem where I have a form that is suppose to submit a recipe, but when I check the database the entry is there with an id, date and author but no title, mainImage, content, category or tags. I've been trying to figure it out on my own for the past couple of days and going over the book again to see if I made any mistakes, but no luck.
Here is my model:
// Load the module dependencies
var mongoose = require('mongoose'),
paginator = require('mongoose-paginator'),
Schema = mongoose.Schema;
var CommentsSchema = new Schema({
commentor: String,
data: {
type: Date,
default: Date.now()
},
content: {
type: String,
default: ' ',
trim: true,
required: "Enter a comment into the comment box"
},
agree: Number,
disagree: Number
});
function toLower(lowerText) {
return lowerText.toLowerCase();
}
var TagsSchema = new Schema({
tags: {
type: String,
set: toLower
}
});
var RecipeSchema = new Schema({
created: {
type: Date,
default: Date.now()
},
author: {
type: Schema.ObjectId,
ref: 'User'
},
title: {
type: String,
default: ' ',
trim: true,
required: 'Title cannot be blank'
},
mainImage: {
type: String,
default: ' ',
trim: true
},
content: {
type: String,
default: ' ',
trim: true,
required: "Please enter recipe"
},
likes: Number,
faves: Number,
category: {
type: String,
set: toLower
},
tags: [TagsSchema],
comments: [CommentsSchema]
});
// Use paginator
RecipeSchema.plugin(paginator, {
limit: 20,
defaultKey: '_id',
direction: 1
});
mongoose.model('Recipe', RecipeSchema);
Here is my controller
// Load the module dependencies
var mongoose = require('mongoose'),
Recipe = mongoose.model('Recipe');
// Create a new error handling controller
var getErrorMessage = function(err) {
if (err.errors) {
for (var errName in err.errors) {
if (err.errors[errName].message) return err.errors[errName].message;
}
} else {
return 'Unknown server error';
}
};
// Create a new controller method that creates a new recipe
exports.create = function(req, res) {
// Create a new recipe object
var recipe = new Recipe(req.body);
// Set the recipe's 'author' property
recipe.author = req.user;
// Try saving the recipe
recipe.save(function(err) {
if (err) {
// If an error occurs send the error message
return res.status(400).send({
message: getErrorMessage(err)
});
}else {
// Send a JSON representation of the recipe
res.json(recipe);
}
});
};
// Create a new controller method that retrieves a list of recipes
exports.list = function(req, res) {
// User the model 'find' method to get a list of recipes
Recipe.find().sort('-created').populate('author', 'username userName').exec(function(err, recipes) {
if (err) {
// If an error occurs send the error message
return res.status(400).send({
message: getErrorMessage(err)
});
} else {
// Send a JSON representation of the article
res.json(recipes);
}
});
};
// Create a new controller method that returns an existing recipe
exports.read = function(req, res) {
res.json(req.recipe);
}
// Create a new controller method that updates an existing recipe
exports.update = function(req, res) {
// Get the recipe from the 'request' object
var recipe = req.recipe;
// Update the recipe fields
recipe.title = req.body.title;
recipe.mainImage = req.body.mainImage;
recipe.content = req.body.content;
recipe.category = req.body.category;
recipe.tags = req.body.tags;
// Try saving the updated recipe
recipe.save(function(err) {
if (err) {
// If an error occurs send the error message
return res.status(400).send({
message: getErrorMessage(err)
});
} else {
// Send a JSON representation of the recipe
res.json(recipe);
}
});
};
// Create a new controller method that deletes an existing recipe
exports.delete = function(req, res) {
// Get the recipe from the 'request' object
var recipe = req.recipe;
// Use the model 'remove' method to delete the recipe
recipe.remove(function(err) {
if (err) {
// If an error occurs send the error message
return res.status(400).send({
message: getErrorMessage(err)
});
} else {
// Send a JSON representation of the recipe
res.json(recipe);
}
});
};
// Create a new controller middleware that retrieves a single existing recipe
exports.recipeByID = function(req, res, next, id) {
// Use the model 'findById' method to find a single recipe
Recipe.findById(id).populate('author', 'username userName').exec(function(err, recipe) {
if (err) return next(err);
if (!recipe) return next(new Error('Failed to load recipe ' + id));
// If an recipe is found use the 'request' object to pass it to the next middleware
req.recipe = recipe;
// Call the next middleware
next();
});
};
// Create a new controller middleware that is used to authorize an recipe operation
exports.hasAuthorization = function(req, res, next) {
// If the current user is not the author of the recipe send the appropriate error message
if (req.recipe.author.id !== req.user.id) {
return res.status(403).send({
message: 'User is not authorized'
});
}
// Call the next middleware
next();
};
Here is express.js
// Load the module dependencies
var config = require('./config'),
http = require('http'),
express = require('express'),
morgan = require('morgan'),
compress = require('compression'),
bodyParser = require('body-parser'),
methodOverride = require('method-override'),
session = require('express-session'),
MongoStore = require('connect-mongo')(session),
flash = require('connect-flash'),
passport = require('passport');
// Define the Express configuration method
module.exports = function(db) {
// Create a new Express appllication instance
var app = express();
// Create a new HTTP server
var server = http.createServer(app);
// Use the 'NDOE_ENV' variable to activate the 'morgan' logger or 'compress' middleware
if (process.env.NODE_ENV === 'development') {
app.use(morgan('dev'));
} else if (process.env.NODE_ENV === 'production') {
app.use(compress());
}
// Use the 'body-parser' and 'method-override' middleware functions
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json({ type: 'application/*+json' }));
app.use(methodOverride('X-HTTP-Method-Override'));
// Configure the MongoDB session storage
var mongoStore = new MongoStore({
db: db.connection.db
});
// Configure the 'session' middleware
app.use(session({
saveUninitialized: true,
resave: true,
secret: config.sessionSecret
}));
// Set the application view engine and 'views' folder
app.set('views', './app/views');
app.set('view engine', 'ejs');
// Configure the flash messages middleware
app.use(flash());
// Configure the Passport middleware
app.use(passport.initialize());
app.use(passport.session());
// Load the routing files
require('../app/routes/index.server.routes.js')(app);
require('../app/routes/users.server.routes.js')(app);
require('../app/routes/recipes.server.routes.js')(app);
// Configure static file serving
app.use(express.static('./public'));
// Return the Server instance
return server;
};
AngularJS recipes controller
// Invoke 'strict' JavaScript mode
'use strict';
// Create the 'recipes' controller
angular.module('recipes').controller('RecipesController', ['$scope', '$routeParams', '$location', 'Authentication', 'Recipe',
function($scope, $routeParams, $location, Authentication, Recipe) {
// Expose the Authentication service
$scope.authentication = Authentication;
// Create a new controller method for creating new recipes
$scope.create = function() {
// Use the form fields to create a new recipe $resource object
var recipe = new Recipe({
title: this.title,
mainImage: this.mainImage,
content: this.content,
category: this.category,
tags: this.tags
});
// Use the recipe '$save' method to send an appropriate POST request
recipe.$save(function(response) {
// If an recipe was created successfully, redirect the user to the recipe's page
$location.path('recipes/' + response._id);
}, function(errorResponse) {
// Otherwise, present the user with the error message
$scope.error = errorResponse.data.message;
});
};
// Create a new controller method for retrieving a list of recipes
$scope.find = function() {
// Use the recipe 'query' method to send an appropriate GET request
$scope.recipes = Recipe.query();
};
// Create a new controller method for retrieving a single recipe
$scope.findOne = function() {
// Use the recipe 'get' method to send an appropriate GET request
$scope.recipe = Recipe.get({
recipeId: $routeParams.recipeId
});
};
// Create a new controller method for updating a single recipe
$scope.update = function() {
// Use the recipe '$update' method to send an appropriate PUT request
$scope.recipe.$update(function() {
// If an recipe was updated successfully, redirect the user to the recipe's page
$location.path('recipes/' + $scope.recipe._id);
}, function(errorResponse) {
// Otherwise, present the user with the error message
$scope.error = errorResponse.data.message;
});
};
// Create a new controller method for deleting a single recipe
$scope.delete = function(recipe) {
// If an recipe was sent to the method, delete it
if (recipe) {
// Use the recipe '$remove' method to delete the recipe
recipe.$remove(function() {
// Remove the recipe from the recipes list
for (var i in $scope.recipes) {
if ($scope.recipes[i] === recipe) {
$scope.recipes.splice(i, 1);
}
}
});
} else {
// Otherwise, use the recipe '$remove' method to delete the recipe
$scope.recipe.$remove(function() {
$location.path('recipes');
});
}
};
}
]);
And here is the form
<section data-ng-controller="RecipesController">
<div class="full-width-container">
<div class="create-recipe">
<div class="content">
<form data-ng-submit="create()" novalidate>
<h1>Create A New Recipe</h1>
<label>Title</label>
<div>
<input type="text" data-ng-model="title" placeholder="Title" id="title" required />
</div>
<label>Main Image</label>
<div>
<input type="text" data-ng-model="mainImage" placeholder="Enter image url" id="mainImage" />
</div>
<label>Recipe</label>
<div>
<textarea data-ng-model="content" placeholder="Enter Recipe" id="content"></textarea>
</div>
<label>Category</label>
<div>
<input type="text" data-ng-model="category" placeholder="Available categories (Breakfast, Brunch, Lunch, Dinner)" id="category"/>
</div>
<label>Tags</label>
<div>
<input type="text" data-ng-model="tags" placeholder="Seperate tags by a comma" id="tags"/>
</div>
<div>
<input type="submit" value="Create" class="form-submit" />
</div>
<div data-ng-show="error">
<strong data-ng-bind="error"></strong>
</div>
</form>
</div>
</div>
</div>
</section>
Thanks for the suggestions, mcpDESIGNS. I tried of your suggestions, but still no luck. When I submit it goes through successfully like before, but still when I look at the database it's still empty with the exception of id, author and date. I don't know I'm implementing your suggestion correctly or not.
The first suggestion -
<section ng-controller="RecipesController">
<div class="full-width-container">
<div class="create-recipe">
<div class="content">
<form ng-submit="create(recipe)" novalidate>
<h1>Create A New Recipe</h1>
<label>Title</label>
<div>
<input type="text" ng-model"recipe.title" placeholder="Title" id="title" required />
</div>
<label>Main Image</label>
<div>
<input type="text" ng-model"recipe.mainImage" placeholder="Enter image url" id="mainImage" />
</div>
<label>Recipe</label>
<div>
<textarea ng-model"recipe.content" placeholder="Enter Recipe" id="content"></textarea>
</div>
<label>Category</label>
<div>
<input type="text" ng-model"recipe.category" placeholder="Available categories (Breakfast, Brunch, Lunch, Dinner)" id="category"/>
</div>
<label>Tags</label>
<div>
<input type="text" ng-model"recipe.tags" placeholder="Seperate tags by a comma" id="tags"/>
</div>
<div>
<input type="submit" value="Create" class="form-submit" />
</div>
<div ng-show="error">
<strong ng-bind="error"></strong>
</div>
</form>
</div>
</div>
</div>
</section>
The second suggestion -
// Invoke 'strict' JavaScript mode
'use strict';
// Create the 'recipes' controller
angular.module('recipes').controller('RecipesController', ['$scope', '$routeParams', '$location', 'Authentication', 'Recipe',
function($scope, $routeParams, $location, Authentication, Recipe) {
// Expose the Authentication service
$scope.authentication = Authentication;
// Create a new controller method for creating new recipes
$scope.create = function() {
// Use the form fields to create a new recipe $resource object
var recipe = new Recipe({
title: $scope.title,
mainImage: $scope.mainImage,
content: $scope.content,
category: $scope.category,
tags: $scope.tags
});
// Use the recipe '$save' method to send an appropriate POST request
recipe.$save(function(response) {
// If an recipe was created successfully, redirect the user to the recipe's page
$location.path('recipes/' + response._id);
}, function(errorResponse) {
// Otherwise, present the user with the error message
$scope.error = errorResponse.data.message;
});
};
// Create a new controller method for retrieving a list of recipes
$scope.find = function() {
// Use the recipe 'query' method to send an appropriate GET request
$scope.recipes = Recipe.query();
};
// Create a new controller method for retrieving a single recipe
$scope.findOne = function() {
// Use the recipe 'get' method to send an appropriate GET request
$scope.recipe = Recipe.get({
recipeId: $routeParams.recipeId
});
};
// Create a new controller method for updating a single recipe
$scope.update = function() {
// Use the recipe '$update' method to send an appropriate PUT request
$scope.recipe.$update(function() {
// If an recipe was updated successfully, redirect the user to the recipe's page
$location.path('recipes/' + $scope.recipe._id);
}, function(errorResponse) {
// Otherwise, present the user with the error message
$scope.error = errorResponse.data.message;
});
};
// Create a new controller method for deleting a single recipe
$scope.delete = function(recipe) {
// If an recipe was sent to the method, delete it
if (recipe) {
// Use the recipe '$remove' method to delete the recipe
recipe.$remove(function() {
// Remove the recipe from the recipes list
for (var i in $scope.recipes) {
if ($scope.recipes[i] === recipe) {
$scope.recipes.splice(i, 1);
}
}
});
} else {
// Otherwise, use the recipe '$remove' method to delete the recipe
$scope.recipe.$remove(function() {
$location.path('recipes');
});
}
};
}
]);
Here is my recipes.server.routes.js
var users = require('../../app/controllers/users.server.controller'),
recipes = require('../../app/controllers/recipes.server.controller');
var needsRole = function(role) {
return function(req, res, next) {
if (req.user && req.user.role === role)
next();
else
res.status(401, 'Unauthorized');
};
};
// Deine the routes 'module' method
module.exports = function(app) {
// Setting the 'recipes' base routes
app.route('/api/recipes')
.get(recipes.list)
.post(users.requiresLogin, needsRole("admin"), recipes.create);
// Set up the 'recipes' parameterized routes
app.route('/api/recipes/:recipeId')
.get(recipes.read)
.put(users.requiresLogin, recipes.hasAuthorization, recipes.update)
.delete(users.requiresLogin, recipes.hasAuthorization, recipes.delete);
// Set up the 'recipeId' parameter middleware
app.param('recipeId', recipes.recipeByID);
};
Upvotes: 1
Views: 667
Reputation: 66971
So from what I can see, the problem is in your $scope.create
function.
In your function, you're creating your object that you want to send to the database, but the big problem is: this.
Since you're creating a resource there, the this
might be referring to something totally different, not your $scope
.
You have 2 options, either change them all to:
title : $scope.title,
mainImage : $scope.mainImage,
// etc
Or:
Try simply creating an object
in your View of that entire form. For example:
<input ng-model="recipe.title" />
<input ng-model="reciple.mainImage" />
// etc
This way you can just pass the entire object to your ng-submit
create(recipe)
.
When you're in the controller you can then access each property of that object with recipe.
, much easier. You could even pass that entire thing to your resource and let it map itself. new Recipe(recipe);
for example.
Upvotes: 1