I am relatively new to coding and have been working on a restful routes exercise for an online course using a MEN stack. In the exercise, I am trying to use a default image when no data is entered into the /new route form. I have already indicated a default image in my mongoose schema, but it doesn't seem to be working when I leave the image field blank on my form. It creates a new post with a "unknown" src attribute. I have attached code for my schema, my /new ejs form, and new and create routes. Thanks for any help!
(MONGOOSE SCHEMA)
var catSchema = new mongoose.Schema({
name : String,
description: String,
ranking: Number,
image: {type: String, default: "https://i.cubeupload.com/SnsWs2.png"},
created: {type:Date, default: Date.now}
(NEW AND CREATE ROUTES)
app.post("/cats", function(req,res){
Cat.create(req.body.cat, function(err,newBlog){
if(err){
res.redirect("/cats/new")
}else{
res.redirect("/cats")
}
});
});
(NEW ROUTE)
<%- include("partials/header") %>
<div class="container">
<form action="/cats" method="POST">
<div class="form-group">
<label required for="cat[name]">Name</label>
<input type="text" name="cat[name]" class="form-control" id="name" aria-describedby="emailHelp" placeholder="Enter cat name">
</div>
<div class="form-group">
<label for="cat[description]">Description</label>
<input type="text" class="form-control" name="cat[description]" placeholder="description">
</div>
<div class="form-group">
<label for="cat[ranking]">Ranking</label>
<input type="number" class="form-control" name="cat[ranking]" placeholder="Password">
</div>
<div class="form-group">
<label for="cat[image]">Ranking</label>
<input type="text" class="form-control" name="cat[image]" placeholder="image URL">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
Related
I am trying to submit a form to create a new Appointment in my mongo database. When the form submits, I get a validation error telling me that certain fields are required. I have console.logged the fields from the form (as you can see under "Controllers") and they are empty. I have tried tracing the submission process, but I have no idea where the form is losing the information. I currently have the exact same setup for submitting a new car into my database and it works perfectly. The same console.logs in my car submission controller print out the new car object. I provided my routing, appointment model, controllers and form code below. I successfully reach the console.logs in my controller so I do not think it is a routing issue. I have also included the exact error message I get at the end of this post. I believe the form loses all data sometime between submission and validation, but I am not sure how to debug that, and am totally lost on how to fix this issue.
Routing:
const express = require('express');
const router = express.Router();
const appts = require('../controllers/appointments');
const catchAsync = require('../utils/catchAsync');
const { isLoggedIn, isAuthor } = require('../middleware');
const Appt = require('../models/appointments');
router.route('/')
.get(catchAsync(appts.index))
.post(catchAsync(appts.createAppt));
router.get('/new',appts.renderNewForm);
module.exports = router;
Controller:
const Appt = require('../models/appointments');
module.exports.createAppt = async (req,res) =>{
const appt = new Appt(req.body.appt);
console.log("Body: ",req.body);
console.log("Body: ",req.body.appt);
console.log("appt: ",appt);
//campground.author = req.user._id;
await appt.save();
req.flash('success', 'Successfully sent! Thank you for scheduling some time to meet with us, see you soon!');
res.redirect(`/cars`);
}
Model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ApptSchema = new Schema({
name:String,
email:{
type:String,
required: true
},
phone:{
type:Number,
required: true
},
date:{
type:Date,
required: true
},
time:{
type:String,
required: true
},
interests: String,
scheduleTime:Date
});
module.exports = mongoose.model('Appointment',ApptSchema);
Form:
<% layout('layouts/boilerplate')%>
<div class="display-3 text-center">SCHEDULE AN APPOINTMENT ONLINE:</div>
<div class="row">
<div class="col-md-6 offset-md-3">
<form action="/appointments" method="POST" novalidate class="validated-form" enctype="multipart/form-data">
<div class="mb-3">
<label class="form-label" for="name">Name</label>
<input class="form-control" type="text" id="name" name="appt[name]" required>
<div class="valid-feedback">
Looks good!
</div>
</div>
<div class="mb-3">
<label class="form-label" for="email">Email</label>
<input class="form-control" type="email" id="email" name="appt[email]" required>
<div class="valid-feedback">
Looks good!
</div>
</div>
<div class="mb-3">
<label class="form-label" for="phone">Phone Number:</label>
<input class="form-control" type="tel" id="phone" name="appt[phone]" pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}" required>
<small>Format: 555-456-7890</small>
</div>
<div class="mb-3">
<label for="date" class="form-label">Please Enter your Prefered Date</label>
<input class="form-control" type="date" id="date" name="appt[date]" required>
</div>
<div class="mb-3">
<label for="time" class="form-label">Please Select a Time Slot</label>
<select class="form-control" id="time" name="appt[time]" required>
<option value="morning">Morning (10-11:30am)</option>
<option value="afternoon">Afternoon (12-2:30pm)</option>
<option value="evening">Evening (3-4:30pm)</option>
</select>
</div>
<div class="mb-3">
<label class="form-label" for="interests">What are you interested in?</label>
<textarea class="form-control" type="text" id="interests" name="appt[interests]" placeholder="example: All wheel drive family car with great mileage." required></textarea>
<div class="valid-feedback">
Looks good!
</div>
</div>
<div class="mb-3">
<label><strong>Appoitnment Request will be sent to Just Cars. You will shortly receive an email with a specified time within your time slot for your appointment.</strong></label>
</div>
<div class="mb-3 text-center">
<button class="btn btn-success">Request Appointment</button>
</div>
</form>
</div>
</div>
<div class="container">
<div class="row text-center" style="margin-top:40px; margin-bottom:50px;">
<div class="display-6"><strong>OTHER SCHEDULING OPTIONS, OR RESCHEDULE AN APPOINTMENT: </strong></div>
<div class="mt-4">
<a class = "btn btn-lg btn-outline-dark mx-3" href="tel:3853237118"><i class="fas fa-phone"></i> CALL US AT (385)323-7118</a>
<a class = "btn btn-lg btn-outline-dark mx-3" href="mailto:"><i class="fas fa-envelope"></i> SEND US AN EMAIL</a>
</div>
</div>
</div>
<hr class="my-4">
<div class="text-center"><a class="btn btn-dark" href="/cars">Back to All Cars</a></div>
Error from Browser
ValidationError: Appointment validation failed: time: Path `time` is required., date: Path `date` is required., phone: Path `phone` is required., email: Path `email` is required.
at model.Document.invalidate (/Users/Joselito/Desktop/JustCarsFinal/node_modules/mongoose/lib/document.js:2626:32)
at /Users/Joselito/Desktop/JustCarsFinal/node_modules/mongoose/lib/document.js:2446:17
at /Users/Joselito/Desktop/JustCarsFinal/node_modules/mongoose/lib/schematype.js:1225:9
at process._tickCallback (internal/process/next_tick.js:61:11)
Changed enctype from "multipart/form-data" to "application/x-www-form-urlencoded" in my edit form and now everything works.
If anyone is able to explain this to me, I would very much appreciate it. I know that forms of enctype="multipart/form-data" are used for forms that require file input, but I thought they also work on forms without files. Is file input a requirements for "multipart/form-data" enctypes?
So I have a post method in my nodejs application and I want to send a form from my ejs file to an array in one of my models. In the ejs file, I have a select option for the user to enter a list of subjects. Once submitted, I cannot get the values from the select option. I tried to store it in an array but it shows undefined. I have an instructor model where it stores the subjects fields in a list of string array. I am not sure what to do. Here is what I have:
admin.js
router.get('/newInstructor', isLoggedIn, function(req, res) {
res.render('newInstructor.ejs');
});
router.post('/newInstructor', isLoggedIn, function(req, res) {
var lisOfSubjects = [req.body.listOfSubjects];
console.log('Subjects -> ' + lisOfSubjects);
var newInstructor = new Instructor ({
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
officeNumber: req.body.officeNum,
cellNumber: req.body.cellNum,
address: {
address1: req.body.address,
address2: req.body.address2,
city: req.body.city,
state: req.body.state,
zipCode: req.body.zip
},
subjects: lisOfSubjects,
officeLocation: req.body.officeLocation
});
console.log(newInstructor);
});
ejs view file
<!-- js function to insert and remove from the select option-->
<script>
function insertTeachingSelect() {
var text = document.getElementById("subject").value;
console.log(text);
var select = document.getElementById("listOfSubjects");
select.options[select.options.length] = new Option(text, 'Value1');
}
function removeTeachingSelect() {
var removeSelect = document.getElementById("listOfSubjects");
removeSelect.remove(removeSelect.selectedIndex);
}
</script>
......
<div class="form-group">
<div class="form-row">
<div class="col-md-4">
<div class="form-label-group">
<input type="text" id="subject" name="subject" class="form-control">
<label for="Enter subject">Enter subject</label>
</div>
</div>
<div class="col-md-2">
<div class="form-label-group">
<input type="button" onclick="insertTeachingSelect()" class="btn btn-primary btn-block" value="Add more">
<input type="button" onclick="removeTeachingSelect()" class="btn btn-primary btn-block" value="Remove">
</div>
</div>
<div class="col-md-6">
<div class="form-label-group">
<input type="text" id="officeLocation" name="officeLocation" class="form-control" placeholder="Office location" required="required">
<label for="officeLocation">Office location</label>
</div>
</div>
</div>
</div>
<div class="form-group">
<div class="form-row">
<div class="col-md-4">
<div class="form-label-group">
<select id="listOfSubjects" name="listOfSubjects"size="5"></select>
</div>
</div>
</div>
</div>
<div class="form-group ">
<div class="form-row">
<div class="col-md-6 mx-auto">
<div class="form-label-group">
<button type="submit" class="btn btn-primary btn-block">Submit</button>
Here I'm using the nested property Schema of mongoose to nest all the "stages" then using formGroup in typescript.
I'm not sure what should be the formControlName for the radio
buttons as it needs to be similar?
Also, not sure if I'm missing anything?
Code:
// Schema
const userSchema = new Schema({
name: { type: String, required: true },
email: { type: String, required: true },
stages: {
stageOne: { type: Boolean, default: false },
stageTwo: { type: Boolean, default: false },
stageThree: { type: Boolean, default: false }
});
// typescript/component.ts
this.form = this.formBuilder.group({
name: [''],
email: [''],
stages: this.formBuilder.group({
stageOne: [null],
stageTwo: [null],
stageThree: [null]
});
});
// html/template.html
<form [formGroup]="form">
<div class="form-group">
<input name="name" class="form-control" id="name" type="text" [(ngModel)]="name" formControlName="name">
<input name="email" class="form-control" id="email" type="email" [(ngModel)]="email" formControlName="email">
<div class="custom-control custom-radio">
<input type="radio" id="stageOne" class="custom-control-input" [(ngModel)]="stageOne" formControlName="???">
<label class="custom-control-label" for="stageOne">1</label>
</div>
<div class="custom-control custom-radio">
<input type="radio" id="stageTwo" class="custom-control-input" [(ngModel)]="stageTwo" formControlName="???">
<label class="custom-control-label" for="stageTwo">2</label>
</div>
<div class="custom-control custom-radio">
<input type="radio" id="stageThree" class="custom-control-input" [(ngModel)]="stageThree" formControlName="???">
<label class="custom-control-label" for="stageThree">3</label>
</div>
</div>
</form>
It is no different than with your other input form controls. However, the ControlValueAccessor will get the value from your radio button so you do not require the additional ngModel directives. When you submit your form the from object itself, this.form.value, will contain all your form values.
Reactive Form
this.form = this.formBuilder.group({
name: [''],
email: [''],
stages: ['1']
});
Template
<form [formGroup]="form">
<div class="form-group">
<input name="name" class="form-control" id="name" type="text" formControlName="name">
<input name="email" class="form-control" id="email" type="email" formControlName="email">
<div class="custom-control custom-radio">
<input type="radio" id="stageOne" class="custom-control-input" formControlName="stages" [value]="1">
<label class="custom-control-label" for="stageOne">1</label>
</div>
<div class="custom-control custom-radio">
<input type="radio" id="stageTwo" class="custom-control-input" formControlName="stages" [value]="2">
<label class="custom-control-label" for="stageTwo">2</label>
</div>
<div class="custom-control custom-radio">
<input type="radio" id="stageThree" class="custom-control-input" formControlName="stages" [value]="3">
<label class="custom-control-label" for="stageThree">3</label>
</div>
</div>
</form>
Ok so in my app I am trying to allow image uploading using multer. My app is built using node.js and my database is using mongodb. When I create an account and select an image for the avatar image, it creates the account but automatically uses the no-image.png file I have setup in case someone doesn't select an image. Here is the code... Any help will be awesome.
// handle signup logic
router.post("/register", function(req, res) {
upload(req, res, function(err) {
if(err){
req.flash("error", err.message);
return res.redirect("/register");
}
var newUser = new User({
username: req.body.username,
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
bio: req.body.bio
});
if(typeof req.file !== "undefined") {
newUser.avatar = '/uploads/userImg/' + req.file.filename;
} else {
newUser.avatar = '/uploads/userImg/no-image.png';
}
console.log(newUser);
if(req.body.adminCode === process.env.ADMINCODE) {
newUser.isAdmin = true;
}
if(req.body.answer !== process.env.SECRET){
req.flash("error", "answer the question");
return res.redirect("back");
} else {
User.register(newUser, req.body.password, function(err, user){
if(err){
return res.render("register", {error: err.message});
}
passport.authenticate("local")(req, res, function(){
req.flash("success", "Welcome to Let's Camp " + user.username);
res.redirect("/campgrounds");
});
});
}
});
});
var multer = require("multer");
var storage = multer.diskStorage({
destination: function(req, file, callback) {
callback(null, './public/uploads/userImg');
},
filename: function(req, file, callback) {
callback(null, Date.now() + file.originalname);
}
});
var upload = multer({ storage : storage}).single('image');
<% include ./partials/header %>
<div class="row">
<div class="col-xs-8 col-xs-offset-2">
<form action="/register" method="post">
<h1 class="text-center">Sign Up</h1>
</div>
<div class="row">
<div class="col-xs-4 col-xs-offset-2">
<div class="form-group">
<label for="firstName">First Name</label>
<input id="firstName" class="form-control" type="text" name="firstName" placeholder="First Name*" required>
</div>
</div>
<div class="col-xs-4 col-xs-offset-0">
<div class="form-group">
<label for="lastName">Last Name</label>
<input id="lastName" class="form-control" type="text" name="lastName" placeholder="Last Name*" required>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-4 col-xs-offset-2">
<div class="form-group">
<label for="email">Email</label>
<input id="email" class="form-control" type="email" name="email" placeholder="Email*" required>
</div>
</div>
<div class="col-xs-4 col-xs-offset-0">
<div class="form-group">
<label for="avatar">Avatar Image URL</label>
<input id="avatar" class="form-control" type="file" name="avatar">
</div>
</div>
</div>
<div class="row">
<div class="col-xs-4 col-xs-offset-2">
<div class="form-group">
<label for="username">Username</label>
<input id="username" class="form-control" type="text" name="username" placeholder="Username*" required>
</div>
</div>
<div class="col-xs-4 col-xs-offset-0">
<div class="form-group">
<label for="password">Password</label>
<input id="password" class="form-control" type="password" name="password" placeholder="Password*" required>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-8 col-xs-offset-2">
<div class="form-group">
<label for="bio">Bio</label>
<textarea id="bio" class="form-control" type="bio" name="bio" rows="5" placeholder="Write a short description of yourself and what you enjoy about camping."></textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-4 col-xs-offset-2">
<div class="form-group">
<label for="adminCode">Admin Code</label>
<input id="adminCode" class="form-control" type="text" name="adminCode" placeholder="Admin Code">
</div>
</div>
<div class="col-xs-4 col-xs-offset-0">
<div class="form-group">
<label for="number">Enter: I Love Camping</label>
<input id="number" class="form-control" type="text" name="answer" placeholder="Answer*" required>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-8 col-xs-offset-2">
<div class="form-group">
<button class="btn btn-lg btn-primary btn-block">Sign Up!</button>
</div>
Go Back
</form>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<p class="text-center"><strong>*</strong> indicates a required field.</p>
</div>
</div>
</div>
<% include ./partials/footer %>
If I use enctype="multipart/form-data" I get an error for some reason but if I leave it off it completes but the image still doesnt upload. Just reverts to the no-image.png
If You want to post file You've to use entype="multipart/form-data":
<form action="/register" method="post" enctype="multipart/form-data">
If You've other problems so fix that problem.
from documentation :
.single(fieldname)
Accept a single file with the name fieldname. The single file will be stored in req.file.
So create upload method like this and call it in Your router:
var uploadAvatar = multer({ storage : storage}).single('avatar');
or rename Your input file name="image" :
<input id="avatar" class="form-control" type="file" name="image">
.I know I am not targeting correctly my schema because I get an empty array. I want to push my input values in the staff array. It seems like there's limited handlebars documentation on input fields templating. Thanks!
My Schema:
var docketSchema = new Schema({
companyName: string,
address: string,
staff: [{ manager: String,
receptionist: String
}]
});
My handlebars template:
<div class="form-group">
<input type="text" id="address" name="address" required value="{{input.address}}">
</div>
<div class="form-group">
<input type="text" id="companyName" name="companyName" required value="{{input.companyName}}">
</div>
<div class="form-group">
<input type="text" id="manager" name="manager" required value="{{input.staff.manager}}">
</div>
<div class="form-group">
<input type="text" id="receptionist" name="receptionist" required value="{{input.staff.receptionist}}">
</div>
My route:
router.post('/create', function(req, res, next) {
userService.addDocket(req.body, function(err) {
var vm = {
input: req.body
};
res.redirect('/dockets');
});
});
My console:
[ { companyName: 'Acme',
address: 'New York',
staff: [] } ]
My addDocket method:
exports.addDocket = function(docket, next) {
var newDocket = new Docket({
companyName: docket.companyName,
address: docket.address,
staff: docket.staff
});
newDocket.save(function(err) {
next(null);
});
};
In your handlebars template, try creating the form elements with arrays as follows:
<div class="form-group">
<input type="text" id="address" name="address" required value="{{input.address}}">
</div>
<div class="form-group">
<input type="text" id="companyName" name="companyName" required value="{{input.companyName}}">
</div>
<div class="form-group">
<input type="text" id="manager" name="staff[manager]" required value="{{input.staff.manager}}">
</div>
<div class="form-group">
<input type="text" id="receptionist" name="staff[receptionist]" required value="{{input.staff.receptionist}}">
</div>