Introduction
If you're looking for examples using knockout.js and underscore.js together, this should be a good one. The resulting UI is nothing sexy.
A couple of potential annoying gotchas:
The example uses Typscript syntax because that is what I use in my current dev environment.
Please forgive me for not having a hosted demo, I'm coming up to speed on how and where to do that.
Background
While searching for "typeahead" examples, I realized I already did this with knockout.js and underscore.js.
Uses a knockout.js observable with the input filter text, an observable array to hold initialized data, a computed observable with chained underscore.js filter and sort to do the type ahead filtering magic.
For my purposes I'm assuming some basic knowledge and experience with using knockout.js and underscore.js.
Using the Code
A quick typescript class to use:
class myDataModel
{
Name = '';
Type = '';
DATE: Date = null;
constructor(pname, ptype, pdate:Date)
{
this.Name = pname;
this.Type = ptype;
this.DATE = pdate;
}
}
And a quick data mock-up to use.
placeholderData = [
{ Name: 'Bob T. Turner', Type: "M", DATE: new Date('Jan 03 1964') },
{ Name: 'Sarah S. Wilson', Type: "F", DATE: new Date('Apr 16 1956') },
{ Name: 'Frederick R. Flintock', Type: "M", DATE: new Date('Dec 23 1974') },
{ Name: 'Joey P. Ponder', Type: "M", DATE: new Date('Jun 01 1963') },
{ Name: 'Alice C. Reston', Type: "M", DATE: new Date('Aug 28 2000') }
];
The Knockout.js observables, including a computed observable which filters and sorts based on UI input.
I will further explain the use of Knockout observables and the Underscore chain()
below.
dataFilter = ko.observable('');
modelArray = ko.observableArray(<Array<myDataModel>>[]);
computedHtmlResult = ko.computed(() =>
{
var filterText = this.dataFilter();
var baseList = this.modelArray();
if (!filterText)
return '';
else
var vfilteredSearch = _(baseList)
.chain()
.filter((pmodel) => {
return (pmodel.Name.toLowerCase()).indexOf(filterText.toLowerCase()) !== -1;
})
.sortBy((pfilteredmodel) => {
return pfilteredmodel.Name;
})
.value();
var vreturnValue = '';
_.each(vfilteredSearch, (pfsmodel) => {
var vdateText = pfsmodel.DATE.toLocaleDateString('en-US');
vreturnValue += pfsmodel.Name + ' ' +
pfsmodel.Type + ' ' + vdateText + '<br>';
});
return vreturnValue;
});
HTML
With Knockout.js binding is accomplished with "data-bind="textInput: dataFilter "..."
.
<input type="text" id="dev-settings-res-scratchdiv"
data-bind="textInput: dataFilter "
class="input-xxlarge"/>
<span data-bind="html: computedHtmlResult "> </span>
Very clean and simple.
Back to Typescript.
The Knockout.js computed observable computedHtmlResult
contains the important type-ahead intelligence: (if you are not used to underscore.js functional chains, this may not be clear at first, but I will explain more).
If there is no filter text input, we just return:
if (!filterText) return '';
Filter the list of models into a new list of models (vfilteredSearch
) to only include those where the name contains the filter text:
var vfilteredSearch = _(baseList)
Chain the filter and sort.
.chain()
Filter the "scratch" models to only include what we want. I chose to use toLowerCase
to make it case insensitive.
.filter((pmodel) => { return (pmodel.Name.toLowerCase()).indexOf(filterText.toLowerCase()) !== -1; })
Sort by name and add the filtered models to the new filtered array:
.sortBy((pfilteredmodel) => { return pfilteredmodel.Name; }) .value();
Now vfilteredSearch
contains the filtered models.
Let's create some simple HTML text to display in the UI. Not very sexy, but demonstrates the concept quite well:
var vreturnValue = '';
_.each(vfilteredSearch, (pfsmodel) =>
{
var vdateText = pfsmodel.DATE.toLocaleDateString('en-US');
vreturnValue += pfsmodel.Name + ' ' + pfsmodel.Type + ' ' + vdateText + '<br>';
});
return vreturnValue;
The return value now contains simple HTML text for only the filtered models, this will be displayed in the span.
If all works well, the text should be blank until a user inputs something in the text input.
An input of "b
" or "B
" should result in only "Bob T. Turner...
" displayed.
An input of "o
" or "O
" should show all the data.
Currently working with a team developing our next generation web application with a Rest API in C# targeting a single page MVC HTML5 AVL app using Typescript, Knockout, Underscore, Bootstrap, Font-Awesome and Moment.
The backend/server is software-as-a-service (SaaS) currently connected to an SQL DB, but we are looking into utilizing Redis more and future development with Cassandra technologies.
This is quite a fast paced, dynamic project keeping me busy and challenged with new ways of coordinating different tasks and goals, not to mention shifting from my more comfortable OOP C# background into the world of functional programming in the wonderful world of web applications. Let me catch my breath.....