Mike Wills
Mike Wills

Reputation: 21275

Multiple Uploadify Buttons, which had a file added?

I am setting up a Knockout array where a person can add in multiple items, each with the option to upload a file attachment. I have the buttons working and I can upload a file from anywhere in the DOM, but now I need to know which upload button was clicked so I can attach the file information to right array item. Each button has it's own ID. Any thoughts as to how I determine this?

I am not sure if I can do a jsFiddle with uploadify. Here is the source:

function PetOwner() {
    var self = this;
    self.FirstName = ko.observable('');
    self.MiddleName = ko.observable('');
    self.LastName = ko.observable('');
    self.Address = ko.observable('');
    self.City = ko.observable('Mankato');
    self.State = ko.observable('MN');
    self.ZipCode = ko.observable('56001');
    self.HomePhone = ko.observable('');
    self.WorkPhone = ko.observable('');
    self.MobilePhone = ko.observable('');
    self.Email = ko.observable('');
    self.Pets = ko.observableArray([new PetVM()]);

    self.Message = ko.observable('');

    // Add an additional pet to the application
    self.AddPet = function () {
        self.Pets.push(new PetVM());
        $("input[type=file]").uploadify(uploadifyConfig);
    };
}

// Individual pet view model (included in AnimalLicenseVM)
function PetVM() {
    var self = this;
    self.Type = ko.observable('');
    self.Name = ko.observable('');
    self.Sex = ko.observable('');
    self.Breed = ko.observable('');
    self.SpayedOrNeutered = ko.observable('');
    self.Age = ko.observable('');
    self.Color = ko.observable('');
    self.Marks = ko.observable('');
    self.RabiesTag = ko.observable('');
    self.DateOfVacc = ko.observable('');
    self.Expiration = ko.observable('');
    self.Microchip = ko.observable('');
    self.ImgUrl = ko.observable('');
}

var uploadifyConfig = {
    'swf': "/jquery/uploadify/uploadify.swf",
    'uploader': "/my/uploader/",
    'fileTypeDesc': 'Image Files',
    'fileTypeExts': '*.gif; *.jpg; *.png',
    'fileSizeLimit': '5MB',
    'buttonText': 'Choose Image',
    'scriptAccess': 'always',
    'uploadLimit': 1,
    'height': 20,
    'width': 100,
    //'buttonClass': 'uploadify-button',
    'onUploadSuccess': function (file, data, response) {
        var json = jQuery.parseJSON(data);
        if (json.IsSuccessful) {
            // TODO
        } else {
            alert(json.Message);
        }
    }
}

Here is the relevant HTML.

<div class="pet-row" data-bind="foreach: Pets"> 
    <input class="required" type="text" title="Please include a pet name" data-bind="value: Name, uniqueName: true" />
    <input type="file" accept="image/*" class="fileUpload" data-bind="attr: {id: 'petFile-' + $index() }" />
    <input type="hidden" data-bind="value: ImgUrl, uniqueName: true" />
</div>

Upvotes: 0

Views: 236

Answers (2)

Mike Wills
Mike Wills

Reputation: 21275

The only problem with @Ilya Luzyanin answer was that Uploadify uses flash, thus a click event didn't work.

He did get me going down the right path however. Here is what I had to do:

  1. I left the PetOwner() alone. I didn't have to modify it.
  2. I moved my Uploadify config to the PetVM() and called it self.UploadifyConfig which contained my configuration.
  3. In my onUploadSuccess() method in the UploadifyConfig I was then able to use self.ImgUrl.
  4. I did use the bindingHandler, but I had to use update as init didn't have the id yet at the time of initialization.

My bindingHandler:

ko.bindingHandlers.uploadify = {
    update: function (element, valueAccessor) {
        if ($(element).attr('id') !== '') {
            $(element).uploadify(valueAccessor());
        }
    }
};

Complete PetVM()

function PetVM() {
    var self = this;
    self.Type = ko.observable('');
    self.Name = ko.observable('');
    self.Sex = ko.observable('');
    self.Breed = ko.observable('');
    self.SpayedOrNeutered = ko.observable('');
    self.Age = ko.observable('');
    self.Color = ko.observable('');
    self.Marks = ko.observable('');
    self.RabiesTag = ko.observable('');
    self.DateOfVacc = ko.observable('');
    self.Expiration = ko.observable('');
    self.Microchip = ko.observable('');
    self.ImgUrl = ko.observable('');

    self.UploadifyConfig = {
        'swf': "/jquery/uploadify/uploadify.swf",
        'uploader': "/my/uploader/",
        'fileTypeDesc': 'Image Files',
        'fileTypeExts': '*.gif; *.jpg; *.png',
        'fileSizeLimit': '5MB',
        'buttonText': 'Choose Image',
        'scriptAccess': 'always',
        'uploadLimit': 1,
        'height': 20,
        'width': 100,
        //'buttonClass': 'uploadify-button',
        'onUploadSuccess': function (file, data, response) {
            var json = jQuery.parseJSON(data);
            if (json.IsSuccessful) {
                self.ImgUrl(json.Message);
            } else {
                alert(json.Message);
            }
        }
    }
}

I hope this helps others in the future.

Upvotes: 0

Ilya Luzyanin
Ilya Luzyanin

Reputation: 8110

First of all, I would suggest you to move uploadify initialization to a custom binding handler, because currently you're initializing all file inputs each time new pet is added and also it's as a good practice to not mix DOM manipulation with view model. So your custom binding handler could look something like this:

(function(ko, $) {   
    ko.bindingHandlers.uploadify = {
        init: function(element, valueAccessor) {
            console.log($(element).attr('id') + ' is initialized');
            // Here should be your initialization of
            // uploadify, something like $(element).uploadify(valueAccessor());
        }
    }
})(ko, jQuery);

And in markup something like:

<input type="file" data-bind="click: $parent.signalUpload, attr: {'id': id}, uploadify: $parent.uploadifyConfig" />

I used simplified version of view model, just to demonstrate how to determine which upload button was clicked:

function ViewModel() {
    var self = this;
    self.counter = 1;
    self.buttons = ko.observableArray([]);
    self.addNewItem = function() {
        self.buttons.push({id : self.counter, name: 'New item ' + self.counter++});
    };
    self.signalUpload = function(data) {
        console.log(data.name);
        return true;        
    };
    self.uploadifyConfig = {};
}

Working demo.

When clicking upload button, console will log current item's name, which it receives from data parameter, which is passed to click callback.

Upvotes: 1

Related Questions