I'm writing an express app that will allow someone to create a recipe (cooking recipe), which is then saved to mongodb. When they open the app they can see the list of recipes, edit them, delete them etc.
The recipes are being stored in mongo like this
> db.recipes.find()
{ "_id" : ObjectId("542fc7d635ebd51b8afd507b"), "name" : "chili",
"ingredients" : [
{ "ingredient" : "mince", "quantity" : "500", "unit" : "g" },
{ "ingredient" : "cumin", "quantity" : "1", "unit" : "tsp" },
{ "ingredient" : "paprika", "quantity" : "2", "unit" : "tsp" } ]
}
{ "_id" : ObjectId("542fccbb6de6181f8da58346"), "name" : "test",
"ingredients" : [
{ "ingredient" : "stuff", "quantity" : "10", "unit" : "g" },
{ "ingredient" : "stuff", "quantity" : "10", "unit" : "g" } ]
}
The db is read from whenever the 'recipes' page is viewed
router.get('/recipes', function(req, res) {
db.collection('recipes').find().toArray(function(err, results) {
if(results.length > 0) {
recipes = results;
for(var i = 0; i < results.length; i++) {
recipeList[i] = results[i].name;
}
res.render('recipes', {recipes: recipes, recipeList: recipeList});
} else {
recipes = [];
res.render('recipes', {recipes: recipes, recipeList: recipeList});
}
});
});
and then in recipes.jade
the recipes are listed as follows
table.table
thead
tr
th Name
tbody
- for(var i = 0; i < recipes.length; i++)
tr
td= recipes[i].name
When one of these recipes is clicked on a modal is launched as follows
script("type=text/javascript").
$('td').on('click', function() {
var recipe = $(this).clone().children().remove().end().text();
$('#myModalLabel.modal-title').html(recipe);
$('#myModal').modal();
});
which should list the ingredients
I'm having difficulty figuring out how to display the ingredients for a recipe when it is clicked on. I tried to figure it out by adding this code in the modal, which I know is wrong but I don't know what to change.
p ingredients
- for(var j = 0; j < recipes.length; j++)
ul
li= recipes[j].ingredients[0].ingredient
Following nanoamps's answer, and modifying slightly, I now have a separate template file singleRecipe.jade
- var theIngredients = recipes[recipeIndex].ingredients;
p ingredients
ul
- for(var j = 0; j < theIngredients.length; j++)
li= theIngredients[j].quantity + theIngredients[j].unit + ' ' + theIngredients[j].ingredient
so when a recipe is clicked, this handler gets the name and index
$('td').on('click', function() {
var recipeIndex = $(this).parent().index();
var recipeName = $(this).text();
$('#myModal .modal-body').load('/singleRecipe/' + recipeIndex, function() {
$('#myModal').modal();
});
});
and the route handler in app.js
is now
router.get('/singleRecipe/:d', function(req, res) {
db.collection('recipes').find().toArray(function(err, results) {
if(results.length > 0) {
recipes = results;
for(var i = 0; i < results.length; i++) {
recipeList[i] = results[i].name;
}
res.render('singleRecipe', {recipes: recipes, recipeIndex: req.params.d});
} else {
recipes = [];
res.render('recipes', {recipes: recipes, recipeList: recipeList});
}
});
});
I know this is still quite messy, but it's working as shown below, so now I can just start refactoring.
Your current display code is looping through all the recipes (with the for j
loop) and then printing the first ingredient of each (with the .ingredient[0]
reference).
Start by finding out which recipe you've chosen in your click handler with something like:
var recipeIndex = $(this).index();
var recipeName = $(this).text();
Then you can use the recipe index to pick the right one from your recipes
array. You could build a massive list somewhere in your initial recipes.jade
page, hide it with CSS and then clone into the modal when required, but that's not very scalable. You'd be better off putting the rendering of the individual recipe details into a separate template with its own route:
- var theIngredients = recipes[recipeIndex].ingredients;
p ingredients
ul
- for(var j = 0; j < theIngredients.length; j++)
li= theIngredients[j].quantity + theIngredients[j].unit + ' ' + theIngredients[j].ingredient
This will build a list of ingredients for the given index, looping to create each list item with the concatenated ingredient details.
Then pull that into your modal using a separate GET request in the click handler with something like:
$('#myModal .modal-body').load('/singleRecipe?index=' + recipeIndex, function() {
$('#myModal').modal();
});
which requests your new template, passing the index in the URL.
Although that should work, any changes to the collection will bring up the wrong recipe. Build on that by instead rendering MongoDB's unique _id
identifier somewhere in the initial table rows (eg. as a data-id
attribute) and using that as the thing you lookup and pass through your click handler and request. Then the single recipe route can lookup the single record in the database instead of loading the whole collection. It'll be faster, less data down the wire, and less brittle.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.