Reputation: 6237
I've read this topic how to implement autocomplete with knockout.
I've made it to work, but notice that I couldn't select my own-typed value.
More description: I type any string into input that doesn't exists in autocomplete source. As result this string isn't setted as value of that input.
ko.bindingHandlers.autoComplete = {
// Only using init event because the Jquery.UI.AutoComplete widget will take care of the update callbacks
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
// { selected: mySelectedOptionObservable, options: myArrayOfLabelValuePairs }
var settings = valueAccessor();
var selectedOption = settings.selected;
var options = settings.options;
var elVal = $(element).val();
var updateElementValueWithLabel = function (event, ui) {
// Stop the default behavior
event.preventDefault();
// Update the value of the html element with the label
// of the activated option in the list (ui.item)
$(element).val(ui.item !== null ? ui.item.label : elVal);
// Update our SelectedOption observable
if(typeof ui.item !== "undefined") {
// ui.item - label|value|...
selectedOption(ui.item);
}
};
$(element).autocomplete({
source: options,
select: function (event, ui) {
updateElementValueWithLabel(event, ui);
},
focus: function (event, ui) {
updateElementValueWithLabel(event, ui);
},
change: function (event, ui) {
updateElementValueWithLabel(event, ui);
}
});
}
};
// Array with original data
var remoteData = [{
name: 'Ernie',
id: 1
}, {
name: 'Bert',
id: 2
}, {
name: 'Germaine',
id: 3
}, {
name: 'Sally',
id: 4
}, {
name: 'Daisy',
id: 5
}, {
name: 'Peaches',
id: 6
}];
function ViewModel() {
var self = this;
self.users = remoteData;
self.selectedOption = ko.observable('');
self.options = self.users.map(function (element) {
// JQuery.UI.AutoComplete expects label & value properties, but we can add our own
return {
label: element.name,
value: element.id,
// This way we still have acess to the original object
object: element
};
});
}
$(function () {
ko.applyBindings(new ViewModel());
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://rawgit.com/rniemeyer/knockout-jqAutocomplete/master/build/knockout-jqAutocomplete.js"></script>
<input type="text" data-bind="autoComplete: { selected: selectedOption, options: options }" />
<!-- Debugging -->
<p data-bind="text: ko.toJSON(selectedOption())"></p>
Upvotes: 0
Views: 847
Reputation: 4304
If you want to modify the binding to accept values that aren't in the options list you'll need to adjust updateElementValueWithLabel
to not reset the element value when an item isn't selected.
ko.bindingHandlers.autoComplete = {
// Only using init event because the Jquery.UI.AutoComplete widget will take care of the update callbacks
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
// { selected: mySelectedOptionObservable, options: myArrayOfLabelValuePairs }
var settings = valueAccessor();
var selectedOption = settings.selected;
var options = settings.options;
var elVal = $(element).val();
var updateElementValueWithLabel = function (event, ui) {
// Stop the default behavior
event.preventDefault();
// Update our SelectedOption observable
if(ui.item) {
// Update the value of the html element with the label
// of the activated option in the list (ui.item)
// ui.item - label|value|...
$(element).val(ui.item.label);
selectedOption(ui.item.label);
}else{
selectedOption($(element).val());
}
};
$(element).autocomplete({
source: options,
select: function (event, ui) {
updateElementValueWithLabel(event, ui);
},
change: function (event, ui) {
updateElementValueWithLabel(event, ui);
}
});
}
};
// Array with original data
var remoteData = [{
name: 'Ernie',
id: 1
}, {
name: 'Bert',
id: 2
}, {
name: 'Germaine',
id: 3
}, {
name: 'Sally',
id: 4
}, {
name: 'Daisy',
id: 5
}, {
name: 'Peaches',
id: 6
}];
function ViewModel() {
var self = this;
self.users = remoteData;
self.selectedOption = ko.observable('');
self.options = self.users.map(function (element) {
// JQuery.UI.AutoComplete expects label & value properties, but we can add our own
return {
label: element.name,
value: element.id,
// This way we still have acess to the original object
object: element
};
});
}
$(function () {
ko.applyBindings(new ViewModel());
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://rawgit.com/rniemeyer/knockout-jqAutocomplete/master/build/knockout-jqAutocomplete.js"></script>
<input type="text" data-bind="autoComplete: { selected: selectedOption, options: options }" />
<!-- Debugging -->
<p data-bind="text: ko.toJSON(selectedOption())"></p>
Upvotes: 1