Reputation: 31
I have a filter function that I would like to adapt to user input, but I don't really know how to do it. I'm fairly new to Knockout so I would appreciate some help with this.
When I click the filter button (see code below) i get this in the input area:
function observable() {
if (arguments.length > 0) {
// Write
// Ignore writes if the value hasn't changed
if (observable.isDifferent(observable[observableLatestValue], arguments[0])) {
observable.valueWillMutate();
observable[observableLatestValue] = arguments[0];
observable.valueHasMutated();
return this; // Permits chained assignments
} else {
// Read
ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
return observable[observableLatestValue];
}
}
What I want to achieve is to write a value in the input area, click the button (for now, will use submit later) and have the search results filtered. The array employeeList is an observable array that is populated through an ajax call (the search function).
KO Code:
self.employeeList = ko.observableArray([]);
self.currentFilter = ko.observable();
self.filterEmpl = ko.computed(function () {
if (!self.currentFilter()) {
return self.employeeList();
} else {
return ko.utils.arrayFilter(self.employeeList(), function (employee) {
return employee.DepartmentName == self.currentFilter();
});
}
});
self.filter = function (value) {
self.currentFilter(value);
} //filter
HTML:
<form>
<input type="text" placeholder="Department" id="department" class="filterInput" data-bind="value: currentFilter" />
<button data-bind="click: function () { filter(currentFilter) }">Filter</button>
<br />
<input type="text" placeholder="Office" class="filterInput" />
<br />
<input type="text" placeholder="Skills" class="filterInput lastInput" />
</form>
Thanks!
Upvotes: 1
Views: 603
Reputation: 23372
Your filterEmpl
is a ko.computed
. This means it automatically updates once one of the observable
values it uses is updated.
In your case, it will update whenever either self.employeeList
or self.currentFilter
changes.
To try this out, type one of the DepartmentNames
in the example below. Once you remove focus from the input, the value
data-bind updates currentFilter
, and self.filterEmpl
is updated.
var VM = function() {
self.employeeList = ko.observableArray([
{ DepartmentName: "Test1", Name: "Employee 1" },
{ DepartmentName: "Test2", Name: "Employee 2" }
]);
self.currentFilter = ko.observable();
self.filterEmpl = ko.computed(function() {
if (!self.currentFilter()) {
return self.employeeList();
} else {
return ko.utils.arrayFilter(self.employeeList(), function(employee) {
return employee.DepartmentName == self.currentFilter();
});
}
});
}
ko.applyBindings(new VM());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<h2>Type "Test1" or "Test2" and blur focus to filter</h2>
<form>
<input type="text" placeholder="Department" data-bind="value: currentFilter" />
</form>
<h2>All employees:</h2>
<ul data-bind="foreach: employeeList">
<li data-bind="text: Name"></li>
</ul>
<h2>Filtered employees:</h2>
<ul data-bind="foreach: filterEmpl">
<li data-bind="text: Name"></li>
</ul>
Now, if you want to filter only when a button is pressed, you don't need the ko.computed
. You define a second ko.observableArray
and write to it from within the filter
function. Note that you don't need to pass it any arguments; the viewmodel is already aware of the currentFilter
value via the value
binding.
var VM = function() {
self.employeeList = ko.observableArray([
{ DepartmentName: "Test1", Name: "Employee 1" },
{ DepartmentName: "Test2", Name: "Employee 2" }
]);
self.currentFilter = ko.observable();
self.filterEmpl = ko.observableArray(self.employeeList());
self.filter = function() {
var result = self.employeeList(),
filter = self.currentFilter();
if (filter) {
result = ko.utils.arrayFilter(result, function(employee) {
return employee.DepartmentName == filter;
});
}
self.filterEmpl(result);
};
}
ko.applyBindings(new VM());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<h2>Type "Test1" or "Test2" and tap button to filter</h2>
<form>
<input type="text" placeholder="Department" data-bind="value: currentFilter" />
<button data-bind="click: filter">filter</button>
</form>
<h2>All employees:</h2>
<ul data-bind="foreach: employeeList">
<li data-bind="text: Name"></li>
</ul>
<h2>Filtered employees:</h2>
<ul data-bind="foreach: filterEmpl">
<li data-bind="text: Name"></li>
</ul>
Personally, I like to use the computed
approach. You can extend the observable using the rateLimit
option if performance is limiting. Ultimately, it's mostly a UX decision.
P.S. The input you did get in the input area is knockout's definition of the ko.observable
function. In your <button>
, you pass currentFilter
without getting its value using currentFilter()
. In filter
, you write this to currentFilter
which is data-bound to the <input>
. I figured it'd be more useful to explain the two approaches, but you still might want to know where the strange input came from...
Upvotes: 1