0

I have developed a small recipe app. This one uses the edamame api for it. I have implemented a filter function which should filter by criteria like diet and health. In addition, it should also be possible to filter by calories. For this, the user specifies the maximum number of calories, if he wants to filter by calories. However, the filter function does not work. I enter the term "high protein" for the filter "health", and the term "burger" in the searchbar. But the result isn't filtered. The code looks functional to me. I hope you guys can help me out!

This is my HTML-Code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Kitchn - Kochen leicht gemacht! </title>
    <link href="styles.css" rel="stylesheet">
</head>
<body>
    <main class="container">
        <div style="text-align: center;">
            <e style="margin: 0;" data-i18n-key="languages-title">Sprachen</e>
            <div id="languages" class="grid">
                <div id="de"></div>
                <div id="en"></div>
            </div> 
    <div class = "container" >
        <div class = "row" id="photo"><!--collage-->
            <div class="col-12" id="logo">
                
                <img src="assets\image-removebg-preview.png" id= "food" class="row"/>
                
                <div><p data-i18n-key="title" id="title">AppName</p></div>
            </div>  
        </div>
    </div>
        <div class="row" id="search"><!--searchbar-->
            
            <div class="col-8" id="searchbar" >
                <img src = "assets\pawprint.png" id="paw"/>
                <input placeholder="Vegan, etc." id="input"/>
            </div>
            <div class="col-2" id="button" >
                <img src = "assets\paw.png" id="enter"/>
            </div>

        </div>
        <div class ="row"id = "cards"><!--reccommedned-->
            
        </div> <!--Filterfunktion für Kalorien etc. hier -->
        <div class="row" id="filter">
            <div class="col-3">
              <select id="filter-select">
                <option value="diet">Diät</option>
                <option value="health">Gesundheit</option>
              </select>
            </div>
            <div class="col-3">
              <input type="text" id="filter-value" placeholder="Wert eingeben">
            </div>
            <div class="col-3">
                <button id="submit" onclick="filterRecipes()">Filter anwenden</button>
            </div>
            <div class="col-3">
                <div>Maximale Kalorien:</div>
                <input type="text" id="calorie-limit" placeholder="Max. Kalorien">
            </div>
        </div>
    </div>
    <script src ="script.js"></script>
</body>
</html>

This is my JavaScript-Code:

const queryBox = document.getElementById("input");
const submit = document.getElementById("button");

submit.addEventListener("click", async function() {
  const query = queryBox.value;
  const recipes = await getRecipes(query);
  useApiData(recipes);
});

document.addEventListener("keypress", async function(e) {
  if (e.key === "Enter") {
    const query = queryBox.value;
    const recipes = await getRecipes(query);
    useApiData(recipes);
  }
});

const appId = "a59a7160";
const apiKey = "69e96203434da5104b731b2fd8a597b7";



async function getRecipes(query, from = 0, to = 8, diet = [], health = [], maxCalories = Number.POSITIVE_INFINITY) {
  let url =`https://api.edamam.com/search?q=${query}&app_id=${appId}&app_key=${apiKey}&from=${from}&to=${to}`
  if (diet.length) {
    const dietString = diet.join(",");
    url+=`&diet=${encodeURIComponent(dietString)}`
    
  }
  if (health.length) {
    const healthString = health.join(",")
    url+=`&health=${encodeURIComponent(healthString)}`
  }
  if (maxCalories !== Number.POSITIVE_INFINITY) {
    url+= `&calories=${maxCalories.toString()}`
  }
  const response = await fetch(
    url
  );
  const data = await response.json();

  return data.hits;
}

function useApiData(data) {
  const filteredRecipes = filterRecipes(data);
  let toadd = "";
  for (let i = 0; i < filteredRecipes.length; i++) {
    let diet = "No Data Found";
    if (data[i].recipe.dietLabels && Array.isArray(data[i].recipe.dietLabels) && data[i].recipe.dietLabels.length > 0) {
      diet = data[i].recipe.dietLabels;
    }

    let health = "No restrictions";
    if (data[i].recipe.healthLabels && Array.isArray(data[i].recipe.healthLabels) && data[i].recipe.healthLabels.length > 0) {
      health = data[i].recipe.healthLabels;
    }


    toadd += `
    <div class="card">
        <img src="${data[i].recipe.image}" class="card-ing-top" alt="..."/>
        <div class="card-body">
            <h5 class="card-title">${data[i].recipe.label}</h5>
            <a href="${data[i].recipe.url}" class="btn"><b>Click here for full recipe!</a> </b>
            <br>
            <br>
                <r class = "item-data"><b>Calories: </b> <br> ${data[i].recipe.calories.toFixed(2)}</r>
                <br>
                <r class="item-data"><b>Diet label: </b> <br> ${diet}</r>
                <br>
                <r class = "item-data"><b>Health label: </b> <br> ${health}</r> 
                <br>
                <r class = "item-data"><b>Ingredient line:</b> <br> ${data[i].recipe.ingredientLines}</r>
        </div>
    </div>
    `
}
document.getElementById("cards").innerHTML = toadd;
}

function filterRecipes(recipes, diet = [], health = []) {
  if (diet.length === 0 && health.length === 0) {
    return recipes;
  }

  const filteredRecipes = recipes.filter((recipe) => {
    if (diet.length > 0 && recipe.recipe.dietLabels) {
      const dietLabels = recipe.recipe.dietLabels.map((label) => label.toLowerCase());
      if (!diet.some((d) => dietLabels.includes(d))) {
        return false;
      }
    }

    if (health.length > 0 && recipe.recipe.healthLabels) {
      const healthLabels = recipe.recipe.healthLabels.map((label) => label.toLowerCase());
      if (!health.some((h) => healthLabels.includes(h))) {
        return false;
      }
    }

    return true;
  });

  return filteredRecipes;
}


//Language code

let exceeded = "You have exceeded the MONTHLY quota for Characters on your current plan, BASIC. Upgrade your plan at https://rapidapi.com/googlecloud/api/google-translate1"

const defaultLocale = navigator.language.slice(0,2)

let locale

let translations = {}

document.addEventListener("DOMContentLoaded", () => {
    setLocale(defaultLocale)
    bindLocaleSwitcher(defaultLocale)
})

function translateElement(element){
    const key = element.getAttribute("data-i18n-key")
    const translation = translations[locale][key]
    element.innerText = translation
}

async function setLocale(newLocale){
    if(newLocale===locale) return
    const newTranslations = await fetchTranslationsFor(newLocale)
    locale = newLocale
    translations = newTranslations
    translatePage()
}

async function fetchTranslationsFor(newLocale){
    const response = await fetch(`/lang/${newLocale}.json`)
    return await response.json()
}

function translatePage(){
    document.querySelectorAll("[data-i18n-key]").forEach(translateElement)
}

function translateElement(element){
    const key = element.getAttribute("data-i18n-key")
    const translation = translations[key]
    element.innerText = translation
}

function bindLocaleSwitcher(initialValue){
    const switcher = document.getElementById("languages").children
    for(const sw of switcher){
        sw.addEventListener('click', () => {
            setLocale(sw.id)
            input.value = ""
            results.innerHTML = ""
        })
    }
}

This is my CSS-Code:

@import url('https://fonts.googleapis.com/css2?family=Grand+Hotel&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@500;700&display=swap');
.container{
    background-color: #FDEEDC;
    
}
body{
    margin: 0;
    padding: 0;
    background-color: #FDEEDC;
}

#languages{
    display: flex;
    justify-content: center;
    margin-bottom: 15px;
}

#languages > div{
    background-repeat: no-repeat;
    background-position: center;
    background-size: 180%;
    border-radius: 999px;    
    width: 30px;
    height: 30px;
    margin-left: 2vh;
    margin-top: 2vh;
    transition: all .2s;
}
#languages > div:hover{
    filter: brightness(105%);
    background-size: 220%;
    transition: all .2s;
}
#languages > div:active{
    filter: brightness(85%);
}

#de{
    background: url('svg/Flag_of_Germany.svg');    
}

#en{
    background: url('svg/gb.svg');    
}
/* TEST ende */
#photo{
    background-color: #FDEEDC;
    height: 50vh;
}
#search{
    position: absolute;
    z-index: 1;
    width: 100%;
    height: 78px;
    align-items: center;
    justify-content: center;
    display: flex;
    top: 0;
    margin-top: 57vh;
    background-color: #FDEEDC;
    
}
#searchbar{
    background-color: #FFC3A1;
    width: 70%;
    height: 80px;
    align-items: center;
    border-radius: 60px;
    margin-right: 10px;
    justify-content: center;
    display: flex;

}
#button{
    background-color: #FDEEDC;
    
}
#cards{
    background-color: #FDEEDC;
    margin-top: 100px;
    
}
#food{
    height: 22%;
    width:22% ;
    align-items: center;
    justify-content: center;
    display: flex;
    padding-top: 7px;
    
}
#logo{
    align-items: center;
    justify-content: center;
    display: flex;
    margin: auto;
    
}
#input{
    width: 90%;
    font-size: 28px;
    border: 0;
    background-color: #FFC3A100;
    color: #FDEEDC;
    
}
#paw{
    width: 9vh;
    height: 9vh;
    transform: scaleX(-1);
    margin-left: 15px;
    margin-right: 10px;
}
#input:focus{
    outline: none;
}
p{
    font-family: 'Grand Hotel', cursive;
    font-size: 70px;
}

r{
    font-family: 'Times New Roman';
    font-size: 20px;
}

e{
    font-family: 'Grand Hotel', cursive;
    font-size: 30px;
}

#enter{
    height: 8vh;
    width: 8vh;
    margin-left: 1vh;
    background-color: #F0997D;
    border-radius: 70px;
    transition: 300ms;
}
#enter:hover{
    cursor: pointer;
    background-color: #F2B4B4;
    background-size: 220%;
    transition: all .2s;
}
.card{
    border-radius: 10px;
    border:10px solid #F2B4B4;
    width:300px;
    padding: 10px;
    background-color: #FFDECF;
    margin: 20px;
    float: left;
    height: 1000px;
}
.card-ing-top{
    height: 300px;
    width: 300px;
    object-fit: cover;
    border-radius: 10px;
}
.card-title{
    font-family: Montserrat;
    font-size: 20px;
}
.btn{
    text-decoration: none;
    font-family: Montserrat;
    font-weight: 400;
    color: #A75D5D;
}

In my localhost, I checked the functions of my app. I entered a term like "burer" in the search bar and "high-protein" as filter for health and pressed the search button. However, the filterfunction for the search doesn't work, meaning the get doesn't work and I don't get any filtered recipes back based on my search.

1 Answers1

0

It looks like your API query string is incorrect and it returns a 400 error. For example, if you remove the last parameter (e.g. &calories=), then the response is good. Check in the documentation.

Andrew
  • 630
  • 1
  • 5
  • 14
  • can you tell me the line in the code where to change this. – warrior.0779 Feb 13 '23 at 12:07
  • Function 'getter' right after the line "let target= https://api.edamam.com..." you have a block of code to add filterCriteria to the URL. But those filters do not fit the edamam API docs. – Andrew Feb 13 '23 at 12:29