KnockoutJS – Creating a Comma-Delimited List of Values
I’ve been building a lot of great stuff with KnockoutJS lately. It seems that it can enable many – if not all – of the development requirements I have these days, whether I’m building a single-page application (SPA) or just adding a snippet of content to an existing page. I can even build KnockoutJS-enabled “Web Parts” by dropping a Content Editor Web Part [CEWP] into a page. It doesn’t matter what version of SharePoint it is.
The KnockoutJS documentation is generally very good, but sometimes I find the examples lacking a bit. It’s always tempting to make examples show off too much, which often leads to showing off too little. (I have this same problem with my SPServices documentation and examples.)
A common use case is wanting to display a set of values in a comma-delimited list. Let’s take this example. Say I have an observable array of Ticker objects, like so:
self.Tickers = ko.observableArray([ {lookupId: 1, lookupValue:"SPLS"}, {lookupId: 2, lookupValue:"APPL"}, {lookupId: 3, lookupValue:"GOOG"} ]);
Because I’m pulling data from SharePoint, I want to hang onto the lookupId value along with the text value, which is what I want to display. Because of this a simple Tickers.join(“, “) doesn’t cut it.
I’d like to display the list of tickers like this:
SPLS, APPL, GOOG
Pretty simple, right? But after a little Binglage, I couldn’t find a concise example, thus this post.
If you check the KnockoutJS documentation for foreach, you’ll see that there is a variable available called $index. The $index variable gives you the zero-based index of the current array item.
So if I use foreach on Tickers:
<div data-bind="template: { name: 'Ticker', foreach: Tickers }"></div>
I can use the $index to determine if I should emit a comma. We don’t want to see a comma after the last value, so it requires this small bit of finesse.
<script type="text/html" id="Ticker"><span data-bind="visible: $index() > 0">, </span><span data-bind="text: lookupValue"></span></script>
It’s a little bit bass-ackward, but if the $index value is greater than zero – which it is for all values except the first one, where the index is zero – then we *prepend* a comma to the value.
Yes, I’m using a separate template to emit the tickers. That’s mainly because in my case I’m doing a little bit more than what I’m showing here. However, by creating a separate template, I have a reusable piece of markup. that I can use in many places.
I hope someone out there finds this useful!
References
To avoid the consternation of putting the comma before the text in the template, you can create a custom bindingHandler that post-processes the HTML after foreach has done its thing:
ko.bindingHandlers.replace = {
update: function (element, valueAccessor) {
var cfg = valueAccessor();
var s = element.innerHTML;
if (cfg.text)
{
if (cfg.eraseLast == true) {
var last = s.lastIndexOf(cfg.text);
s = s.substring(0, last) + s.substring(last + cfg.text.length);
}
s = s.split(cfg.text).join(cfg.with);
}
element.innerHTML = s;
}
Then use it like this: (this example separates the items with a comma)
%comma%