Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I show the results of a filtered list using Knockout.Js?

I am building an app that takes a list of restaurants and displays them on a Google Map. I also display this list so the user can see the names of the restaurants.

I want to filter the list using Knockout.js so that when someone starts typing the name of the restaurant, only the one's that match the query appear.

HTML + Knockout Bindings

<div>
  <div id="search">
    <input type="text" placeholder="Filter" data-bind="textInput:query">
  </div>
  <ul data-bind="foreach: restaurants">
    <li data-bind="text: name"></li>
  </ul>
</div>
<div id="map"></div>

JavaScript

var restaurants = [
    {name: 'Chez Simone', location: {lat:48.8603937, lng: 2.3430545}},
    {name: 'Cafe Coutume', location: {lat:48.851599, lng: 2.3162123}},
    {name: 'Café Pinson', location: {lat:48.863732, lng: 2.3631037}},
    {name: 'Sol Semilla', location: {lat:48.8730959, lng: 2.363135900000001}},
    {name: 'Juice Lab Marais', location: {lat:48.8563595, lng: 2.3637123}}
];

//viewModel
var viewModel = function(){
  var self = this;
  self.restaurantList = ko.observableArray(restaurants);

  //Stores user input
  self.query = ko.observable('');

  // Filters through observableArray and filters results using util.arrayFilter();
  self.search = ko.computed(function(){
    var query = this.query().toLowerCase();
    if(!query) {
      return self.restaurantList();
    } else {
      //go through restaurant list and for each restaurant, check if the letters in query
      // appear in restaurant name. If so, display name. else don't.
      var restaurantList = self.restaurantList();
      for(i = 0; i < restaurantList.length; i++){
        var restaurant = restaurantList[i];
        var restaurantName = restaurant.name;
        // check if query letter/s appears in restaurant name
        if(restaurantName.toLowerCase().indexOf(query) > -1){
        // Help! This is where I'm stuck. 
         return restaurantName;
        }
      }
    }
  }, self);
}; // viewModel ends `

The logic I have so far works - if I use console.log(restaurantName) it prints out only the correct restaurants. I can't get it to appear in the view.

How do I display it in the list view?

like image 505
Ana Isabel Avatar asked Dec 10 '25 03:12

Ana Isabel


1 Answers

Your computed property search should always return an array so you can bind the foreach to the filtered collection.

You already got the first part:

if (!query) return self.restaurantList();

In other words, if you're not searching, show all restaurants.

You also succeeded to get most of the last part working, except the part where you loop and return.

You'll have to use restaurantList.filter (or, as you mention in your comment, util.arrayFilter) to create an array of items that match your query.

 return self.restaurantList().filter(function(restaurant) {
     return (restaurant.name.toLowerCase().indexOf(query) > -1)
 });

Then, with foreach: search, you'll get the snippet below:

var restaurants = [
    {name: 'Chez Simone', location: {lat:48.8603937, lng: 2.3430545}},
    {name: 'Cafe Coutume', location: {lat:48.851599, lng: 2.3162123}},
    {name: 'Café Pinson', location: {lat:48.863732, lng: 2.3631037}},
    {name: 'Sol Semilla', location: {lat:48.8730959, lng: 2.363135900000001}},
    {name: 'Juice Lab Marais', location: {lat:48.8563595, lng: 2.3637123}}
];

//viewModel
var viewModel = function(){
  var self = this;
  self.restaurantList = ko.observableArray(restaurants);

  //Stores user input
  self.query = ko.observable('');

  self.search = ko.computed(function(){
    var query = this.query().toLowerCase();
    if(!query) {
      return self.restaurantList();
    } else {
      var restaurantList = self.restaurantList();
      return restaurantList.filter(function(restaurant) {
        var restaurantName = restaurant.name;
        return (restaurantName.toLowerCase().indexOf(query) > -1)
      });
    }
  }, self);
};

ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

<div>
  <div id="search">
    <input type="text" placeholder="Filter" data-bind="textInput:query">
  </div>
  <ul data-bind="foreach: search">
    <li data-bind="text: name"></li>
  </ul>
</div>
<div id="map"></div>

Note that to update any markers in the google maps UI, you'll have to subscribe to search and update your map accordingly.

like image 156
user3297291 Avatar answered Dec 11 '25 17:12

user3297291