Alberto Rocha
Alberto Rocha

Reputation: 13

Filtering list with knockout

I know there is a lot of similar question on the subject, but I have been trying to solved this issue by my own and I was not able to do it, its been a week already.

What I'm trying to do is just to filter the list with a text box. code below:

HTML:

<!DOCTYPE html>
<html>
  <head>
      <title>Magic Towns</title>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <link rel="stylesheet" href="css/styles.css">
    <style>
      html,
      body {
        font-family: Arial, sans-serif;
        height: 100%;
        margin: 0;
        padding: 0;
      }
      #map {
        height: 100%;
      }
    </style>
  </head>
  <body>

<div id="map"></div>
<div class="menuContainer">
  <span class="center" style="font-size:30px;cursor:pointer" onclick="openNav()">&#9776;</span>
  <div id="mySidenav" class="sidenav">
    <a href="javascript:void(0)" class="closebtn" onclick="closeNav()">&times;</a>
    <a href="#"><h1>Locations:</h1></a>
    <form role="search">
                    <input type="text" data-bind="value: query, valueUpdate: 'keyup'" placeholder="Search...">
    </form>
    <div data-bind='with currentPlace' >
      <div data-bind='foreach: placeList'>
        <p href="#" class="whiteFont" data-bind='text: city, click: closeNav'></p>
      </div>
    </div>
  </div>
</div>

    <script src='js/sidebar.js'></script>
    <script src="js/jquery-3.2.1.min.js"></script>
    <script src="js/knockout-3.4.2.js"></script>
    <script src="js/data.js"></script>
    <script src="js/app3.js"></script>


  </body>
</html>

javascript:

var Place = function(data) {
    this.city = ko.observable(data.city);
    this.lat = ko.observable(data.lat);
    this.lng = ko.observable(data.lng);
};


var ViewModel = function() {
    var self = this;

    self.placeList = ko.observableArray([]);
    self.query = ko.observable(''),

    initialPlaces.forEach(function(placeLocation) {
      self.placeList.push( new Place(placeLocation));
    });
    self.currentPlace = ko.observable( this.placeList()[0]);

};



    ko.applyBindings(ViewModel);

data:

var initialPlaces = [
  {"city":"R","lat":22,"lng":-102},
  {"city":"T","lat":23,"lng":-110},
  {"city":"P","lat":18,"lng":-92},
  {"city":"F","lat":25,"lng":-102},
];

I already try this links Link1 Link2 and another ones but nothing... I'm a noob on Javascript and knockout (no more than 2 weeks in to it).

Every time I try any example my list goes blank and nothing is been displayed.

Could you please let me know where can a I find information on how to solve this issue or could you please help with any comment.

Thanks

Upvotes: 0

Views: 1908

Answers (1)

adiga
adiga

Reputation: 35253

There are some issues with your code.

  • with binding creates a new binding context. Which means, knockout will look for placeList property inside currentPlace object since you have that nested inside the with binding.
  • You are missing : after with in your binding (with currentPlace)
  • You need to pass an object to ko.applyBindings, not a function. You can either create an object literal as ViewModel. Or create a function an then use the new operator to create an object. You are using the latter. (Difference b/n object literal and using new operator on a constructor function)

I'm not sure what currentPlace is supposed to do since you want to filter the array based on city. You can create a computed property like this which will filter the array based on query:

var initialPlaces = [{
  "city": "London",
  "lat": 22,
  "lng": -102
}, {
  "city": "New York",
  "lat": 23,
  "lng": -110
}, {
  "city": "New Delhi",
  "lat": 18,
  "lng": -92
}];

var Place = function(data) {
  this.city = ko.observable(data.city);
  this.lat = ko.observable(data.lat);
  this.lng = ko.observable(data.lng);
};

var ViewModel = function() {
  var self = this;

  self.placeList = ko.observableArray([]);
  self.query = ko.observable('');

  initialPlaces.forEach(function(placeLocation) {
    self.placeList.push(new Place(placeLocation));
  });

  // everytime query/placeList changes, this gets computed again
  self.filteredPlaces = ko.computed(function() {
    if (!self.query()) {
      return self.placeList();
    } else {
      return self.placeList()
        .filter(place => place.city().toLowerCase().indexOf(self.query().toLowerCase()) > -1);
    }
  });
};

ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<input type="text" data-bind="value: query, valueUpdate: 'keyup'" placeholder="Search...">

<div data-bind="foreach:filteredPlaces">
  <span data-bind="text:city"></span><br>
</div>

Click on Run code snippet to test it out. Here's a fiddle if you want to play around with it.

Upvotes: 6

Related Questions