Question

I have been working with knockout.js and knockout-sortable.js. In order to create a sortable table. One of the columns of this table should upload images, and still be able to sort the table. The upload works fine, but then when I sort the table the button of the fine uploader is missed, and all the attributes.

Fiddle: http://jsfiddle.net/XdevK/6/

Steps to reproduce:

  1. Click Add Asset
  2. Click again Add Asset.
  3. Move the second asset to the first position. The fineuploader button is missed.

What is actually happening is that the div content is removed once the sort is done.

Js Code:

var CommunityAsset = function(value, description, name) {
        this.Value = value;  
        this.Description = ko.observable(description);
        this.Name = "fineUploader" + name;            
    }
    var viewOverview = function()
    {
        var self = this;

        self.communityAssets = ko.observableArray();

        self.clearAsset = function(data, event) {

               self.communityAssets.remove(data);   

        };

        self.Uploaders = {};
        self.addAsset = function() {
            var name = self.communityAssets().length;
            var asset = new CommunityAsset("http://placehold.it/240x160", "", name);                    
            self.communityAssets.push(asset);
            createFineUploader(name);
        };        
        self.addAssetWithParams = function(value, description, name) {            
            var asset = new CommunityAsset(value, description, name);        
            self.communityAssets.push(asset);
            createFineUploader(name);


        };        
    }
var vc = new viewOverview();
vc.addAssetWithParams("http://placehold.it/240x160", "Hola",0);

ko.applyBindings(vc, $("#communityOverview")[0]);    




 function createFineUploader(intIndex)
    {
        vc.Uploaders[intIndex] = new qq.FineUploader({
                            element: $('#fineUploader'+intIndex)[0],
                            request: {
                                endpoint: '/Communities/FileUpload'
                            },
                            autoUpload: true,
                            sizeLimit: 4000000, // max size
                            validation: {
                                allowedExtensions: ['jpeg', 'jpg', 'gif']
                            },                
                            text: {
                                uploadButton: '<i class="icon-upload icon-white"></i> Upload a file'
                            },
                            multiple: false,
                            template: '<div class="qq-uploader ">' +
                                    '<pre class="qq-upload-drop-area "><span>{dragZoneText}</span></pre>' +
                                    '<div class="qq-upload-button btn btn-success" style="width: auto;">{uploadButtonText}</div>' +
                                    '<ul class="qq-upload-list" style="margin-top: 10px; text-align: center;"></ul>' +
                                '</div>',
                            classes: {
                                success: 'alert alert-success',
                                fail: 'alert alert-error'
                            },
                            callbacks: {
                                onComplete: function(id, name, response) {      
                                    if (response.success) 
                                    {
                                        var asset = ko.utils.arrayFirst(vc.communityAssets(), function(currentAsset) {
                                            return currentAsset.Name == "fineUploader"+intIndex; // <-- is this the desired seat?
                                        });

                                        // asset found?
                                        if (asset) {
                                            asset.Value = response.path;                                            
                                        }                                     
                                    }
                                }
                            },
                            debug: true
                        });

    }

Html

<div class="tab-pane" id="communityOverview"> 
<table class="table" >
                                <thead>
                                    <tr>
                                      <th>Sort Order</th>
                                      <th>Community Overview Image</th>
                                      <th>Community Overview Image Description</th>                  
                                    </tr>
                                </thead>
                                <tbody  data-bind="sortable: communityAssets">
                                    <tr>
                                        <td class="item"><span data-bind="text: $index" ></span>                     
                                        </td>
                                        <td class="item">                        
                                            <div class="image-options">
                                                <div class="control-group">                                                                                     
                                                    <div class="controls">
                                                        <img  style="width:240px; height:160px;"  data-bind="attr:{src: Value}" />                                                      
                                                    </div>
                                                    <br />                                                                                                        
                                                    <div data-bind="attr: {id:Name}"></div>
                                                </div>   
                                            </div><!-- .image-options -->
                                            <a href="Javascript:void(0);" data-bind="click: $root.clearAsset ">Delete Asset</a>
                                        </td>
                                        <td class="item">
                                            <textarea class="input-block-level" rows="11" cols="3" data-bind="value: Description"></textarea>                                            
                                        </td>                  
                                    </tr>              
                                </tbody>
                            </table>
    <a href="#" data-bind="click: addAsset">Add Task</a>                                
</div>

What I think is actually happening is that the sortable recreates the table. This means i should run the recreation of the fineuploader library on the callback of the sort.

How can I mantain the button to stay as it is once the sort is done?

Was it helpful?

Solution

I decided to recreate the fileUploader (this could happen with any other pluggin inserted into the sortable table).

It was kind of easy.

Here's the code (not in jsfiddle because I would lose time making the scenario).

HTML

<table class="table">
<thead>
<tr>
    <th>
        Sort Order
    </th>
    <th>
        Community Overview Image
    </th>
    <th>
        Community Overview Image Description
    </th>
</tr>
</thead>
<tbody data-bind="sortable: communityAssets">
<tr>
    <td class="item">
        <span data-bind="text: $index"></span>
    </td>
    <td class="item">
        <div class="image-options">
            <div class="control-group">
                <div class="controls">
                    <img style="width:240px; height:160px;" data-bind="attr:{src: URL}"/>
                </div>
                <br/>
                <div data-bind="attr: {id: 'fineUploader' + Name}">
                </div>
            </div>
        </div>
        <!-- .image-options -->
        <a href="Javascript:void(0);" data-bind="click: $root.clearAsset ">Delete Asset</a>
    </td>
    <td class="item">
        <textarea class="input-block-level" rows="11" cols="3" data-bind="value: Description"></textarea>
    </td>
</tr>
</tbody>
</table>

Javascript:

 var CommunityAsset = function(value, description, name, url) {
        this.URL = url;
        this.Value = value;  
        this.Description = ko.observable(description);
        this.Name = name;  
        this.FileName  = "";
        this.FileSize = "";          
    }
    var viewOverview = function()
    {
        var self = this;

        self.communityAssets = ko.observableArray();

        self.clearAsset = function(data, event) {

               self.communityAssets.remove(data);   

        };

        self.Uploaders = {};
        self.addAsset = function() {
            var name = self.communityAssets().length;
            var asset = new CommunityAsset("", "", name, "http://placehold.it/240x160");                    
            self.communityAssets.push(asset);
            createFineUploader(name);
        };


    self.addAssetWithParams = function(value, description, name, url) {            
        var asset = new CommunityAsset(value, description, name, url);        
        self.communityAssets.push(asset);

    };       
    self.recreateUploaders = function(arg) {   
        createFineUploader(arg.item.Name);
        if (arg.item.FileName != "")
        {
            $("#fineUploader"+arg.item.Name+" .qq-upload-list").append('<li class=" alert alert-success"><span class="qq-upload-file">'+arg.item.FileName+
            '</span><span class="qq-upload-size" style="display: inline;">'+arg.item.FileSize+'</span></li>');
        }            

function createFineUploader(intIndex)
{
    vc.Uploaders[intIndex] = new qq.FineUploader({
                        element: $('#fineUploader'+intIndex)[0],
                        request: {
                            endpoint: '/Communities/FileUpload'
                        },
                        autoUpload: true,
                        sizeLimit: 4000000, // max size
                        validation: {
                            allowedExtensions: ['jpeg', 'jpg', 'gif']
                        },                
                        text: {
                            uploadButton: '<i class="icon-upload icon-white"></i> Upload a file'
                        },
                        multiple: false,
                        template: '<div class="qq-uploader ">' +
                                '<pre class="qq-upload-drop-area "><span>{dragZoneText}</span></pre>' +
                                '<div class="qq-upload-button btn btn-success" style="width: auto;">{uploadButtonText}</div>' +
                                '<ul class="qq-upload-list" style="margin-top: 10px; text-align: center;"></ul>' +
                            '</div>',
                        classes: {
                            success: 'alert alert-success',
                            fail: 'alert alert-error'
                        },
                        callbacks: {
                            onComplete: function(id, name, response) {      
                                if (response.success) 
                                {
                                    var asset = ko.utils.arrayFirst(vc.communityAssets(), function(currentAsset) {
                                        return currentAsset.Name == intIndex; // <-- is this the desired seat?
                                    });

                                    if (asset) {
                                        var number = response.size;
                                        var size = "";
                                        if (number >= 1048576)
                                        {   
                                            number = number / 1048576;
                                            size = "MB";
                                        }
                                        else
                                        {
                                            number = number / 1024;
                                            size = "kB";

                                        }
                                        asset.FileSize = number.toFixed(1) + "" + size;
                                        asset.FileName = response.fileName;
                                        asset.Value = response.path;
                                    }                                     
                                }
                            }
                        },
                        debug: true
                    });

}              
     }; 
}
var vc = new viewOverview();  
ko.bindingHandlers.sortable.afterMove  = vc.recreateUploaders;
ko.applyBindings(vc, $("#communityOverview")[0]);

The magic is done with ko.bindingHandlers.sortable.afterMove = vc.recreateUploaders; So after a row is moved we recreate the the pluggin creation. There's a portion I added of the FileName, this was done because if you uploaded a file correctly the label of the file name will be also removed if the row is moved, so this will add the FileName label back again.

Hope it helps someone!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top