0
votes

I've been looking for a clean way to dynamically resize Kendo UI Sliders; Knockout-Kendo and Paul Irish's smart resize plugin have enabled me to arrive at a reasonably workable solution for an individual slider:

<div class="sliderWrapper">
    <input class="slider col" data-bind="kendoSlider: { value: currValue, enabled: enabled, min: 0, max: 100, slide: sliderOnSlide, tickPlacement: 'none', smallStep: 1, showButtons: false, tooltip: { 'enabled': false } }, sliderTip: {}" />
</div>

var ViewModel = function (initValue) {
    this.currValue = ko.observable(initValue);
    this.enabled = ko.observable(true);
};

ko.bindingHandlers.sliderTip = {
    init: function(element, valueAccessor) {
        var dragger = $(element).closest('.k-slider').find('.k-draghandle');
        dragger.empty().html('<span class="sliderTip">0%</span>');
    } 
};

ko.applyBindings(new ViewModel(0));

$(window).smartresize(function() {
    $('.sliderBox').each(function() {
        var value = $(this).val();
        $(this).prev().empty().append('<input class="slider col" data-bind="kendoSlider: { value: currValue, enabled: enabled, min: 0, max: 100, slide: sliderOnSlide, tickPlacement: \'none\', smallStep: 1, showButtons: false, tooltip: { \'enabled\': false } }, sliderTip: {}" />');
         ko.applyBindings(new ViewModel(value));
          $(this).prev().find('.sliderTip').text(value + '%');
    });
});

You can see the result by dragging the borders of the Result pane at this Fiddle: http://jsfiddle.net/MontiDesign/DuZK3/24/

I'm pleased, but I've been working with a Knockout scenario that allows for dynamically adding multiple sliders (see http://jsfiddle.net/MontiDesign/DuZK3/37/). The above solution simply wipes out all the sliders, though. I tried a variation using a custom binding handler that does not work -- and forgive me if this is just hideous, but I'm new to Knockout and still growing in JS/jQuery skills:

ko.bindingHandlers.resizeSlider = {
    init: function(element, valueAccessor, allBindings) {
        var slider = $(element).data('kendoSlider');
        if (slider) {
            $(window).smartresize(function() {
                $(slider).closest('.sliderWrapper').next().each(function() {
                    var value = $(this).val();
                    $(this).prev().empty().append('<input class="slider col" data-bind="kendoSlider: { value: currValue, enabled: enabled, min: 0, max: 100, slide: sliderOnSlide, tickPlacement: \'none\', smallStep: 1, showButtons: false, tooltip: { \'enabled\': false } }, sliderTip: {}" />');
                    ko.applyBindings(new CreateSlider(value));
                    $(this).prev().find('.sliderTip').text(value + '%');
                });
            });
        }
    }
} 

I figure I need to iterate through the array of dynamically created sliders and apply the resize function to each one, but I'm uncertain of how to accomplish this with Knockout.

Thanks in advance for any help you can provide!

1

1 Answers

0
votes

Well, I couldn't find a way to do this without employing multiple bindings with Knockout.js, which I've learned is a very bad idea.

So, I ditched the Kendo sliders. jQuery UI's range sliders base the position of the drag handle on percentages rather than hard pixels, so they are fluid out of the box and resize without a hitch. And they work well with Knockout.

I found this fiddle, and adapted it to my own needs:

<button data-bind="click: $root.addThis">Add a Slider</button>

<div id="wrapper" data-bind="foreach: sliders">
    <div class="sliderContainer">
        <div class="checkbox">
            <input type="checkbox" class="checkbox" data-bind="checked: enabled" /> <span class="sansSerif">Charity Name</span>
        </div>
        <div class="slider col" data-bind="slider: currValue, sliderOptions: { min: 0, max: 100, range: 'min', value: 0 }"></div>
        <input class="sliderBox col sansSerif" type="text" data-bind="value: currValue, enabled: enabled, valueUpdate: 'afterkeydown'" />
    </div>
</div>

$(function () {

    function SliderViewModel() {
        var self = this;

        self.sliders = ko.observableArray();
        self.addThis = function() {
            var s = self.sliders;
            s.push(new CreateSlider());
        }
    };

    ko.bindingHandlers.slider = {
        init: function(element, valueAccessor, allBindingsAccessor) {
            var options = allBindingsAccessor().sliderOptions || {};
            $(element).slider(options);
            $('.ui-slider-handle', element).append('<span data-bind="text: currValue">0</span>%');
            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);
        }   
    };

    ko.applyBindings(new SliderViewModel());

    $(document).on('mouseup touchend', function () {
        if ($('.ui-slider-handle').hasClass('ui-state-focus')) {
            $('.ui-slider-handle.ui-state-focus').removeClass('ui-state-focus');
        }
    });

});

function CreateSlider() {
    var self = this;
    self.currValue = ko.observable(0);
    self.enabled = ko.observable(true);
}

Full example is available at http://jsfiddle.net/MontiDesign/Sb4qG/4/