I am creating a web app that returns recipes when a user searches for a specific ingredient(s). I am using the edamam api and decided to use pagination so that I could load more recipes relative to the users search when the user clicks on the "Load more" button. Note that edamam will return 20 recipes at a time. This is from edamam's documentation on how their pagination works:
The way pagination works in recipe search API has changed significantly from version 1. The parameters from and to are no longer supported. Instead, responses will contain a pre-constructed URL for the next request in _links.next.href. If this path is not present, then this was the last page and there are no more results.
I largely got this functionality working however, there is a part of my html that utilizes an ejs if statement that is causing some problems (issue in "loadrecipes.js").
My question is 2 fold:
The code. Git hub repo is here for easier viewing
I have 3 files:
index.js
import express from "express";
import axios from "axios";
import bodyParser from "body-parser";
import 'dotenv/config';
const app = express();
const port = 3000;
const API_URL = "https://api.edamam.com/api/recipes/v2?type=public";
const myId = process.env.ID;
const myAPIKey = process.env.API_KEY;
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static("public"));
let nextPageUrl = null;
app.get("/", (req, res) => {
res.render("index.ejs");
})
app.post("/search", async (req, res) => {
const inputText = req.body.ingrediants;
try{
const requestUrl = nextPageUrl || API_URL;
const response = await axios.get(requestUrl, {
params: {
q: inputText,
app_id: myId,
app_key: myAPIKey
}
});
const result = response.data;
//store next page url for pagination
if(result._links.next.href){
nextPageUrl = result._links.next.href;
} else{
nextPageUrl = null; //no more pages/recipes given the user search
}
res.render("index.ejs", {
recipes: result,
userInput: inputText,
nextPageUrl: nextPageUrl
});
}catch(error){
res.render("index.ejs", {
content: "Looks like there's an issue: " + error.message
});
}
})
app.listen(port, () => {
console.log(`Server listening on port ${port}.`);
})
index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cook Your Kitchen</title>
<link rel="stylesheet" type="text/css" href="styles/main.css">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous">
</head>
<body>
<div class="container">
<div class="px-4 py-5 my-4 text-center">
<h1 class="display-5 fw-bold text-body-emphasis">Cook Your Kitchen</h1>
<div class="col-lg-6 mx-auto">
<p class="lead mb-4">Have some random ingrediants in your kitchen? <br>
Those veggies about to go bad?? <br>
Enter the ingrediants you want to use and get recipe suggestions!
</p>
<form action="/search" method="post">
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center">
<input type="text" id="search" name="ingrediants" class="form-control"
placeholder="Chicken, beans, tomato,..." aria-label="Recipient's username"
aria-describedby="button-addon2">
<button class="btn btn-outline-secondary" type="submit" value="Submit" id="button-addon2">Get a recipe</button>
</div>
</form>
</div>
</div>
</div>
<% if(locals.recipes){ %>
<div class="container px-4 py-5" id="custom-cards">
<h2>You searched for: <%= userInput %>
</h2>
<div id="recipe-list" class="row row-cols-1 row-cols-lg-3 align-items-stretch g-4 py-5">
<% recipes.hits.forEach(recipe=> { %>
<div class="col" onclick="location.href='<%= recipe.recipe.url %>';">
<div class="card card-cover h-100 overflow-hidden text-bg-dark rounded-4"
style="background-image: url('<%= recipe.recipe.image %>');">
<div class="d-flex flex-column h-100 p-5 pb-3 text-white text-shadow-1 text-bg">
<h3 class="mt-5 mb-4 display-6 lh-1 fw-bold">
<%= recipe.recipe.label %>
</h3>
<ul class="d-flex list-unstyled mt-auto">
<% if (recipe.recipe.dietLabels.length > 0) { %>
<li class="badge text-bg-dark rounded-pill px-2">
<small>
<%= recipe.recipe.dietLabels[0]%>
</small>
</li>
<%} %>
<li class="badge text-bg-dark rounded-pill mx-2 px-2">
<small>
<%= recipe.recipe.healthLabels[0] %>
</small>
</li>
<li class="badge text-bg-dark rounded-pill px-2">
<small>
<%= recipe.recipe.healthLabels[1] %>
</small>
</li>
</ul>
</div>
</div>
</div>
<% })} %>
</div>
</div>
<% if (locals.nextPageUrl){ %>
<div class="text-center mb-4">
<button type="button" class="btn btn-light" id="loadMoreButton" data-next-page="<%= nextPageUrl %>">Load More</button>
</div>
<% } %>
<script src="https://code.jquery.com/jquery-3.7.1.js" integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4="
crossorigin="anonymous"></script>
<script src="loadrecipes.js"></script>
</body>
</html>
This is the part in index.ejs I am trying to update (red box) enter image description here
loadrecipes.js
const nextPageUrl = $("#loadMoreButton").data("next-page");
$("#loadMoreButton").on("click", (event) => {
event.preventDefault();
event.stopPropagation();
//using ajax to load more recipes
if (nextPageUrl) {
$.ajax({
method: "GET",
url: nextPageUrl,
success: (result) => {
// Append result to the existing recipes
// and update the nextPageUrl data attribute if there's a next page.
const moreRecipes = result.hits;
if(moreRecipes.length > 0){
moreRecipes.forEach((recipe) => {
//copying card html/ejs from index.ejs and replacing ejs tags for each additionally retrieved recipe
const recipeCard = `
<div class="col" onclick="location.href='${recipe.recipe.url}';">
<div class="card card-cover h-100 overflow-hidden text-bg-dark rounded-4"
style="background-image: url('${recipe.recipe.image}');">
<div class="d-flex flex-column h-100 p-5 pb-3 text-white text-shadow-1 text-bg">
<h3 class="mt-5 mb-4 display-6 lh-1 fw-bold">
${recipe.recipe.label}
</h3>
<ul class="d-flex list-unstyled mt-auto">
<% if (${recipe.recipe.dietLabels.length} > 0) { %>
<li class="badge text-bg-dark rounded-pill px-2">
<small>
${recipe.recipe.dietLabels[0]}
</small>
</li>
<%} %>
<li class="badge text-bg-dark rounded-pill mx-2 px-2">
<small>
${recipe.recipe.healthLabels[0]}
</small>
</li>
<li class="badge text-bg-dark rounded-pill px-2">
<small>
${recipe.recipe.healthLabels[0]}
</small>
</li>
</ul>
</div>
</div>
</div>
`;
// Append the new recipe card to the recipe list div
$("#recipe-list").append(recipeCard);
});
// Update the nextPageUrl data attribute
$("#loadMoreButton").data("next-page", result._links.next.href);
} else {
// If there are no more recipes, hide the "Load More" button
$("#loadMoreButton").hide();
}
}
});
}
})
I've been looking through documentation and other similar questions posted on stack overflow but haven't found much that is helpful to this exact issue. Any help will be appreciated!
After a lot trial and error and reading various pieces of documentation I solved my problem by creating a global variable to hold the diet label html/ejs that needed to be updated + appended. I pulling out the if statement in recipeCard variable in loadrecipes.js. and assigned the argument of the if statement to diet label variable. I then referenced the diet label variable in the recipe card update. I am convinced there is likley a better solution but this what I've come up that works.
const nextPageUrl = $("#loadMoreButton").data("next-page");
$("#loadMoreButton").on("click", (event) => {
event.preventDefault();
event.stopPropagation();
let dietLabel = "";
//using ajax to load more recipes
if (nextPageUrl) {
$.ajax({
method: "GET",
url: nextPageUrl,
success: (result) => {
// Append result to the existing recipes
// and update the nextPageUrl data attribute if there's a next page.
const moreRecipes = result.hits;
if(moreRecipes.length > 0){
moreRecipes.forEach((recipe) => {
if(recipe.recipe.dietLabels.length > 0){
dietLabel = `
<li class="badge text-bg-dark rounded-pill px-2">
<small>
${recipe.recipe.dietLabels[0]}
</small>
</li>
`;
}
//copying card html/ejs from index.ejs and replacing ejs tags for each additionally retrieved recipe
const recipeCard = `
<div class="col" onclick="location.href='${recipe.recipe.url}';">
<div class="card card-cover h-100 overflow-hidden text-bg-dark rounded-4"
style="background-image: url('${recipe.recipe.image}');">
<div class="d-flex flex-column h-100 p-5 pb-3 text-white text-shadow-1 text-bg">
<h3 class="mt-5 mb-4 display-6 lh-1 fw-bold">
${recipe.recipe.label}
</h3>
<ul class="d-flex list-unstyled mt-auto">
${dietLabel}
<li class="badge text-bg-dark rounded-pill mx-2 px-2">
<small>
${recipe.recipe.healthLabels[0]}
</small>
</li>
<li class="badge text-bg-dark rounded-pill px-2">
<small>
${recipe.recipe.healthLabels[1]}
</small>
</li>
</ul>
</div>
</div>
</div>
`;
// Append the new recipe card to the recipe list div
$("#recipe-list").append(recipeCard);
});
// Update the nextPageUrl data attribute
$("#loadMoreButton").data("next-page", result._links.next.href);
} else {
// If there are no more recipes, hide the "Load More" button
$("#loadMoreButton").hide();
}
}
});
}
})