如何在Knockout绑定输入中使用jQuery UI Spinner小部件?
<tbody data-bind="foreach: orders">
<tr>
<td data-bind="text: Name"></td>
<td><input type="number" style="width: 100px;" data-bind="value: Price" /></td>
<td><input type="number" style="width: 50px;" data-bind="value: VAT" /></td>
<td><input type="number" style="width: 50px;" data-bind="value: Number" /></td>
<td data-bind="text: Final()"></td>
<td><a href="javascript:void(0);" data-bind="click: $root.removeOrder">Remove</a></td>
</tr>
</tbody>
答案 0 :(得分:19)
最好的方法是创建custom binding
以将spinner
绑定到输入:
ko.bindingHandlers.spinner = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize spinner with some optional options
var options = allBindingsAccessor().spinnerOptions || {};
$(element).spinner(options);
//handle the field changing
ko.utils.registerEventHandler(element, "spinchange", function () {
var observable = valueAccessor();
observable($(element).spinner("value"));
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).spinner("destroy");
});
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
current = $(element).spinner("value");
if (value !== current) {
$(element).spinner("value", value);
}
}
};
然后只使用它代替value
绑定:
<input
type="number"
style="width: 100px;"
data-bind="spinner: Price, spinnerOptions: { min: 0 } " />
答案 1 :(得分:2)
此页面上的答案是正确且有用的。然而,我发现当有人在字段中输入值时,我的行为会变得很糟糕。每一次按键似乎都会引发“旋转停止”事件。此外,按键是绕过字段格式和options.step。幸运的是,我们可以检查传入的事件,看看实际发生了什么。可能有更好的方法,但我认为无论如何我都会分享。
// Abstract to a function to allow for multiple binding types
function createSpinner(defaultOptions) {
return {
init: function (element, valueAccessor, allBindingsAccessor) {
var options = $.extend(true, {}, allBindingsAccessor().spinnerOptions, defaultOptions);
var widget = $(element);
var observable = valueAccessor();
widget.spinner(options);
// handle field changes onblur [copies field -> model]
ko.utils.registerEventHandler(element, "blur", function (event) {
var inputValue = Number(widget.spinner("value"));
var modelValue = observable();
if (inputValue !== modelValue) {
// Set the widget (this forces formatting and rounding) - does not fire events
widget.spinner("value", inputValue);
// Read the value back out (saves us rounding)
var numberValue = Number(widget.spinner("value"));
// Set the observable
observable(numberValue);
}
});
// handle other field changes
ko.utils.registerEventHandler(element, "spinstop", function (event) {
// jQuery.spinner spinstop is a bit overzealous with its spinstop event.
if (event.keyCode !== undefined) {
// If it has a keyCode someone is typing... so don't interfere
} else if (event.originalEvent && event.originalEvent.type === "mouseup") {
// This is an *actual* spinstop
var numberValue = Number(widget.spinner("value"));
observable(numberValue);
}
});
// handle disposal
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
widget.spinner("destroy");
});
},
update: function (element, valueAccessor) {
// [copies model -> field]
var widget = $(element);
var observable = valueAccessor();
var inputValue = Number(widget.spinner("value"));
var modelValue = observable();
if (inputValue !== modelValue) {
widget.spinner("value", modelValue);
}
}
};
}
ko.bindingHandlers.moneyspin = createSpinner({ numberFormat: 'C0', culture: 'en-GB', min: 0, incremental: true });
ko.bindingHandlers.intspin = createSpinner({ numberFormat: 'n0', culture: 'en-GB' });
答案 2 :(得分:0)
@Artem Vyshniakov的回答是正确的。但是,如果您正在寻找IE polyfill,而不是在支持它的浏览器中替换html5数字输入,请试试这个:
ko.bindingHandlers.spinner = {
init: function (element, valueAccessor, allBindingsAccessor) {
if (Modernizr.inputtypes.number) {
ko.bindingHandlers.value.init.apply(null, arguments);
} else {
//initialize spinner with some optional options
var options = allBindingsAccessor().spinnerOptions || {};
$(element).spinner(options);
//handle the field changing
$(element).on("spinstop", function () {
var observable = valueAccessor();
observable($(element).spinner("value"));
});
//handle disposal
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).off("spinstop");
$(element).spinner("destroy");
});
}
},
update: function (element, valueAccessor, allBindingsAccessor) {
if (Modernizr.inputtypes.number) {
ko.bindingHandlers.value.update.apply(null, arguments);
} else {
var value = ko.utils.unwrapObservable(valueAccessor());
var disable = allBindingsAccessor().disable;
if (typeof disable !== "undefined") {
$(element).spinner((disable) ? "disable" : "enable");
}
var current = $(element).spinner("value");
if (value !== current) {
$(element).spinner("value", value);
}
}
}
};
function Order(name, price, vat, number) {
var self = this;
self.Name = ko.observable(name);
self.Price = ko.observable(price);
self.VAT = ko.observable(vat);
self.Number = ko.observable(number);
self.Final = ko.computed(function() {
return (self.Price() + self.VAT()) * self.Number();
});
}
function ViewModel() {
var self = this;
self.orders = ko.observableArray();
self.removeOrder = function(item) {
self.orders.remove(item);
};
self.save = function() {
alert(ko.toJSON(self));
};
}
var viewModel = new ViewModel();
viewModel.orders.push(new Order("Sugar", 100, 15, 3));
viewModel.orders.push(new Order("Salt", 200, 25, 4));
viewModel.orders.push(new Order("Milk", 200, 35, 1));
ko.applyBindings(viewModel);