Reputation: 5294
sorry I am a bit new to knockout I am simply trying to do a live search on a table however I can't quite seem to get my model's function to fire on the key up event of the search box. here is the fiddle.
http://jsfiddle.net/LkqTU/26466/
here is the code.
<div class="container">
<table class="table table-condensed table-hover">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Department</th>
</tr>
</thead>
<tbody data-bind='foreach: employees'>
<tr>
<td data-bind='text: firstName'></td>
<td data-bind='text: lastName'></td>
<td data-bind='text: department'></td>
</tr>
</tbody>
</table>
<h2 class="text-center">Search</h2>
<div class="form">
<div class="form-group">
<label>first name:</label>
<input type="search" class="form-control" data-bind="value: query valueUpdate: 'keyup' event: { keyup: search }" autocomplete="off" />
</div>
</div>
</div>
here is the javascript
function employee(firstName, lastName, department) {
this.firstName = ko.observable(firstName);
this.lastName = ko.observable(lastName);
this.department = ko.observable(department);
this.isVisible = ko.observable(true);
}
function model() {
var self = this;
this.employees = ko.observableArray("");
this.query = ko.observable("");
this.search = function () {
$.each(self.employees, function (i, item) {
item.isVisible(false);
if (item.firstName().toLowerCase().indexOf(this.query().toLowerCase()) >= 0) {
item.isVisible(true);
}
});
};
}
var mymodel = new model();
$(document).ready(function () {
loaddata();
ko.applyBindings(mymodel);
});
function loaddata() {
mymodel.employees.push(new employee("Bob", "Jones", "HR"));
mymodel.employees.push(new employee("Mary", "Smith", "HR"));
mymodel.employees.push(new employee("Greg", "Black", "Finance"));
}
Upvotes: 1
Views: 4841
Reputation: 34844
I would recommend using the power of the observable array to show/hide the results of the search, rather than keeping track of which items in the list are visible/hidden in your employee
function/object. One way to achieve that is through the computed
observable, like this:
self.filteredEmployees = ko.computed(function () {
var filter = self.query().toLowerCase();
if (!filter) {
return self.employees();
} else {
return ko.utils.arrayFilter(self.employees(), function (item) {
return item.firstName().toLowerCase().indexOf(filter) !== -1;
});
}
});
Whenever any observable inside of the computed
is updated, then the computed
itself is re-evaluated. So in your case, this effectively puts a subscription on your search text box value (query
observable in the view model).
The other change is that your markup will now need to foreach
bind to the filteredEmployees
computed
instead of the actual employees
observable array.
The logic inside of the computed
does the following:
query
observable). If not, then the entire employees
observable array is returned from the computed
and bound to the foreach
binding.arrayFilter
is used to loop through each of the items in the employees
observable array. Each iteration uses the employee's first name (firstName
observable) to compare against the lower case version of that name to the lower case version of the text typed into the search input text box (query
observable). The nested return
syntax might look a little strange at first, but the inner return
is populating the observable array that the outer return
is using as the value returned for the computed
observable. The filtered returned observable array might be everything, nothing or something in between, but it is not touching the actual employees
observable array; a new/different object is returned from the computed
observable.Note - The filter
variable inside of the computed
observable captures the lower case value of what was typed in by the user. This eliminates the need to force the search input text box value to lower case on each loop iteration (saving time and resources).
One last markup change, instead of using the value
binding and the updateValue
binding, the updated jsFiddle now uses the newer textInput
binding. This is the recommended approach for newer versions of Knockout (3+). It is more efficient and supports copying/pasting/cutting into text boxes, which the value
binding and updateValue
binding struggled with.
Here is the markup change for the textInput
binding:
<input type="search" class="form-control"
data-bind="textInput: query" autocomplete="off" />
Using multiple bindings
Your posted code was missing ,
(commas) to separate multiple bindings on the same element.
This is your posted code:
<input type="search" class="form-control"
data-bind="value: query valueUpdate: 'keyup' event: { keyup: search }"
autocomplete="off" />
It should have been this:
<input type="search" class="form-control"
data-bind="value: query, valueUpdate: 'keyup', event: { keyup: search }"
autocomplete="off" />
Note - I have updated your jsFiddle
Upvotes: 6