Tags listing inside input field
This simulates the Select2 tag lookup with ko.sortable and jqAuto autocomplete
HTML
<link rel="stylesheet" href="jquery-ui.css">
<script src="http://knockoutjs.com/downloads/knockout-3.3.0.js"></script>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<script src="https://rawgit.com/rniemeyer/knockout-sortable/master/build/knockout-sortable.js"></script>
<div class="form-group">
<label>Tags:</label>
<div class="form-control" data-bind="css: {'focus' : isSelected()}">
<div class="tag-list" data-bind="sortable:{template:'tagsTmpl',data:$root.Tags}"></div>
<input class="taginput"
placeholder="<add a tag>"
data-bind="jqAuto: { autoFocus: true },
jqAutoSource: $root.myTags,
jqAutoValue: $root.tagname,
jqAutoSourceLabel: 'text',
jqAutoSourceInputValue: 'text',
jqAutoSourceValue: 'text',
textInput: $root.tagname,
event: {keyup: $root.inputTag},
hasFocus: isSelected"/>
</div>
</div>
<hr>
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
<div id="console-log"></div>
<!-- tagsTmpl-scene -->
<script type="text/html" id="tagsTmpl">
<div class="tag">
<span class="tag-action remove" data-bind="
click: function(){$root.removeTag($data)};
">x</span>
<span class="tag-text" data-bind="text: text"></span>
</div></script>
CSS
.tag-list {
height: auto;
min-height: 25px;
max-height: 350px;
padding: 0px 2px;
margin: 0px;
overflow-y: auto;
float: left;
clear: both;
}
.tag {
float: left;
background-color: #ccc;
border-radius: 2px;
margin: 0px 2px;
padding: 0px 2px;
cursor: move;
white-space: nowrap;
border:1px solid #777;
}
.tag .tag-action {
width: 0.5em;
cursor: pointer;
}
.tag-action.remove {
/* ASG 2015 css */
color: red;
}
input.taginput {
float: left;
background-color: #FcFcFc;
min-width: 34px;
max-width: 248px;
z-index:12;
margin-left: 5px;
border:none;
margin-top: 2px;
font-size: 14px;
}
input.taginput:focus {
outline: 0;
}
.focus {
border-color: rgba(82, 168, 236, 0.8);
outline: 0;
outline: thin dotted \9;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);
}
.console-line {
font-family: monospace;
margin: 2px;
}
JavaScript
//jqAuto -- main binding (should contain additional options to pass to autocomplete)
//jqAutoSource -- the array to populate with choices (needs to be an observableArray)
//jqAutoQuery -- function to return choices
//jqAutoValue -- where to write the selected value
//jqAutoSourceLabel -- the property that should be displayed in the possible choices
//jqAutoSourceInputValue -- the property that should be displayed in the input box
//jqAutoSourceValue -- the property to use for the value
ko.bindingHandlers.jqAuto = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var options = valueAccessor() || {},
allBindings = allBindingsAccessor(),
unwrap = ko.utils.unwrapObservable,
modelValue = allBindings.jqAutoValue,
source = allBindings.jqAutoSource,
query = allBindings.jqAutoQuery,
valueProp = allBindings.jqAutoSourceValue,
inputValueProp = allBindings.jqAutoSourceInputValue || valueProp,
labelProp = allBindings.jqAutoSourceLabel || inputValueProp;
//function that is shared by both select and change event handlers
function writeValueToModel(valueToWrite) {
if (ko.isWriteableObservable(modelValue)) {
modelValue(valueToWrite);
} else { //write to non-observable
if (allBindings['_ko_property_writers'] && allBindings['_ko_property_writers']['jqAutoValue']) allBindings['_ko_property_writers']['jqAutoValue'](valueToWrite);
}
}
//on a selection write the proper value to the model
options.select = function (event, ui) {
writeValueToModel(ui.item ? ui.item.actualValue : null);
};
//hold the autocomplete current response
var currentResponse = null;
//handle the choices being updated in a DO, to decouple value updates from source (options) updates
var mappedSource = ko.dependentObservable({
...