Reputation: 2292
I have a dropdown menu, made of ul/li/a tags (Bootstrap), that is bound dynamically in Knockout. I'm working on adding a feature to it to filter based on what you're typing by setting the visibility of the li if its selection doesn't match the filter string. That works fine but if they all do not match I'd like to display some sort of "No results" div/message. I haven't found a good way to check if all are not visible though.
Relevant JS:
self.filteredOptions = function (e) {
return $(e).find("a").text().toLowerCase().startsWith(self.filterString());
}
Relevant HTML:
<ul data-bind="foreach: ddlOptions">
<li data-bind="visible: $parent.filteredOptions($element)">
<a href="#" data-bind='event:{ click: function(){ $parent.Selection(Option)}}, attr:{"data-value": ID}, text: Option'></a>
</li>
</ul>
<div> **If dropdown empty make this appear** </div>
ddlOptions
is an array of object like [{ID : "123", Option: "option1"}, ..]
So ideally it would be nice to do somethings like visible: ddlOptions().length == 0
on the message div but I'm just hiding those li tags the data isn't actually changing. Is there an elegant way to check if any are still visible?
Upvotes: 2
Views: 1412
Reputation: 23397
I'd create a computed array that holds only the filtered options. The way you're using $element
to get the text looks a bit backwards to me... (or are there other requirements?)
var vm = function() {
this.options = ko.observableArray([{
ID: "123",
Option: "Option1"
}]);
this.filterString = ko.observable("");
this.filteredOptions = ko.computed(function() {
var originalOptions = this.options();
var filterString = this.filterString().toLowerCase();
if (!filterString) {
return originalOptions;
}
return originalOptions.filter(function(option) {
return option.Option
.toLowerCase()
.indexOf(filterString) === 0;
});
}, this);
};
ko.applyBindings(new vm());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<ul data-bind="foreach: filteredOptions">
<li data-bind="text: Option"></li>
</ul>
<div data-bind="visible: !filteredOptions().length">No matches</div>
<input data-bind="value: filterString, valueUpdate: 'keyup'" />
Upvotes: 2