【问题标题】:Keeping Modal Form Open After Failed Submission Attempt提交尝试失败后保持模式表单打开
【发布时间】:2020-11-12 10:07:09
【问题描述】:

我正在开发一个 Nodejs 项目,目前正致力于在用户未能满足表单要求(例如填写所有表单字段)后使模态表单保持打开状态。我在网上做了一些阅读,但无法理解我将如何做到这一点。目前,当填写表单出错时,我只是让表单呈现一个新页面。这个项目的回购是:https://github.com/halsheik/RecipeWarehouse.git。下面,我还为这个问题粘贴了我的代码的相关块。我将不胜感激。

// 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();

【问题讨论】:

    标签: javascript html css node.js forms


    【解决方案1】:

    为了防止空洞页面刷新,您必须研究客户端框架,我建议您学习 AngularJS,它可以让您在不刷新页面的情况下刷新表单。

    【讨论】:

    • Ciao,这是一个建议,而不是一个答案。尝试提供至少一个链接从哪里开始,或一些示例代码来帮助 OP。谢谢
    • 看看这个他解释得很漂亮,并举例stackoverflow.com/questions/30646997/…
    【解决方案2】:

    我还会推荐一些客户端框架,在我的情况下,我以这种方式使用 bootstrap 和 jquery:

    <!-- Button trigger modal -->
    <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
      Launch demo modal
    </button>
    
    <!-- Modal -->
    <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div class="modal-body">
            ...
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
            <button type="button" class="btn btn-primary">Save changes</button>
          </div>
        </div>
      </div>
    </div>
    <script>
        $(document).ready(function() {
            $('button[value=save]').click(function(e) {
                e.preventDefault();
    
                // ....
                // Code to check validity
                // ....
    
                // Hides modal
                $('#exampleModal').modal('hide');
                });
            });
    </script>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-05
      • 2020-10-06
      • 2014-08-29
      • 1970-01-01
      • 1970-01-01
      • 2016-01-07
      • 2015-09-30
      • 2016-07-03
      相关资源
      最近更新 更多