Mean stack client side update - node.js

I'm having trouble implementing the client side update CRUD logic. The fields are currently being deleted with the set up as it is. What am I missing?
My angular:
$scope.editService = function(id) {
$http.put('/api/hc/' + id,
{title: 'new',
shortname: 'new',
summary: 'new',
description: 'new'}
)
.success(function(data) {
})
.error(function(data) {
console.log('Error: ' + data);
});
};
My express: Nothing seems to be getting passed the the JSON values, for some reason all of the fields and keys are wiped clean, leaving only the _id and _v keys and values.
.put(function(req, res) {
Service.findById(req.params._id, function(err, service) {
if (err)
res.send(err);
service.title = req.body.title; // update the items info
service.summary = req.body.summary;
service.shortname = req.body.shortname;
service.description = req.body.description;
// save the items
service.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Service updated!' });
});
});
})
My view
<form name="editForm" ng-submit="editService(service._id)" ng-repeat="service in services
filter:json">
<input type="text" placeholder="{{ service.title}}" ng-model="serviceTitle" required>
<input type="text" placeholder="{{ service.shortname}}" ng-model="serviceShortname" required>
<input type="text" placeholder="{{ service.description}}" ng-model="serviceSummary" required>
<textarea type="text" placeholder="{{ service.summary}}" ng-model="serviceDescription" required></textarea>
<button type="submit">Edit</button>
</form>

You're not actually putting data in the example you gave.
$http.put('/api/hc/' + id)
should be
$http.put('/api/hc/' + id, formData)
where formData is whatever object you glean from the form fields you want to send up the pipe. Also, have a gander at angular's $[resource][1] service, it's a much cleaner (imo) way to do REST clients than using $http directly.

Related

i am getting an error while i am updating my data using express node.js

i am trying to update my category name but when i click on the submit button it gives me an error however i tested my code using postman and it actually updated but in that case i have to pass the product's id manually.
the callback function of the update category:
exports.updateCategory = (req, res, next) => {
console.log('GET update CATEGORY /update-category');
if (!req.body) {
return res
.status(400)
.send({ message: "Data to update can not be empty" })
}
const id = req.params.id; //req.body.id >> body means in the html/ejs file the name should be id
categorySchema.findByIdAndUpdate(id, req.body, { userFindAndModify: false })
.then(data => {
if (!data) {
res.status(404).send({ message: `Cannot Update user with ${id}` })
}
//updating the data
else {
res.render('category/edit_category.ejs', {
category: data //variable that i am going to use is category in the ejs
})
}
})
.catch(err => {
res.status(500).send({ message: "Error update user information" })
})
}
the update form >>
<form action='<%=`/halalMunchies/update-category/${category._id}`%>' method="post">
.
.
.
<label class="control-label" for="categoryName"> Category Name </label>
<input type="hidden" name="id" value="" id="id">
<input id="categoryName" class="form-control" value="<%= category.categoryName%>" name="categoryName" required autofocus="autofocus" />
the router.js
//PUT UPDATE CATEGORY
router.put('/update-category/:id', categoriesController.updateCategory);
router.get('/update-category/:id', categoriesController.updateCategory);
the error message on page is :
Cannot POST /halalMunchies/update-category/618632e3a5ad00fa5368ad5c
As the error message tells you, you did not provide a post endpoint in your router. Since you're using the post method in your form, you need to add the following in your router.js:
router.post('/update-category/:id', categoriesController.updateCategory);

Update data in mongodb with input valors

I have three input to obtain three different values. Im using express.js , node.js, mongodb and ejs templates.
<form action="/save-profile/<%= user.id %>/<%= user.name %>/<%= user.lastname %>/<%= user.description %>" method="POST">
<div class="input-group mb-3">
<span class="input-group-text" id="basic-addon1">Name</span><%= user.username %>
<input type="text" class="form-control" placeholder="'John'" aria-label="Username" name="username">
<span class="input-group-text">lastName</span><%= user.lastname %>
<input type="text" class="form-control" placeholder="" aria-label="Server" name="lastname">
</div>
<div class="input-group">
<span class="input-group-text">Description:</span>
<textarea class="form-control" aria-label="With textarea" placeholder="" name="description"><%= user.description %></textarea>
</div>
</p><br>
<button class="btn btn-primary mb-10 btn-lg">Save</button>
</div>
</div>
In js file:
router.post('/save-profile', async (req, res) => {
const profile_id = await User.findById({ _id: req.body.id })
const updatedName = await User.findOneAndUpdate({ username: req.body.username})
const updatedlastname = await User.findOneAndUpdate({ apellido: req.body.lastname })
const updatedDescription = await User.findOneAndUpdate({ description: req.body.description })
console.log(profile_id,updatedName,updatedApellido,updatedDescription)
res.redirect('/profile')})
I tried to do it with a get but it didn't work
Firstly, action attribute in the form tag accepts the path where you are handling the form data. You only need to pass the user.id, there's no need to pass the other fields for this use-case.
<form action="/save-profile/<%= user.id %>" method="POST">
...
</form>
Secondly, in your route handler, the database record can be updated using only a single findOneAndUpdate call. You don't need to call it multiple times for every field if you're only going to update a single record.
The path written in action attribute will appear as /save-profile/1, for instance, in your route handler. Value preceding /save-profile/ i.e. 1 can be accessed by modifying the path in route handler as /save-profile/:id and in the callback you can get it by req.params.id
Finally you have this.
router.post('/save-profile/:id', async (req, res) => {
const response = await User.findOneAndUpdate(
{ _id: req.params.id },
{
username: req.body.username,
apellido: req.body.lastname,
description: req.body.description
},
{ new: true }
)
// Do something with response
res.redirect('/profile')
})

Trouble rendering of documents with referenced fields on Vue.js mongoose

Recently, I picked up Vue.js and Mongoose to develop a personal project to mainly track Ingredients' on hand quantities on a certain online game.
Different dishes require different ingredients. Lotus Seed - Bird Egg soup, Jewelry Soup and Jade Parcels all require different number of Lotus Head as its ingredients.
I update an ingredient's quantity by using updateOne on ingredients collection.
Unfortunately, I originally embedded the ingredients on foods / dishes, which I realized problematic
recently, coz literally you just count ingredients what you currently have.
So a food document now looks like this
{
"_id" : ObjectId("5fca4ada32195d5814510242"),
"foodName" : "Lotus Seed and Bird Egg Soup",
"onHandQty" : 20,
"ingredients" : [
"5fca481432195d581451023f",
"5fca483932195d5814510240",
"5fca48a232195d5814510241"
]
}
I read about Mongoose's populate(), and tested to output one food/dish. Unfortunately there's nothing coming out of Vue.js front-end after trying that code.
server/models/Food.js
const { Router } = require('express');
const FoodItem = require('../../models/Food');
const IngredientItem = require('../../models/Ingredient');
const router = Router()
router.get('/', async(req, res) =>{
try {
const food = await FoodItem.findOne({
foodName: 'Lotus Seed and Bird Egg Soup'
}).populate('ingredients').
exec(function (err, food) {
if (err) return handleError(err);
console.log('The food is %s', food.foodName);
});
res.send(food);
} catch (error) {
res.status(500).json({
message: error.message
})
}
});
module.exports = router
A portion of component where ingredients are rendered
client/src/components/Food.vue
<div class="tile is-ancestor">
<div class="tile">
<div class="tile is-parent">
<div class="tile is-child box">
<template v-if="food.ingredients">
<div class="ingredients-block">
<p>Ingredients List:</p>
<ul class="ingredients-list">
<li class="row" v-for="ingredient in food.ingredients" :key="ingredient._id">
<div id="ingredient-image-container">
<img class="image is-64x64" :src="require(`../assets/images/food_inv/${ingredient.imagePath}.png`)" alt="ingredient.ingredientName" :title="ingredient._id">
{{ingredient.ingredientName}}
</div>
<div class="required-qty-container">
<!-- <i class="material-icons" id="required-inner-qty">food_bank</i> -->
Required:
{{ ingredient.requiredQty }}
</div>
<div class="on-hand-qty-container">
<p>On Hand:</p>
<input v-if="ingredient.onHandQty < ingredient.requiredQty" class="input is-danger on-hand-input" type="number" v-model="ingredient.onHandQty" min="0">
<input v-else class="input is-primary on-hand-input" type="number" v-model="ingredient.onHandQty" min="0">
<!-- <button class="button is-primary save-button" #click="test({ingredient_id: ingredient._id, onhandqty: ingredient.onHandQty})"><i class="material-icons">save</i></button> -->
<button class="button is-primary save-button" #click="$emit('update-qtys', {ingredient_id: ingredient._id, onhandqty: ingredient.onHandQty})"><i class="material-icons">save</i></button>
</div>
</li>
</ul>
</div>
</template>
</div>
</div>
</div>
</div>
Whole project on Github: Food Inventory
Quick Fixes,
change your food schema's ingredients field from object to array,
const foodSchema = new mongoose.Schema(
{
foodName: String,
imagePath: String,
effect: String,
onHandQty: Number,
// correct this to array
ingredients: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Ingredient'
}]
}
);
there are 2 ways to call mongoose methods first exec() with callback and second without exec() callback,
exec with callback that you have used buy need to send response (res.send(food) or res.json(food)) from inside the exec call function,
router.get('/', async(req, res) =>{
try {
await FoodItem.find()
.populate('ingredients')
.exec(function (err, food) {
if (err) return handleError(err);
console.log('The food is %s', food);
// put response here
res.json(food);
});
} catch (error) {
res.status(500).json({ message: error.message })
}
});
exec without call back
router.get('/', async(req, res) =>{
try {
const food = await FoodItem.find()
.populate('ingredients')
.exec();
res.json(food);
} catch (error) {
res.status(500).json({ message: error.message })
}
});

How to combine schema model with file upload in Node.js and MongoDB?

Good day everyone, I am building a web application to store employee information in a local database. I have written the necessary code so that the client can input the required information such as Name, email, phone number etc. via a form and save it to MongoDB. Now I would like to add a file upload feature so that documents can be uploaded alongside employee information, such as insurance policies, passport copies etc. I am struggling to find a way to implement this as a lot methods online show implementations of CRUD and file uploads separately. Is there anyone experienced who would be able to suggest an implementation approach? I'm new to Node dev and not familiar with the vast selection of available frameworks. So far I have the following set up:
Frameworks used: Express, Mongoose, Express-Handlebars (view engine), Bootstrap
employee.Model:
const mongoose = require('mongoose');
const mongoURI = 'mongodb://localhost:27017/testDB'
const conn = mongoose.createConnection(mongoURI, {useNewUrlParser: true});
Schema = mongoose.Schema;
var employeeSchema = new Schema({
fullName: {
type: String,
required: true
},
email: {
type: String
},
mobile: {
type: String
},
city: {
type: String
}
});
const Employee = conn.model('Employee', employeeSchema);
module.exports = Employee;
Employee Controller:
const mongoose = require ('mongoose');
const express = require ('express');
var router = express.Router();
const Employee = require('../models/employee.model');
router.post('/', (req, res) => {
insertRecord(req, res);
});
function insertRecord (req, res) {
var employee = new Employee();
employee.fullName = req.body.fullName;
employee.email = req.body.email;
employee.mobile = req.body.mobile;
employee.city = req.body.city;
employee.save((err, doc) =>{
if (!err)
res.redirect('employee/list');
else {
console.log('Error during record insertion: ' + err);
}
});
}
Handlebars View:
<h3>{{viewTitle}}</h3>
<form action="/employee" method="POST" autocomplete="off">
<div class="form-group">
<label>Full Name</label>
<input type="text" class="form-control" name="fullName"
placeholder="Full Name">
</div>
<div class="form-group">
<label>Email</label>
<input type="text" class="form-control" name="email"
placeholder="Email">
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label>Mobile</label>
<input type="text" class="form-control" name="mobile"
placeholder="Mobile">
</div>
<div class="form-group col-md-6">
<label>City</label>
<input type="text" class="form-control" name="city"
placeholder="City">
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-info"><i class="fa fa-
database"></i> Submit</button>
</div>
</form>
Uploaded files are inputs just like others, they just need the specific enctype="multipart/form-data" attribute on the HTML side, and a proper handling on the server.
And easy-to-grasp demonstration using Multer (based on their own examples actually):
const upload = require('multer')({dest: 'uploads/'}); // npm i multer
const fileUploadMiddleware = upload.fields({
{name: 'resume', maxCount: 1},
{name: 'insurance', maxCount: 4}
});
router.post('/', fileUploadMiddleware, (req, res) => {
insertRecord(req, res);
});
function insertRecord(req, res) {
...
console.log(req.files.resume[0].mimetype); // this is resume file type
console.log(req.files.insurance[0].path); // this is first insurance file path
console.log(req.body.fullName); // this still works
}

Node.js (Express) Form Clears on Submission

I am working on a really basic registration form in Node.js (with Express), and I am trying to find the easiest way to provide basic form validation. I've gone with "Express-Validator", which seems to do a fine job. However, my goal is to simply show any validation messages that are required and to leave the values entered by the user alone.
It seems that the request information is not making it back into the res.render, which I guess makes sense. However, I've looked everywhere I can think of and I can't find any reference that discusses how to keep form fields populated after showing error messages.
Below is a small snippet describing my approach:
post: function(req, res){
var userName = req.body.username;
var password = req.body.password;
//Validate input
req.assert("username", 'Invalid email address.').isEmail();
req.assert("password", 'Password cannot be empty.').notEmpty();
req.assert("passwordConfirm", 'Passwords entered do not match!').equals(password);
//Make sure we have no validation errors
var pageErrors = req.validationErrors();
if(!pageErrors)
{
userModel.CreateUser(userName, password, function(err){
if(err)
{
//there was a problem inserting new user... probably already exists
//will need to check the error to confirm
var dbErrorMessage = "Could not insert record into database!";
if(err.code === 11000)
{
//this is a duplicate entry
dbErrorMessage = "A user with that email address already exists!";
}
res.render('register.html', { pageErrors: [{msg: dbErrorMessage }]});
}
else
{
res.render('register.html', { successMessage: successMessage });
}
});
}
else
{
res.render('register.html', { pageErrors: pageErrors });
}
Unfortunately, you have to repopulate the form manually. If you get any page errors, you will pass back the form values to the view.
if(!pageErrors)
{
// ...
}
else
{
res.render('register.html', {
pageErrors: pageErrors,
userName: userName
});
}
And in your view, you would do a simple check to see if their are any errors and repopulate accordingly. You would have to keep track of what errors are produced for each form field.
<% if (userNameError) { %>
<input type="text" name="userName" value="<%- userName %>" />
<% } else { %>
<input type="text" name="userName" />
<% } %>
Another popular way is to send your form via ajax to to the server, and do all your validations. If there is an error, the entered form data remains and you would show the error, otherwise redirect after the successful login. Below is an example of how to submit a form with javascript.
$("#login-button").live("submit", function (e) {
// this will prevent the form from being uploaded to the server the conventioanl way
e.preventDefault();
// the form data
var data = $(this).serialize();
// this logs the user in
$.ajax({
type: 'POST',
url: BASE_URL + '/login',
data: data,
dataType: 'json',
success: function (data, status) {
// successful
},
});
// superfluous fallback
return false;
});
There is an easy way is you are using
app.use(express.bodyParser()) and app.use(expressValidator());
You can use req.body
res.render('register.html', {
pageErrors: pageErrors,
validated: req.body
});
And I'm not sure which templating language you are using but you could do something like..
<input type="text" name="userName" value="<%= pageErrors.userName.value || validated.userName %>" />
This then gives back the good input if ok or the bad input if it needs correcting.
You can get this done using connect-flash
Below are the code snippets in different files in order to get the values entered by the user back in form when validations fail while doing signup with passport.
Run this below command to add new package into package.json
npm install connect-flash --save
app.js
var flash = require('connect-flash');
app.use(flash()); // add this above passport initialize
app.use(passport.initialize());
app.use(passport.session());
config/passport.js (Please focus on form data loading into flash)
passport.use('local.signup', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
}, function (req, email, password, done) {
req.checkBody('first_name', 'Firstname is missing').notEmpty();
req.checkBody('last_name', 'Lastname is missing').notEmpty();
req.checkBody('email', 'Invalid email').notEmpty().isEmail();
req.checkBody('password', 'Password is too short. Minimum size is 6.').notEmpty().isLength({min:6});
req.checkBody('confirm_password', 'Password and confirm password didn\'t not match').equals(req.body.password);
var errors = req.validationErrors();
if (errors) {
var messages = [];
errors.forEach(function(error) {
messages.push(error.msg);
});
req.flash('formdata', req.body); // load form data into flash
return done(null, false, req.flash('error', messages));
}
User.findOne({'email': email}, function (err, user) {
if (err) {
req.flash('formdata', req.body); // load form data into flash
return done(err);
}
if (user) {
req.flash('formdata', req.body); // load form data into flash
return done(null, false, {message: 'Email is already in use.'});
}
var newUser = new User();
newUser.first_name = req.body.first_name;
newUser.last_name = req.body.last_name;
newUser.email = email;
newUser.password = newUser.encryptPassword(password);
newUser.save(function(err, result) {
if (err) {
return done(err);
}
return done(null, newUser);
});
});
}));
routes/index.js (Please focus on form data in flash loaded back into a variable)
router.get('/signup', function (req, res, next) {
var messages = req.flash('error');
var formdata = req.flash('formdata'); // Get formdata back into a variable
res.render('user/signup', {csrfToken: req.csrfToken(),
messages: messages, // pass it here to access in view file
hasErrors: messages.length > 0,
formData: formdata[0]
});
});
router.post('/signup', passport.authenticate('local.signup', {
badRequestMessage: 'Please fill the form with all details',
failureRedirect: '/user/signup',
failureFlash: true
}), function (req, res, next) {
if (req.session.oldUrl) {
var oldUrl = req.session.oldUrl;
req.session.oldUrl = null;
res.redirect(oldUrl);
} else {
res.redirect('/user/profile');
}
});
views/signup.hbs (Please focus on values in input elements)
<form class="wow fadeInUp animated" data-wow-delay=".7s" action="/user/signup" method="post" >
<input type="text" placeholder="First Name" name="first_name" value="{{ formData.first_name }}">
<input type="text" placeholder="Last Name" name="last_name" value="{{ formData.last_name }}">
<input type="text" class="email" placeholder="Email Address" name="email" value="{{ formData.email }}">
<input type="password" name="password" value="" class="lock" placeholder="Password">
<input type="password" name="confirm_password" value="" class="lock" placeholder="Confirm Password">
<input type="hidden" name="_csrf" value="{{ csrfToken }}">
<input type="submit" name="Register" value="Register"></form>
Hope this helps.
set a variable for all the input, for example
var inputData = {
firstname : req.body.firstname,
lastname : req.body.lastname,
email : req.body.email,
username : req.body.username,
password : req.body.password,
password_confirmation : req.body.password_confirmation,
agreetoterms: req.body.agreetoterms
}
and then pass that variable to the view
res.render('register.html', { pageErrors: [{msg: dbErrorMessage }], inputData: inputData });
then in your view
value="<%= inputData.userName %>"
if you are using jade and form Validator from npm , the best part is that you can an if statement in jade and then you simply check if error then with res.render we send the objects also . See This
if(errors){
res.render('register',{
errors : errors,
name : name,
email : email,
username : username,
password : password,
password2 : password2
});
And in jade you do this
input.form-control(name='name',type='text',placeholder='Enter Name',value = (errors ? '#{name}':''))
so if there is errors value will set to variable in name which will rendered when we send back
I think you can also be done in Angular2/Angular.js
Well, there is a simple way to achieve this. Do following in controller:
else{
res.render('register.html', { pageErrors: pageErrors, inputData: req.body});
}
Now, in your ejs (or do it according to your view engine) file do following:
<input type="text" name="name" value="<% if(typeof pageErrors !== "undefined"){ %> <%- inputData.name %> <% } %>" class="form-control">
check http://www.quietless.com/kitchen/building-a-login-system-in-node-js-and-mongodb/
on register.html make this
var data = {};
data.user = $('#user-input').val();
data.email = $('#email-input').val();
data.pass = $('#pass-input').val();
$.ajax({ url: '/signup'
, type: 'POST'
, data: JSON.stringify(data)
, contentType: 'application/json'
, dataType: 'html'
})
.done(function(data) {
if (data == 'ok') {
$('#content').html('You are registered!');
}
else $('#account-form-container').append('<br>error:' + data);
});
there might be error like: CANNOT POST /
in this case the author of tutorial on link above uses lib $.ajaxForm
you can also use https://github.com/felixge/node-formidable
or $('#myform').submit() replace to $('#submit-a-link').click()

Resources