我有一个使用knockout和jquery ui库的自动完成组合框。
当您输入的内容不在自动完成列表中时,组合框变为空白,这正是我想要它做的,但它也不会更新我的selectedOption
值(我想设置当组合框留空时它为null)
我尝试向subscribe
添加selectedOption
事件,但在这种情况下,淘汰赛不会触发它。我也尝试使用valueAllowUnset
属性,但它也没有用。
非常感谢你的帮助!
这是我的代码段(它看起来有点难看,因为我没有添加CSS,但它显示了问题):
function viewModel() {
var self = this;
self.myOptions = ko.observableArray([{
Name: "First option",
Id: 1
},
{
Name: "Second option",
Id: 2
},
{
Name: "Third option",
Id: 3
}
]);
self.selectedOption = ko.observable(3);
}
var myViewModel = new viewModel();
ko.applyBindings(myViewModel);
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js"></script>
<html>
<body>
<p> Combobox: </p>
<select id="myCombo" data-bind="options: myOptions, optionsText: 'Name', optionsValue: 'Id', value: selectedOption, valueAllowUnset: true, combo: selectedOption"></select>
<p> Option selected Id: </p>
<texarea data-bind="text: selectedOption()"> </texarea>
</body>
</html>
<script>
// ko-combo.js
(function() {
//combobox
ko.bindingHandlers.combo = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize combobox with some optional options
var options = {};
$(element).combobox({
select: function() {
var observable = valueAccessor();
observable($(element).combobox('value'));
}
});
//handle the field changing
ko.utils.registerEventHandler(element, "change", function() {
var observable = valueAccessor();
observable($(element).val());
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).combobox("destroy");
});
},
//update the control when the view model changes
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).combobox('value', value);
}
};
})();
</script>
<script>
// jquery.ui.combobox.js
/*!
* Copyright Ben Olson (https://github.com/bseth99/jquery-ui-extensions)
* jQuery UI ComboBox @VERSION
*
* Adapted from Jörn Zaefferer original implementation at
* http://www.learningjquery.com/2010/06/a-jquery-ui-combobox-under-the-hood
*
* And the demo at
* http://jqueryui.com/autocomplete/#combobox
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
(function( $, undefined ) {
$.widget( "ui.combobox", {
options: {
editable: false
},
version: "@VERSION",
widgetEventPrefix: "combobox",
uiCombo: null,
uiInput: null,
_wasOpen: false,
_create: function() {
var self = this,
select = this.element.hide(),
input, wrapper;
input = this.uiInput =
$( "<input />" )
.insertAfter(select)
.addClass("ui-widget ui-widget-content ui-corner-left ui-combobox-input")
.val( select.children(':selected').text() );
wrapper = this.uiCombo =
input.wrap( '<span>' )
.parent()
.addClass( 'ui-combobox' )
.insertAfter( select );
input
.autocomplete({
delay: 0,
minLength: 0,
appendTo: wrapper,
source: $.proxy( this, "_linkSelectList" )
});
$( "<button>" )
.attr( "tabIndex", -1 )
.attr( "type", "button" )
.insertAfter( input )
.button({
icons: {
primary: "ui-icon-triangle-1-s"
},
text: false
})
.removeClass( "ui-corner-all" )
.addClass( "ui-corner-right ui-button-icon ui-combobox-button" );
// Our items have HTML tags. The default rendering uses text()
// to set the content of the <a> tag. We need html().
input.data( "ui-autocomplete" )._renderItem = function( ul, item ) {
return $( "<li>" )
.append( $( "<a>" ).html( item.label ) )
.appendTo( ul );
};
this._on( this._events );
},
_linkSelectList: function( request, response ) {
var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), 'i' );
response( this.element.children('option').map(function() {
var text = $( this ).text();
if ( this.value && ( !request.term || matcher.test(text) ) ) {
return {
label: text.replace(
new RegExp(
"(?![^&;]+;)(?!<[^<>]*)(" +
$.ui.autocomplete.escapeRegex(request.term) +
")(?![^<>]*>)(?![^&;]+;)", "gi"),
"<strong>$1</strong>"),
value: text,
option: this
};
}
})
);
},
_events: {
"autocompletechange input" : function(event, ui) {
var $el = $(event.currentTarget);
if ( !ui.item ) {
var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( $el.val() ) + "$", "i" ),
valid = false;
this.element.children( "option" ).each(function() {
if ( $( this ).text().match( matcher ) ) {
this.selected = valid = true;
return false;
}
});
if (!this.options.editable) {
if (!valid) {
// remove invalid value, as it didn't match anything
$el.val("");
this.element.prop('selectedIndex', -1);
//return false;
}
}
}
this._trigger( "change", event, {
item: ui.item ? ui.item.option : null
});
},
"autocompleteselect input": function( event, ui ) {
ui.item.option.selected = true;
this._trigger( "select", event, {
item: ui.item.option
});
},
"autocompleteopen input": function ( event, ui ) {
this.uiCombo.children('.ui-autocomplete')
.outerWidth(this.uiCombo.outerWidth(true));
},
"mousedown .ui-combobox-button" : function ( event ) {
this._wasOpen = this.uiInput.autocomplete("widget").is(":visible");
},
"click .ui-combobox-button" : function( event ) {
this.uiInput.focus();
// close if already visible
if (this._wasOpen)
return;
// pass empty string as value to search for, displaying all results
this.uiInput.autocomplete("search", "");
}
},
value: function ( newVal ) {
var select = this.element,
valid = false,
selected;
if (!arguments.length) {
selected = select.children(":selected");
return selected.length > 0 ? selected.val() : null;
}
select.prop('selectedIndex', -1);
select.children('option').each(function() {
if ( this.value == newVal ) {
this.selected = valid = true;
return false;
}
});
if ( valid ) {
this.uiInput.val(select.children(':selected').text());
} else {
this.uiInput.val( "" );
this.element.prop('selectedIndex', -1);
}
},
_destroy: function () {
this.element.show();
this.uiCombo.replaceWith( this.element );
},
widget: function () {
return this.uiCombo;
},
_getCreateEventData: function() {
return {
select: this.element,
combo: this.uiCombo,
input: this.uiInput
};
}
});
}(jQuery));
</script>
答案 0 :(得分:1)
在绑定处理程序中通过change
事件更改select
事件:
function viewModel() {
var self = this;
self.myOptions = ko.observableArray([{
Name: "First option",
Id: 1
},
{
Name: "Second option",
Id: 2
},
{
Name: "Third option",
Id: 3
}
]);
self.selectedOption = ko.observable(3);
}
var myViewModel = new viewModel();
ko.applyBindings(myViewModel);
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js"></script>
<html>
<body>
<p> Combobox: </p>
<select id="myCombo" data-bind="options: myOptions, optionsText: 'Name', optionsValue: 'Id', value: selectedOption, valueAllowUnset: true, combo: selectedOption"></select>
<p> Option selected Id: </p>
<texarea data-bind="text: selectedOption()"> </texarea>
</body>
</html>
<script>
// ko-combo.js
(function() {
//combobox
ko.bindingHandlers.combo = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize combobox with some optional options
var options = {};
$(element).combobox({
change: function() {
var observable = valueAccessor();
observable($(element).combobox('value'));
}
});
//handle the field changing
ko.utils.registerEventHandler(element, "change", function() {
var observable = valueAccessor();
observable($(element).val());
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).combobox("destroy");
});
},
//update the control when the view model changes
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).combobox('value', value);
}
};
})();
</script>
<script>
// jquery.ui.combobox.js
/*!
* Copyright Ben Olson (https://github.com/bseth99/jquery-ui-extensions)
* jQuery UI ComboBox @VERSION
*
* Adapted from Jörn Zaefferer original implementation at
* http://www.learningjquery.com/2010/06/a-jquery-ui-combobox-under-the-hood
*
* And the demo at
* http://jqueryui.com/autocomplete/#combobox
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
(function( $, undefined ) {
$.widget( "ui.combobox", {
options: {
editable: false
},
version: "@VERSION",
widgetEventPrefix: "combobox",
uiCombo: null,
uiInput: null,
_wasOpen: false,
_create: function() {
var self = this,
select = this.element.hide(),
input, wrapper;
input = this.uiInput =
$( "<input />" )
.insertAfter(select)
.addClass("ui-widget ui-widget-content ui-corner-left ui-combobox-input")
.val( select.children(':selected').text() );
wrapper = this.uiCombo =
input.wrap( '<span>' )
.parent()
.addClass( 'ui-combobox' )
.insertAfter( select );
input
.autocomplete({
delay: 0,
minLength: 0,
appendTo: wrapper,
source: $.proxy( this, "_linkSelectList" )
});
$( "<button>" )
.attr( "tabIndex", -1 )
.attr( "type", "button" )
.insertAfter( input )
.button({
icons: {
primary: "ui-icon-triangle-1-s"
},
text: false
})
.removeClass( "ui-corner-all" )
.addClass( "ui-corner-right ui-button-icon ui-combobox-button" );
// Our items have HTML tags. The default rendering uses text()
// to set the content of the <a> tag. We need html().
input.data( "ui-autocomplete" )._renderItem = function( ul, item ) {
return $( "<li>" )
.append( $( "<a>" ).html( item.label ) )
.appendTo( ul );
};
this._on( this._events );
},
_linkSelectList: function( request, response ) {
var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), 'i' );
response( this.element.children('option').map(function() {
var text = $( this ).text();
if ( this.value && ( !request.term || matcher.test(text) ) ) {
return {
label: text.replace(
new RegExp(
"(?![^&;]+;)(?!<[^<>]*)(" +
$.ui.autocomplete.escapeRegex(request.term) +
")(?![^<>]*>)(?![^&;]+;)", "gi"),
"<strong>$1</strong>"),
value: text,
option: this
};
}
})
);
},
_events: {
"autocompletechange input" : function(event, ui) {
var $el = $(event.currentTarget);
if ( !ui.item ) {
var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( $el.val() ) + "$", "i" ),
valid = false;
this.element.children( "option" ).each(function() {
if ( $( this ).text().match( matcher ) ) {
this.selected = valid = true;
return false;
}
});
if (!this.options.editable) {
if (!valid) {
// remove invalid value, as it didn't match anything
$el.val("");
this.element.prop('selectedIndex', -1);
//return false;
}
}
}
this._trigger( "change", event, {
item: ui.item ? ui.item.option : null
});
},
"autocompleteselect input": function( event, ui ) {
ui.item.option.selected = true;
this._trigger( "select", event, {
item: ui.item.option
});
},
"autocompleteopen input": function ( event, ui ) {
this.uiCombo.children('.ui-autocomplete')
.outerWidth(this.uiCombo.outerWidth(true));
},
"mousedown .ui-combobox-button" : function ( event ) {
this._wasOpen = this.uiInput.autocomplete("widget").is(":visible");
},
"click .ui-combobox-button" : function( event ) {
this.uiInput.focus();
// close if already visible
if (this._wasOpen)
return;
// pass empty string as value to search for, displaying all results
this.uiInput.autocomplete("search", "");
}
},
value: function ( newVal ) {
var select = this.element,
valid = false,
selected;
if (!arguments.length) {
selected = select.children(":selected");
return selected.length > 0 ? selected.val() : null;
}
select.prop('selectedIndex', -1);
select.children('option').each(function() {
if ( this.value == newVal ) {
this.selected = valid = true;
return false;
}
});
if ( valid ) {
this.uiInput.val(select.children(':selected').text());
} else {
this.uiInput.val( "" );
this.element.prop('selectedIndex', -1);
}
},
_destroy: function () {
this.element.show();
this.uiCombo.replaceWith( this.element );
},
widget: function () {
return this.uiCombo;
},
_getCreateEventData: function() {
return {
select: this.element,
combo: this.uiCombo,
input: this.uiInput
};
}
});
}(jQuery));
</script>