Home > Enterprise >  Update news feed in real time based on search in JS
Update news feed in real time based on search in JS

Time:10-31

I have been learning JS over the last 2 weeks and am trying to build a news app using the Guardian's Api. I have managed to get the images, links and headlines pulling in, but I am struggling with getting the search functionality working. I want it to work so the page instantly updates as soon as you start typing. I can see the below is 'working' in the console but I can't figure out how to display it effectively on the page.

I have tried creating a new array for 'searched for articles' but this was too long winded and did not return a real time updated feed whilst typing.

I have tried 'toggling' the h1 (headline) elements of the articles, but kept getting 'not a function' errors. Any insights would be greatly appreciated as I am not sure of the best approach here.

console log of news feed

VIEW CLASS

class ArticlesView {
  constructor(model, api) {
    this.model = model;
    this.api = api;
    this.articlesFromModel = this.model.getArticles();
    this.newsFeed = document.querySelector("#news-feed");
    this.clearFeedBtn = document.querySelector("#clear-feed-button");
    this.refreshBtn = document.querySelector("#refresh-button");
    this.searchInput = document.querySelector("#search-input");
    this.allHeadlines = [document.querySelectorAll("h1")];
    this.clearFeedBtn.addEventListener("click", () => {
      this.clearFeed();
    });
    this.searchInput.addEventListener("input", (e) => {
      this.articlesFromModel.forEach((article) => {
        const searchInput = e.target.value.toLowerCase();
        const isVisible = article.webTitle.toLowerCase().includes(searchInput);
        if (!isVisible) { 
**        // not sure what to do here
**        }
        console.log(searchInput);
        console.log(article.webTitle);
        console.log(isVisible);
      });
    });
  }

  displayArticlesFromApi() {
    this.api.loadArticles(
      (repoData) => {
        this.model.addArticle(repoData.response.results);
        this.displayArticles();
      },
      () => {
        this.displayError();
      }
    );
  }

  displayArticles() {
    this.articlesFromModel.forEach((article) => {
      this.addImage(article);
      this.addHeadline(article);
    });
  }

  addHeadline(article) {
    const h1 = document.createElement("h1");
    h1.className = "news-title";
    h1.innerText = article.webTitle;
    h1.onclick = () => {
      window.location.href = article.webUrl;
    };
    this.newsFeed.append(h1);
  }

  addImage(article) {
    const img = document.createElement("img");
    img.className = "news-image";
    img.setAttribute("id", article.id);
    img.src = article.fields.thumbnail;
    img.onclick = () => {
      window.location.href = article.webUrl;
    };
    this.newsFeed.append(img);
  }

  displayError() {
    let errorMessage = document.createElement("div");
    errorMessage.className = "error";
    errorMessage.textContent = "Oops, something went wrong!";
    this.newFeed.append(errorMessage);
  }

  clearFeed() {
    const images = document.querySelectorAll("img.news-image");
    images.forEach((element) => {
      element.remove();
    });
    const headlines = document.querySelectorAll("h1.news-title");
    headlines.forEach((element) => {
      element.remove();
    });
  }
}

module.exports = ArticlesView;

MODEL CLASS

class ArticlesModel {
  constructor() {
    this.articles = [];
  }

  getArticles() {
    return this.articles;
  }

  addArticle(article) {
    article.forEach((a) => {
      this.articles.push(a);
    });
  }

  reset() {
    this.articles = [];
  }
}

module.exports = ArticlesModel;

API CLASS

class GuardianApi {
  constructor() {
    this.apiURL = `https://content.guardianapis.com/search?api-key=${apikey}&show-fields=thumbnail`;
  }
  loadArticles(callback) {
    fetch(this.apiURL)
      .then((response) => response.json())
      .then((data) => {
        callback(data);
      });
  }
}

module.exports = GuardianApi;

INDEX.js

console.log("the app is running");
const ArticlesModel = require("./src/ArticlesModel.js");
const ArticlesView = require("./src/ArticlesView.js");
const GuardianApi = require("./src/GuardianApi.js");

const client = new GuardianApi();
const model = new ArticlesModel();
const view = new ArticlesView(model, client);

view.displayArticlesFromApi();

HTML FILE

<!doctype html>
<html>
   <head>
    <link rel="stylesheet" href="style.css">
      <title> Your Daily News Feed</title>
   </head>
   <body>
      <h1>News Feed</h1>

        <input type="search" id="search-input" placeholder="e.g. Football...">
      
      <br><br>
      <button onClick="window.location.reload();">Refresh Page</button> <button id="clear-feed-button">Clear Feed</button>
      
      <br> <br>

      <div id="news-feed">

      </div>
   </body>
   <script src="bundle.js"></script>

</html>

CodePudding user response:

I've created a code snippet for your case. You'll need to adjust the line for the api key. For now I've used a key generated for a dummy email. I've also highlighted where the code was changed

The new search function is:

this.searchInput.addEventListener("input", (e) => {
    const searchInput = e.target.value.toLowerCase();
    // Create a filtered copy of articlesFromModel
    const filteredArticles = this.articlesFromModel.filter((article) => {
        return article.webTitle.toLowerCase().includes(searchInput);
    });
    this.clearFeed()
    // Display filtered list.
    this.displayArticles(filteredArticles);
});

displayArticles() has been modified to accept a parameter

displayArticles(articles) {
    articles.forEach((article) => {
        this.addImage(article);
        this.addHeadline(article);
  });
}

Filtering existing articles

class ArticlesView {
  constructor(model, api) {
    this.model = model;
    this.api = api;
    this.articlesFromModel = this.model.getArticles();
    this.newsFeed = document.querySelector("#news-feed");
    this.clearFeedBtn = document.querySelector("#clear-feed-button");
    this.refreshBtn = document.querySelector("#refresh-button");
    this.searchInput = document.querySelector("#search-input");
    this.allHeadlines = [document.querySelectorAll("h1")];
    this.clearFeedBtn.addEventListener("click", () => {
      this.clearFeed();
    });
    // New search function
    this.searchInput.addEventListener("input", (e) => {
      const searchInput = e.target.value.toLowerCase();
      // Create a filtered copy of articlesFromModel
      const filteredArticles = this.articlesFromModel.filter((article) => {
        return article.webTitle.toLowerCase().includes(searchInput);
      });
      this.clearFeed()
      // Display filtered list.
      this.displayArticles(filteredArticles);
    });
}

displayArticlesFromApi() {
  this.api.loadArticles(
    (repoData) => {
      this.model.addArticle(repoData.response.results);
      // displayArticles() is now being passed an argument
      this.displayArticles(this.articlesFromModel);
    },
    () => {
      this.displayError();
    }
  );
}

// displayArticles() now accepts an argument
displayArticles(articles) {
  articles.forEach((article) => {
    this.addImage(article);
    this.addHeadline(article);
  });
}

addHeadline(article) {
  const h1 = document.createElement("h1");
  h1.className = "news-title";
  h1.innerText = article.webTitle;
  h1.onclick = () => {
    window.location.href = article.webUrl;
  };
  this.newsFeed.append(h1);
}

addImage(article) {
  const img = document.createElement("img");
  img.className = "news-image";
  img.setAttribute("id", article.id);
  img.src = article.fields.thumbnail;
  img.onclick = () => {
    window.location.href = article.webUrl;
  };
  this.newsFeed.append(img);
}

displayError() {
  let errorMessage = document.createElement("div");
  errorMessage.className = "error";
  errorMessage.textContent = "Oops, something went wrong!";
  this.newFeed.append(errorMessage);
}

clearFeed() {
  const images = document.querySelectorAll("img.news-image");
  images.forEach((element) => {
    element.remove();
  });
  const headlines = document.querySelectorAll("h1.news-title");
  headlines.forEach((element) => {
    element.remove();
  });
}
}

class ArticlesModel {
  constructor() {
    this.articles = [];
  }

  getArticles() {
    return this.articles;
  }

  addArticle(article) {
    article.forEach((a) => {
      this.articles.push(a);
    });
  }

  reset() {
    this.articles = [];
  }
}

class GuardianApi {
  constructor() {
    this.apiURL = `https://content.guardianapis.com/search?api-key=bb887d29-f877-4222-96b2-c1e7051bcf71&show-fields=thumbnail`;
  }
  loadArticles(callback) {
    fetch(this.apiURL)
      .then((response) => response.json())
      .then((data) => {
        callback(data);
      });
  }
}

const client = new GuardianApi();
const model = new ArticlesModel();
const view = new ArticlesView(model, client);

view.displayArticlesFromApi();
<!doctype html>
<html>

<head>
  <link rel="stylesheet" href="style.css">
  <title> Your Daily News Feed</title>
</head>

<body>
  <h1>News Feed</h1>

  <input type="search" id="search-input" placeholder="e.g. Football...">

  <br><br>
  <button onClick="window.location.reload();">Refresh Page</button> <button id="clear-feed-button">Clear Feed</button>

  <br> <br>

  <div id="news-feed">

  </div>
</body>
<script src="bundle.js"></script>

</html>

  • Related