Использование нокаута js с ползунками jquery ui

Я пытаюсь выяснить, будет ли нокаут js работать хорошо для следующей проблемы:

У меня есть несколько слайдеров, которые я хочу связать с текстовыми полями.

При изменении текстового поля соответствующий слайдер должен обновиться до нового значения и наоборот.

При изменении значения ползунка или текстового поля необходимо вызвать функцию, которая использует ввод из всех текстовых полей для вычисления результата.

У меня есть мое быстрое и грязное решение jQuery здесь.

Было бы легко добиться того же результата более элегантным способом, используя нокаут js?

Я предполагаю, что мне нужно будет создать настраиваемый обработчик привязки, как это сделано в jQuery UI datepicker change событие, которое не было обнаружено KnockoutJS

Ответ 1

Вот пример: http://jsfiddle.net/jearles/Dt7Ka/

Я использую настраиваемую привязку для интеграции слайдера jquery-ui и использую Knockout для захвата входов и вычисления чистой суммы.

-

интерфейс

<h2>Slider Demo</h2>

Savings: <input data-bind="value: savings, valueUpdate: 'afterkeydown'" />
<div style="margin: 10px" data-bind="slider: savings, sliderOptions: {min: 0, max: 100, range: 'min', step: 1}"></div>

Spent: <input data-bind="value: spent, valueUpdate: 'afterkeydown'" />
<div style="margin: 10px" data-bind="slider: spent, sliderOptions: {min: 0, max: 100, range: 'min', step: 1}"></div>

Net: <span data-bind="text: net"></span>

Просмотр модели

ko.bindingHandlers.slider = {
  init: function (element, valueAccessor, allBindingsAccessor) {
    var options = allBindingsAccessor().sliderOptions || {};
    $(element).slider(options);
    ko.utils.registerEventHandler(element, "slidechange", function (event, ui) {
        var observable = valueAccessor();
        observable(ui.value);
    });
    ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
        $(element).slider("destroy");
    });
    ko.utils.registerEventHandler(element, "slide", function (event, ui) {
        var observable = valueAccessor();
        observable(ui.value);
    });
  },
  update: function (element, valueAccessor) {
    var value = ko.utils.unwrapObservable(valueAccessor());
    if (isNaN(value)) value = 0;
    $(element).slider("value", value);
  }
};

var ViewModel = function() {
    var self = this;

    self.savings = ko.observable(10);
    self.spent = ko.observable(5);
    self.net = ko.computed(function() {
        return self.savings() - self.spent();
    });
}

ko.applyBindings(new ViewModel());

Ответ 2

Я знаю это несколько дней назад, но я внес несколько изменений в код Джона Эрлса:

ko.bindingHandlers.slider = {
init: function (element, valueAccessor, allBindingsAccessor) {
    var options = allBindingsAccessor().sliderOptions || {};
    $(element).slider(options);
    ko.utils.registerEventHandler(element, "slidechange", function (event, ui) {
        var observable = valueAccessor();
        observable(ui.value);
    });
    ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
        $(element).slider("destroy");
    });
    ko.utils.registerEventHandler(element, "slide", function (event, ui) {
        var observable = valueAccessor();
        observable(ui.value);
    });
},
update: function (element, valueAccessor, allBindingsAccessor) {
    var value = ko.utils.unwrapObservable(valueAccessor());
    if (isNaN(value)) value = 0;
    $(element).slider("option", allBindingsAccessor().sliderOptions);
    $(element).slider("value", value);
}
};

Причиной этого является то, что если вы используете параметры, которые меняются (fx другой наблюдаемый), то это не повлияет на слайдер, даже если вы этого захотите.

Ответ 3

@Джон Эрлс и @Майкл Кир Хансен: спасибо за ваши замечательные решения!

Я использовал расширенный код от Michael Kire Hansen. Я привязал опцию "max:" ползунка к ko.observable, и оказалось, что слайдер неправильно обновляет значение в этом случае. Пример. Предположим, что ползунок имеет значение 25 макс. 25, и вы изменяете максимальное значение до 100, ползунок остается в самом правом положении, указывая, что он находится на максимальном значении (но значение все равно 25, а не 100). Как только вы сдвинете одну точку влево, вы получите значение, обновленное до 99.

Решение: в разделе "update:" просто переключите последние две строки на:

$(element).slider("option", allBindingsAccessor().sliderOptions);
$(element).slider("value", value);

Это сначала изменяет параметры, затем значение и работает как шарм.

Ответ 4

Большое спасибо за помощь, мне нужно было использовать слайдер диапазона в моем сценарии, поэтому здесь есть расширение для @John Earles и @Michael Kire Hansen

ko.bindingHandlers.sliderRange = {
init: function (element, valueAccessor, allBindingsAccessor) {
    var options = allBindingsAccessor().sliderOptions || {};
    $(element).slider(options);
    ko.utils.registerEventHandler(element, "slidechange", function (event, ui) {
        var observable = valueAccessor();
        observable.Min(ui.values[0]);
        observable.Max(ui.values[1]);
    });
    ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
        $(element).slider("destroy");
    });
    ko.utils.registerEventHandler(element, "slide", function (event, ui) {
        var observable = valueAccessor();
        observable.Min(ui.values[0]);
        observable.Max(ui.values[1]);
    });
},
update: function (element, valueAccessor, allBindingsAccessor) {
    var value = ko.utils.unwrapObservable(valueAccessor());
    if (isNaN(value.Min())) value.Min(0);
    if (isNaN(value.Max())) value.Max(0);

    $(element).slider("option", allBindingsAccessor().sliderOptions);
    $(element).slider("values", 0, value.Min());
    $(element).slider("values", 1, value.Max());
}
};

а затем HTML для сопровождения

<div id="slider-range" 
             data-bind="sliderRange: { Min: 0, Max: 100 }, 
                                sliderOptions: { 
                                    range: true,
                                    min: 0,
                                    max: 100,
                                    step: 10,
                                    values: [0, 100]
                                }"></div>