javascriptknockout.jsslick.jsko-custom-binding

knockout custom binding for slick js not working


My working code without knockout binding is :

<div class="slick_demo_1">

                    <div>
                        <div class="ibox-content">
                            <h2>Slide 1</h2>
                        </div>
                    </div>
                    <div>
                        <div class="ibox-content">
                            <h2>Slide 2</h2>
                        </div>
                    </div>
                    <div>
                        <div class="ibox-content">
                            <h2>Slide 3</h2>
                        </div>
                    </div>
                </div>

And to initiallize slick my js code is:

$('.slick_demo_1').slick({
                dots: true
            });

The code above is working fine. Now the code with knockout binding is:

<div class="slick_demo_1" data-bind="foreach:showSlider">

                    <div>
                        <div class="ibox-content">
                            <h2 data-bind="slider"></h2>
                        </div>
                    </div>
                </div>

And to bind slick with DOM my code is:

ko.bindingHandlers.slick = { 
            init: function (element, valueAccessor) {
                $(element).slick({
                    dots: true
                });
            }
        };

But the custom binding is not working. What I am doing wrong?


Solution

  • The code you're showing hardly resembles the correct implementation of a custom binder.

    Several hints:

    The basic structure of a custom binding is explained here. See the code template:

    ko.bindingHandlers.yourBindingName = {
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        // This will be called when the binding is first applied to an element
        // Set up any initial state, event handlers, etc. here
    },
    update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        // This will be called once when the binding is first applied to an element,
        // and again whenever any observables/computeds that are accessed change
        // Update the DOM element based on the supplied values here.
    }
    

    };

    As explained, the init is responsible of initializing the caroulse, and the update is responsible of updating the content of the carousel when the array changes. R P Niemeyer makes an in depth explanation of custom binding here: Another Look at Custom Bindings for KnockoutJS.

    If you implement it correctly, your HTML code should look something like this:

    <div data-bind="slick: imagesArray, slickOptions: additionalOptions>
    </div>
    

    On the init, your custom binding should create the elements based on the imagesArray, and call the slick initialization, using the additionalOptions if present, and register the slick destruction when the element is disposed of by ko. In the update you should modify the inner elements and possible reapply slick. You should also review slick's API.

    This fiddle shows a partial implementation, but more complete that the current the floowing snippet:

    var imageUrls = [];
    var i = 1;
    for (i=1;i<=10;i++)
        imageUrls.push('http://lorempixel.com/400/200/animals/'+i);
    
    ko.bindingHandlers.slick = {
        init: function(element, valueAccessor, allBindingsAccessor) {
            // Clears the div
            $(element).empty();
            // Creates the inner divs with images
            var images = ko.unwrap(valueAccessor());
            if (images) {
            	images.forEach(function(imgUrl) {
                    $div = $('<div>');
                    $image = $('<img>');
                    $image.attr('src',imgUrl);
                    $div.append($image);
                    $(element).append($div)
                });
            }
    
            // try to recover slickOptions
            var options = allBindingsAccessor().slickOptions || {};
    
            // Initialize slick on the div, with provided options
            $(element).slick(options);
    
            //handle disposal, if KO removes the element
            ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
                $(element).slick('unslick');
            });
        },
        //update the control when the view model changes
        update: function(element, valueAccessor) {
            var images = ko.unwrap(valueAccessor());
            // Do something to update the content
        }
    };
    
    
    var vm = {
        images: ko.observableArray(imageUrls),
        options: {}
    }
    
    ko.applyBindings(vm, gallery);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/jquery.slick/1.5.8/slick.min.js"></script>
    <link href="https://cdn.jsdelivr.net/jquery.slick/1.5.8/slick.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/jquery.slick/1.5.8/slick-theme.css" rel="stylesheet">
    
    <div id="gallery" 
         style="width:400px"
         data-bind="slick: images, slickOptions: {dots:true, initialSlide:4}">
    </div>

    The missing part in the implementation is the updating of the gallery if the imgUrls changes. But it shows the main techniques in the hints.