I am working on a Nodejs project and currently working on making a modal form stay open after a user fails to meet form requirements, such as filling out all the form fields. I've done some reading online and am having trouble understanding how I would do this. Currently, I just have the form render a new page when there is a mistake completing the form. The repo to this project is: https://github.com/halsheik/RecipeWarehouse.git. Below, I have also pasted the relevant chunks of my code for this problem. I would appreciate any assistance.
// Modules required to run the application
const express = require('express');
const multer = require('multer');
const crypto = require('crypto');
const path = require('path');
const { ensureAuthenticated } = require('../config/auth');
// Creates 'mini app'
const router = express.Router();
// Models
const Recipe = require('../models/Recipe'); // Recipe Model
// Set up storage engine
const storage = multer.diskStorage({
destination: function(req, file, callback){
callback(null, 'public/uploads');
},
filename: function(req, file, callback){
crypto.pseudoRandomBytes(16, function(err, raw) {
if (err) return callback(err);
callback(null, raw.toString('hex') + path.extname(file.originalname));
});
}
});
const upload = multer({
storage: storage
});
// My Recipes
router.get('/myRecipes', ensureAuthenticated, function(req, res){
Recipe.find({}, function(err, recipes){
if(err){
console.log(err);
} else {
res.render('./home/myRecipes', {
recipes: recipes,
recipeImageFileName: recipes.recipeImageFileName,
recipeDescription: recipes.recipeDescription,
ingredients: recipes.ingredients,
directions: recipes.directions
});
}
});
});
// Create Recipe
router.post('/createRecipe', upload.single('recipeImage'), ensureAuthenticated, function(req, res){
const { recipeName, recipeDescription, ingredients, directions } = req.body;
let errors = [];
// Checks that all fields are not empty
if(!recipeName || !recipeDescription || !ingredients || !directions){
errors.push({ msg: 'Please fill in all fields.' });
}
// Checks that an image is uploaded
if(!req.file){
errors.push({ msg: 'Please add an image of your recipe' });
}
// Checks for any errors and prevents recipe creation if any
if(errors.length > 0){
console.log(errors);
Recipe.find({}, function(err, recipes){
if(err){
console.log(err);
} else {
res.render('./home/myRecipes', {
errors: errors,
recipes: recipes,
recipeImageFileName: recipes.recipeImageFileName,
recipeDescription: recipes.recipeDescription,
ingredients: recipes.ingredients,
directions: recipes.directions
});
}
});
} else {
// Create a new 'Recipe' using our model
const newRecipe = new Recipe({
recipeName: recipeName,
author: req.user._id,
recipeImageFileName: req.file.filename,
recipeDescription: recipeDescription,
ingredients: ingredients,
directions: directions,
});
console.log(newRecipe);
// Saves recipe to mongoDB database
newRecipe.save().then(function(){
res.redirect('/recipes/myRecipes');
}).catch(function(err){
console.log(err);
});
}
});
module.exports = router;
<%- include('../_partial/_header'); -%>
<div id="newRecipeContainer">
<div class="overlay"></div>
<div id="closeButtonContainer">
<div id="closeButton">+</div>
</div>
<form action="/recipes/createRecipe" method="POST" enctype="multipart/form-data">
<label id="formSubHeading">Create Your Homemade Recipe</label>
<div id="recipeNameContainer">
<label id="recipeNameLabel">Title</label>
<input id="recipeNameInput" type="text" name="recipeName">
</div>
<div id="recipeImage">
<label id="recipeImageLabel">Add An Image of Your Meal</label>
<input id="recipeImageInput" type="file" accept="image/*" name="recipeImage" onchange="validateImageFile(this);"/>
<label id="recipeImageInputLabel" for="recipeImageInput">Choose A File</label>
</div>
<div id="recipeDescription">
<label id="recipeDescriptionLabel">Description</label>
<textarea id="recipeDescriptionInput" name="recipeDescription" cols="30" rows="10" maxlength="2000"></textarea>
</div>
<div class="ingredientsContainer">
<label id="ingredientsLabel">Ingredients</label>
<button id="addIngredientButton" type="button" @click="addIngredientForm">Add Another Ingredient</button>
<div class="allIngredients" v-for="(ingredient, ingredientIndex) in ingredients">
<label class="ingredientLabel">{{ ingredientIndex + 1 }}.)</label>
<input class="ingredientInput" type="text" name="ingredients" v-model="ingredient.ingredient">
<button class="deleteIngredientButton" type="button" v-if="ingredientIndex > 0" @click="deleteIngredientForm(ingredientIndex)">X</button>
</div>
</div>
<div class="directionsContainer">
<label id="directionsLabel">Directions</label>
<button id="addDirectionButton" type="button" @click="addDirectionForm">Add Another Direction</button>
<div class="allDirections" v-for="(direction, directionIndex) in directions">
<label class="directionLabel">{{ directionIndex + 1 }}.)</label>
<input class="directionInput"type="text" name="directions" v-model="direction.direction">
<button class="deleteDirectionButton" type="button" v-if="directionIndex > 0" @click="deleteDirectionForm(directionIndex)">X</button>
</div>
</div>
<div id="createRecipeButtonContainer">
<button id="createRecipeButton" type="submit">Create Recipe</button>
</div>
</form>
</div>
<div id="recipesContainer">
<div id="myRecipesContainer">
<label id="myRecipesLabel">My Recipes</label>
<a id="newRecipeButton">+ Create New Recipe</a>
</div>
<div id="allRecipes">
<% recipes.forEach(function(recipe){ %>
<% if(recipe.author == user._id){ %>
<div class="secondaryContainer">
<div class="recipeContainerIndv">
<img src="/uploads/<%= recipe.recipeImageFileName %>"/>
<a class="recipeTitle"> <%= recipe.recipeName %> </a>
<!-- <% recipe.directions.forEach(function(direction){ %>
<a><%= direction %></a>
<% }); %> -->
</div>
</div>
<% } %>
<% }); %>
</div>
</div>
<script src="/controls/newRecipeControl.js"></script>
<script src="/controls/imageValidator.js"></script>
<%- include('../_partial/_footer'); -%>
const directionsControl = new Vue({
el: '.directionsContainer',
data: {
directions: [
{
direction: ''
}
]
},
methods: {
addDirectionForm: function(){
this.directions.push({
direction: ''
})
},
deleteDirectionForm: function(directionIndex){
if(directionIndex)
this.directions.splice(directionIndex, 1)
}
}
})
const ingredientsControl = new Vue({
el: '.ingredientsContainer',
data: {
ingredients: [
{
ingredient: ''
}
]
},
methods: {
addIngredientForm: function(){
this.ingredients.push({
ingredient: ''
})
},
deleteIngredientForm: function(ingredientIndex){
if(ingredientIndex)
this.ingredients.splice(ingredientIndex, 1)
}
}
})
const toggleNewRecipeForm = function(){
const newRecipeButton = document.querySelector('#newRecipeButton');
const newRecipeContainer = document.querySelector('#newRecipeContainer');
const closeButton = document.querySelector('#closeButton');
const overlay = document.querySelector('.overlay');
// Open dropDownMenu
newRecipeButton.addEventListener('click', function(){
newRecipeContainer.style.display = 'block';
overlay.classList.toggle('addOverlay');
});
// Close dropDownMenu
closeButton.addEventListener('click', function(){
newRecipeContainer.style.display = 'none';
overlay.classList.toggle('addOverlay');
});
}
toggleNewRecipeForm();