Reputation: 11
For a cookbook, I create a new recipe. Within that recipe, I enter the recipe name, the instructions, and then have an input field for the number of ingredients. When you enter a number, you clicked the Add button. Based upon the input, the equal number of Dropdown menus will appear.
Caveat: The options available in each dropdown list should be the values from a Mongo database. Selecting one value in a markdown should prevent another value from being available. Ex: (Enter 3 into the field. Click add. 3 dropdowns appear. In each dropdown you have: Carrots Onions Potatoes
Selecting Carrots in the first dropdown makes the second dropdown only have onions and potatoes available.
Current Crux: I can pass in the ingredients from my controller and display them in HTML. I cannot within a element.
I have tried JSON.Stringify within the script (within the HTML). I have tried JSON.Stringify within the route of the controller.
I can iterate within the element using EJS output tags. <%= ingredientsData %> or <% ingredientsData.name %>
I cannot do the same with the element.
Model
const mongoose = require("mongoose");
// SCHEMA /
const ingredientSchema = new mongoose.Schema({
name: { type: String, required: true }
});
const Ingredient = mongoose.model('Ingredient', ingredientSchema)
module.exports = Ingredient;
Controller
router.get('/new', async (req, res) => {
console.log("router get recipes/OGnew.ejs");
try {
let allIngredients = await Ingredient.find({});
allIngredients = JSON.stringify(allIngredients);
console.log(allIngredients);
res.render('recipes/OGnew.ejs', {ingredientsData: allIngredients});
}
catch (error) {
console.log("ERROR: get /new", error);
res.redirect('/recipes');
}
});
HTML/EJS
<!-- views/recipes/OGnew.ejs -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Create New Recipe</title>
</head>
<body>
<%- include('../partials/_navbar') %>
<h1>Create Recipe</h1>
<form action="/recipes" method="POST">
<label for="name">Recipe Name:</label>
<input type="text" name="name" id="name" placeholder="Required" required/>
<label for="instructions">Instructions:</label>
<input type="text" name="instructions" id="instructions"/>
<button type="submit">Add Recipe to Database</button>
</form>
<% console.log(ingredientsData); %>
<% console.log(ingredientsData.length); %>
<% if (ingredientsData.length === 0) { %>
<h2>You have not added any ingredients yet.</h2>
<% } %>
<label for="num">Select Ingredients:</label>
<input type="text" name="num" id="num" placeholder="Enter # of ingredients" min="1" required>
<div id="dropdown-div"></div>
<button id="add-dropdown-button">Add</button>
<script type="text/javascript">
console.log("Running script...")
function createDropdowns() {
console.log("Creating Dropdowns...")
const dropdownContainer = document.getElementById('dropdown-div');
dropdownContainer.innerHTML = '';
for (let i = 0; i < numberInput.value; i++) {
const dropdown = document.createElement('select');
dropdown.name = `ingredient${i + 1}`;
// Loop through ingredientsData array and create options
const listIngredients = <%= JSON.stringify(ingredientsData) %>;
//listIngredients = [1,2,3,4]; **IF I REMOVE ANY REFERENCE TO ingredientsDATA and USE this array, the dropdowns will appear but I cannot populate them with the ingredientsData
listIngredients.forEach(ingredient => {
const option = document.createElement('option');
option.value = 1;//Should be ingredient.name (or ingredient._id)
option.text = 1; //Should be ingredient.name
dropdown.appendChild(option);
});
dropdownContainer.appendChild(dropdown);
}
}
const numberInput = document.getElementById('num');
let button = document.getElementById("add-dropdown-button");
button.addEventListener("click", createDropdowns);
</script>
</body>
</html>
My code: https://github.com/padcoding1/men-stack-relating-data-lab-cookbook
Upvotes: 1
Views: 60
Reputation: 8752
On the server try removing JSON.stringify:
const allIngredients = await Ingredient.find({});
console.log(allIngredients);
res.render('recipes/OGnew.ejs', {ingredientsData: allIngredients});
and in the template, use unescaped value tag (<%-
instead of <%=
) when passing variable to JavaScript:
const listIngredients = <%- JSON.stringify(ingredientsData) %>;
console.log('listIngredients', listIngredients);
console.log("Running script...")
function createDropdowns() {
console.log("Creating Dropdowns...")
const dropdownContainer = document.getElementById('dropdown-div');
dropdownContainer.innerHTML = '';
// store dropdowns for later
const dropdowns = [];
// on change, remove selected option from all dropdowns changed one
function updateOpts(e) {
console.log('updating all but: ', e.target.name);
dropdowns.forEach(dropdownEl => {
if (dropdownEl.name != e.target.name) {
[...dropdownEl.options].forEach((opt, i) => {
if (opt.value === e.target.value) {
dropdownEl.options.remove(i);
}
});
}
})
}
for (let i = 0; i < numberInput.value; i++) {
const dropdown = document.createElement('select');
dropdown.name = `ingredient${i + 1}`;
// add select indicator option, default selected
const option = document.createElement('option');
option.text = '--select--';
option.setAttribute('disabled', 'true');
option.setAttribute('selected', 'true');
dropdown.appendChild(option);
// dummy data
const listIngredients = [{
name: 'Carrots',
_id: '111'
}, {
name: 'Onions',
_id: '2222'
}, {
name: 'Potatoes',
_id: '333'
}];
listIngredients.forEach(ingredient => {
const option = document.createElement('option');
option.value = ingredient._id;
option.text = ingredient.name;
dropdown.appendChild(option);
});
// on change, update options
dropdown.onchange = updateOpts;
dropdownContainer.appendChild(dropdown);
dropdowns.push(dropdown);
}
}
const numberInput = document.getElementById('num');
let button = document.getElementById("add-dropdown-button");
button.addEventListener("click", createDropdowns);
<label for="num">Select Ingredients:</label>
<input type="text" name="num" id="num" placeholder="Enter # of ingredients" min="1" required>
<div id="dropdown-div"></div>
<button id="add-dropdown-button">Add</button>
Upvotes: 1