Bootstrap-tokenfield KnockoutJS custom binding.
The whole object returned by AJAX is added as a new token, then saved in your model where it can then be processed with computed observables.
Tokens beginning with _
are styled as private, with close button hidden.
- When only an autocomplete is needed typeahead should be used without tokenfield.
- Bloodhound suggestion engine integration.
I'll be happy without Bloodhound until the rest of my current project comes along, fork it if needed and I'll pull in any changes that gracefully handle each case.
bower install knockoutjs-tokenfield
- KnockoutJS
- Bootstrap Tokenfield
- Twitter Typeahead
- Used as engine behind autocomplete for token selection.
- Can likely be used as straight autocomplete without tokenfield (todo).
Link the tokenfield to your own model.
tokenField: fooModel.bar
Request AJAX to fill auto-complete dropdown.
tokenFieldRemote: '../tags/search'
tokenFieldMethod: 'GET'
- default: 'GET'
- jQuery - AJAX docs
(xml|html|json|jsonp|text)
tokenFieldDatatype: 'json'
- default: 'json'
tokenFieldQuery: 'q'
- default: 'q'
This doesn't really do anything. Could be used to control CSV construction.
tokenFieldKeyIndex: 'id'
- default: 'value'
tokenFieldKeyDisplay: 'label'
- default: 'label'
tokenFieldDelimiter: ','
- default: ','
<div class="form-group">
<label class="control-label" for="tags1">Tags</label>
<input
data-bind="tokenField: fooModel.bar1, tokenFieldRemote: '../remote/json/query'"
type="text"
id="tags1"
name="tags1"
value="_default"
class="form-group" placeholder="">
<span class="help-block"></span>
</div>
Output fooModel
:
{
"bar1": [
{
"value": "_default",
"label": "_default"
},
{
"field": "extra data"
"value": "uniqueid",
"label": "tag pushed from model"
},
{
"id": "id from AJAX"
"field": "field from AJAX"
"value": "tag added from server",
"label": "tag added from server"
}
]
}
<script language="javascript" type="text/javascript">
// Test parent model
var fooModel = {};
$(function() {
console.log('main');
fooModel = {
bar1: ko.observableArray([])
}
ko.applyBindings(fooModel);
ko.applyBindings(fooModel, document.getElementById('debug'));
});
</script>
<hr />
<h2>Debug</h2>
<pre id="debug" data-bind="text: ko.toJSON($data, null, 2)"></pre>
.token.tt-private {
background-color: #428bca;
color: #fff;
}
.token.tt-private a.close {
visibility: hidden;
}
This will take an array of tokens and transform it into a CSV of IDs or display keys depending on what is available.
var _dummyObservable = ko.observable();
this.product_ids = ko.computed({
/**
* Construct CSV from array of objects
*/
read: function () {
console.log('computed.read');
var self = this;
// Retrieve and ignore the value, thus giving computed subscription to itself.
_dummyObservable();
var csv = '';
ko.utils.arrayForEach(ko.unwrap(self.products), function(item) {
console.log('item:'+JSON.stringify(item));
if (csv != '') csv += ',';
// Our ID from AJAX response.
if (item.id !== undefined) {
csv += item.id;
// Tokenfield's ID from `value` attrs.
} else if (item.value !== undefined) {
csv += item.value;
// The label, no ID available.
} else {
csv += item.label;
}
});
return csv;
},
/**
* Push set CSV into array of objects.
*/
write: function (value) {
console.log('computed.write');
var self = this;
ko.utils.arrayForEach(value.split(','), function(item) {
self.areas.push({
label: item,
value: item
});
});
_dummyObservable.notifySubscribers();
},
owner: this,
});
Same thing again as an observableArray
reusable fn
. Returns a computed for use in another field.
Usage: self.product_ids = self.products.computeCsv();
.
ko.observableArray['fn'].computeCsv = function() {
console.log('observableArray.computeCsv');
var self = this;
return ko.computed({
read: function () {
console.log('computed.read');
var csv = '';
ko.utils.arrayForEach(ko.unwrap(self), function(item) {
console.log('item:'+JSON.stringify(item));
if (csv != '') csv += ',';
// Our ID from AJAX response.
if (item.id !== undefined) {
csv += item.id;
// Tokenfield's ID form `value` attrs.
} else if (item.value !== undefined) {
csv += item.value;
// The label, no ID available.
} else {
csv += item.label;
}
});
return csv;
},
write: function (value) {
console.log('computed.write');
ko.utils.arrayForEach(value.split(','), function(item) {
self.push({
label: item,
value: item
});
});
}
});
};