Knockout JS: Fileupload event

lincx picture lincx · Jun 27, 2013 · Viewed 13.5k times · Source

I have this knockout js script for uploading file

This code triggers the upload event when the user selects a file in the upload control

Upload.html

    $(function() {
        var viewModel = {
            filename:  ko.observable(""),
        };

        ko.applyBindings(viewModel);
    });

<form>
<input id="upload" name="upload" 
    data-bind="fileUpload: { property: 'filename', url: 'http://localhost/api/upload/PostFormData' }" 
    type="file" /> 

<button id="submitUpload">Upload</button>
</form>

FileUpload.js

ko.bindingHandlers.fileUpload = {
init: function (element, valueAccessor) {
    $(element).after('<div class="progress"><div class="bar"></div><div class="percent">0%</div></div><div class="progressError"></div>');
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {


    var options = ko.utils.unwrapObservable(valueAccessor()),
        property = ko.utils.unwrapObservable(options.property),
        url = ko.utils.unwrapObservable(options.url);



    if (property && url) {

        $(element).change(function() {
            if (element.files.length) {
                var $this = $(this),
                    fileName = $this.val();

                // this uses jquery.form.js plugin
                $(element.form).ajaxSubmit({
                    url: url,
                    type: "POST",
                    dataType: "text",
                    headers: { "Content-Disposition": "attachment; filename=" + fileName },
                    beforeSubmit: function() {
                        $(".progress").show();
                        $(".progressError").hide();
                        $(".bar").width("0%")
                        $(".percent").html("0%");

                    },
                    uploadProgress: function(event, position, total, percentComplete) {
                        var percentVal = percentComplete + "%";
                        $(".bar").width(percentVal)
                        $(".percent").html(percentVal);

                    },
                    success: function(data) {
                        //$(".progress").hide();
                        //$(".progressError").hide();
                        // set viewModel property to filename
                        $("label[for='upload']").text(data);

                        bindingContext.$data[property](data);
                    },
                    error: function(jqXHR, errorThrown) {
                        $(".progress").hide();
                        $("div.progressError").html(jqXHR.responseText);
                    }
                });
            }
        });
    }
}

}

Now, I want to move the triggering of upload event to the submit button

 <button id="submitUpload">Upload</button>

How to do this? Right now this is where I'm at, I just move the upload event inside the click event of the button. But it's not working, and it doesn't call the ajax request to the API.

  $('#submitUpload').click(function () {

            if (element.files.length) {

                var $this = $(element),
                    fileName = $this.val();
                //alert(element.form);

                // this uses jquery.form.js plugin
                $(element.form).ajaxSubmit({
                    url: url,
                    type: "POST",
                    dataType: "text", 
                    headers: { "Content-Disposition": "attachment; filename=" + fileName },
                    beforeSubmit: function() {
                        $(".progress").show();
                        $(".progressError").hide();
                        $(".bar").width("0%")
                        $(".percent").html("0%");

                    },
                    uploadProgress: function(event, position, total, percentComplete) {
                        var percentVal = percentComplete + "%";
                        $(".bar").width(percentVal)
                        $(".percent").html(percentVal);

                    },
                    success: function(data) {
                        //$(".progress").hide();
                        //$(".progressError").hide();
                        // set viewModel property to filename
                        $("label[for='upload']").text(data);

                        bindingContext.$data[property](data);
                    },
                    error: function(jqXHR, errorThrown) {
                        $(".progress").hide();
                        $("div.progressError").html(jqXHR.responseText);
                    }
                });
            }
        });

Answer

praneeth4victory picture praneeth4victory · Jun 25, 2014

Instead of passing only name, URL to the bindinghandler pass third parameter (fileBinaryData) from your ViewModel Object then read the file Content in KO BindingHandler's Update method then update third observable (fileBinaryData) in update method.

Then you can use this filebinary data in you viewmodel

so for the button bind click event and access fileBinaryData observable which will have the file content.

BindingHandler:

ko.bindingHandlers.FileUpload = {
    init: function (element, valueAccessor) {
        $(element).change(function () {
            var file = this.files[0];
            if (ko.isObservable(valueAccessor())) {
                valueAccessor()(file);
            }
        });
    },
    update: function (element, valueAccessor, allBindingsAccessor) {
        var file = ko.utils.unwrapObservable(valueAccessor());
        var bindings = allBindingsAccessor();

        if (bindings.fileBinaryData && ko.isObservable(bindings.fileBinaryData)) {
            if (!file) {

                bindings.fileBinaryData(null);
            } else {
                var reader = new window.FileReader();
                reader.onload = function (e) {

                    bindings.fileBinaryData(e.target.result);
                };
                reader.readAsBinaryString(file);
            }
        }
    }
}

HTML:

<input type="file" id="fileUpload" class="file_input_hidden" data-bind="FileUpload: spFile, fileObjectURL: spFileObjectURL, fileBinaryData: spFileBinary" /> 

ViewModel:

var viewModel = {
        filename:  ko.observable(""),
        url:  ko.observable(),
        spFileBinary:ko.observable(),
      //Write your CLICK EVENTS
    };

Hope This Helps :)