How to access file input with knockout binding?

You have two problems:

  • if you just write a function call (uploadImage(this.files[0])) inside an object literal it will be executed once when the object literal is initialized so when KO parses the binding. So it will be executed once with the wrong arguments and you change event won't work. You can make it work with wrapping it into an anonymous function. See in documentation Accessing the event object, or passing more parameters section.

  • the this doesn't refer to the current element in the binding you need to use $element instead.

So the correct binding looks like this:

data-bind="event: { change: function() { uploadImage($element.files[0]) } }"

Demo JSFiddle.


For anyone interested, you could use the following custom binding, which allows binding a file input element to a knockout observable containing the File.

It handles setting the observable to the chosen file (as answered by @nemesv), as well as clearing the input element when the observable is being set to null (see this answer):

ko.bindingHandlers.fileUpload = {
    init: function (element, valueAccessor) {
        $(element).change(function () {
            valueAccessor()(element.files[0]);
        });
    },
    update: function (element, valueAccessor) {
        if (ko.unwrap(valueAccessor()) === null) {
            $(element).wrap('<form>').closest('form').get(0).reset();
            $(element).unwrap();
        }
    }
};

Example:

function Example() {
  var self = this;

  self.uploadFile = ko.observable(null);
  self.uploadName = ko.computed(function() {
    return !!self.uploadFile() ? self.uploadFile().name : '-';
  });

  self.clear = function() {
    self.uploadFile(null);
  };
};

ko.bindingHandlers.fileUpload = {
  init: function(element, valueAccessor) {
    $(element).change(function() {
      valueAccessor()(element.files[0]);
    });
  },
  update: function(element, valueAccessor) {
    if (ko.unwrap(valueAccessor()) === null) {
      $(element).wrap('<form>').closest('form').get(0).reset();
      $(element).unwrap();
    }
  }
};

ko.applyBindings(new Example());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<input type="file" data-bind="fileUpload: uploadFile">
<br/>
<br/>Selected file name: <span data-bind="text: uploadName"></span>
<br/>
<button data-bind="click: clear">Clear input</button>

Tags:

Knockout.Js