Skip to content
Advertisement

How do I filter Fetch API results with a search bar in vanilla JS?

I’m really new to javascript. I’m trying to setup a page that fetches from a placeholder API(https://jsonplaceholder.typicode.com/posts) and displays the Title, Author(which I had to add) as well as the Body. I’m also trying to give the ability to use the search bar to filter the results by userId. I got some help a few days ago getting the data from the API to show on my page but I’ve been struggling to get the search bar to work. Doe’s anyone have any ideas on how to get it to work? Any help is greatly appreciated.

Edit: I thought I found a solution but I can’t seem to get it to work. I updated the code to reflect what I’m trying. Sorry if I’m missing simple stuff.

const searchInput = document.querySelector("[data-search]")

let html = []

searchInput.addEventListener("input", e => {
    const value = e.target.value.toLowerCase()
    html.forEach(post => {
      const isVisible =
        posts.userId.toLowerCase().includes(value)
      posts.element.classList.toggle("hide", !isVisible)
    })
  })

Promise.all([1, 22, 33, 44, 55, 66, 77, 88, 95, 99].map(id =>
    fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
        method:'PATCH',
        body: JSON.stringify({
            name: "John Doe",
    }),
    headers: {
        'Content-Type': 'application/json; charset=UTF-8'
    },
})
        .then((response) => response.json())))
            .then(data => {
        console.log(data);
        const html = data.map(posts => {
            return `
            <div class="post">    
                <p id="title">${posts.title}</p>
                <p id="author">${posts.name}</p>
                <p id="body">${posts.body}</p>
            </div>`
        }).join(' ');
        console.log(html);
        document.querySelector('#content').insertAdjacentHTML('beforeend', html);
    })
;
<!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">
    <link rel="stylesheet" href="resources/css/style.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">

   
    <title>Code Problem 4</title>
</head>
<body>
    <header><h1>Blog Post</h1></header>
    <div id="content">
        <h2>Posts</h2>
        <form id="search" action="action_page.php">
            <input type="text" placeholder="Filter by User ID" name="search">
            <button type="submit" onclick="searchWithField()"><i class="fa fa-search"></i></button>
          </form>
    </div>
    <script src="resources/javascript/script.js"></script>
</body>
<footer>
    <span class="shapes">● ▴ ■</span>
 </footer>
</html>

Advertisement

Answer

Let’s take a look at your code: The below code performs a PATCH request, meaning it updates the data of a post, in your case by adding a name. Afterwards, it returns the post its updated data.

    fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
        method:'PATCH',
        body: JSON.stringify({
            name: "John Doe",
    }),
    headers: {
        'Content-Type': 'application/json; charset=UTF-8'
    },
})

If we take a closer look at the api it’s returning data, we see that a userId is already defined. This means the user (which, in your case, is the author) can be retrieved from another api endpoint (https://jsonplaceholder.typicode.com/users/${post.userId}), so no need to use the FETCH request. Instead, we’ll first GET the posts, then GET the users, and combine them in a nice list post_data for reference:

var post_data = [];

//Step one: get all the posts
 fetch(`https://jsonplaceholder.typicode.com/posts`, {
        method:'GET',
    headers: {
        'Content-Type': 'application/json; charset=UTF-8'
    },
})
   //Transform our data to correct format
   .then((response) => response.json())
   //Do something with the posts
   .then(data => { 
        //We use the fetches array to add all our calls to get the user (and its name), and make it possible to wait for all of them to complete before we update our html.
        let fetches = []
        data.forEach(function(post) {
          //Lets get the user coupled to each post
          fetches.push(
            fetch(`https://jsonplaceholder.typicode.com/users/${post.userId}`, {
            method:'GET',
            headers: {
                'Content-Type': 'application/json; charset=UTF-8'
              },
            })           
            //We want the user name to be part of the post data, so we add it and save it to locally held data
            .then((response) => response.json())
            .then(user => {
              //We add the user name directly as a property 'userName' to our posts.
              post["userName"] = user.name;
              //Add this post to our locally stored post_data
              post_data.push(post);  
            })
          );

        });
        //This promise will wait on all user names to be added to our posts
        Promise.all(fetches).then(function() {
          //At this moment, all our posts have been loaded. Now it's time to display them!
          //I've added the post id, which will be a handy way to hide the irrelevant data.
          const html = data.map(post => {
            return `
            <div class="post" id="post_${post.id}">    
                <p id="title">${post.title}</p>
                <p id="author">${post.userName}</p>
                <p id="body">${post.body}</p>
            </div>`
        }).join(' ');
        document.querySelector('#content').insertAdjacentHTML('beforeend', html);
        });
    });
;

Aaah, that’s better! Now we want to be able to filter these results based on author. Since you ask for this to be done in javascript, I’ve adjusted your html from:

        <form id="search" action="action_page.php">
            <input type="text" placeholder="Filter by User ID" name="search">
            <button type="submit" onclick="searchWithField()"><i class="fa fa-search"></i></button>
          </form>

to

            <input type="text" id="search-input" placeholder="Filter by Author" name="search">
            <button type="submit" id="search-button"><i class="fa fa-search"></i></button>

The php link is removed (since i don’t think you’re using php). Note that I’ve added an id to both the <input> and <button> tag. We’ll use those to identify our button.

We can finally try to filter our results:

//Now, we want to filter our blog posts by author.Since you have provided a button, i'll assume we only want to filter results as soon as the button is pressed:
document.getElementById("search-button").addEventListener("click",function(){
  //First, we want to get the text in the input field:
  const filterText = document.getElementById("search-input").value;
  //Secondly, we want to find the posts that have a user matching that name, or that id.
  //We convert all to lower case to ease searching.
  let filtered_post_data = post_data.filter(post => {
    username = post.userName.toLowerCase();
    return (username.includes(filterText.toLowerCase()) || post.id == filterText);
  });
  //now we have the filtered data, it is time to hide all data:
  post_data.forEach(function(post) {
    let post_html = document.getElementById(`post_${post.id}`);
    post_html.style.display = 'none';
  });
  //And afterwards, show all filtered data:
  filtered_post_data.forEach(function(post) {
    let post_html = document.getElementById(`post_${post.id}`);
    post_html.style.display = 'inline';
  });
  
});

working example on codepen

Advertisement