Issue
Currently I can filter by 1 ingredient, but when i try to select multiple ingredients (checkboxes), My recipes disappear as "ingredient1, ingredient2" is being passed to my searchfield rather than ingredient 1 & ingredient2.
My code for my search/filter is
import React, { useState } from "react";
import DisplayFoodItems from "./DisplayFoodItems";
import { ingredients } from "../data/Ingredients";
function Search({ details }) {
const [searchField, setSearchField] = useState("");
const [checked, setChecked] = useState([]);
const all = [...checked];
const filtered = details.filter((entry) => {
//Search box
if (entry.name.toLowerCase().includes(searchField.toLowerCase()) ||
entry.catagory.toLowerCase().includes(searchField.toLocaleLowerCase())) {
return (
entry.name.toLowerCase().includes(searchField.toLowerCase()) ||
entry.catagory.toLowerCase().includes(searchField.toLocaleLowerCase())
)
}
//Filter checkboxes
for (var i = 0; i < entry.ingredients.length; i++) {
console.log(searchField)
if (entry.ingredients[i].toLowerCase().includes(searchField.toLocaleLowerCase())) {
return (
entry.ingredients[i].toLowerCase().includes(searchField.toLocaleLowerCase())
);
}
}
}
);
const handleToggle = c => () => {
// return the first index or -1
const clickedBox = checked.indexOf(c);
if (clickedBox === -1) {
all.push(c);
console.log(all)
setSearchField(all.toString())
} else {
all.splice(clickedBox, 1);
setSearchField("")
}
console.log(all);
setChecked(all);
};
return (
<>
<div>
<input id="search"
className="form-control"
type="text"
placeholder="Search by recipe name or catagory"
onChange={(e) => setSearchField(e.target.value)}
/>
</div>
<div>
{ingredients.map((ingredient, index) => (
<label key={index} className="form-check-label">
<input
onChange={handleToggle(ingredient.name)}
type="checkbox"
className="mr-2"
/>
{ingredient.name}</label>
))}
<hr></hr>
</div>
<div class="flex">
<DisplayFoodItems foodList={filtered} />
</div>
</>
);
}
export default Search;
Here is a picture of my screen if it helps at all
All checkboxes work individually but for example, if salt and oil when checked together it should return Bacon wrapped chicken and Smashed potatoes, however it returns blank at present.
I have tried looping the all array and sending that to setSearchField, but again, I cannot get it to work.
I have tried looping through the array of checked ingredients and sending that to the setSearchField. Im expecting recipes to appear if they contain an ingredient that has been checked.
Solution
If I understand correctly you want something like this?
const foodList = [
{ name: "Food 1", ingredients: ["Salt"] },
{ name: "Food 2", ingredients: ["Oil"] },
{ name: "Food 3", ingredients: ["Milk"] },
{ name: "Food 4", ingredients: ["Salt", "Oil"] },
{ name: "Food 5", ingredients: ["Oil", "Milk"] },
{ name: "Food 6", ingredients: ["Oil", "Salt", "Milk"] },
];
const ingredientList = ["Salt", "Oil", "Milk"]
const Example = () => {
const [ingredients, setIngredients] = useState([]);
const filtered = foodList.filter(food => {
return ingredients.length === 0
|| food.ingredients.length > 0 && ingredients.every(selectedIngredient =>
food.ingredients.some(foodIngredient => foodIngredient.toLowerCase() === selectedIngredient.toLowerCase()));
});
return (
<div>
<h2>Filter</h2>
{
ingredientList.map((ingredient, i) => {
const checked = ingredients.some(selectedIngredient => ingredient.toLowerCase() === selectedIngredient.toLowerCase());
return (
<label key={`ingredient-${i}`}>
<input
type="checkbox"
checked={checked}
onChange={() => {
if (checked) {
setIngredients(ingredients.filter(selectedIngredient => ingredient.toLowerCase()!== selectedIngredient.toLowerCase()))
}
else {
setIngredients([...ingredients, ingredient]);
}
}} />
{ingredient}
</label>
);
})
}
<br />
<br />
<h2>Food list</h2>
<pre>{JSON.stringify(filtered, null, 4)}</pre>
</div>
);
}
Answered By - Armands Uiska
Answer Checked By - - Marilyn (ReactFix Volunteer)