CarenRose
CarenRose

Reputation: 1341

Kendo Upload control remove event has no response

I have a kendo upload control like this:

@(Html.Kendo().Upload()
    .Name("attachments")
    .Async(a => a
        .Save("UploadAsync", "Intel")
        .Remove("RemoveAsync", "Intel")
        .AutoUpload(true)
    )
    .Events(e => e
        .Success("onSuccessfulUpload")
        .Remove("onRemoveFile")
    )
    .Validation(v => v.AllowedExtensions(exts))
)

In the controller, its Save method is like this:

public ActionResult UploadAsync(IEnumerable<HttpPostedFileBase> attachments)
{
    string filename;
    // ... do things ...
    return Json(new { ImageName = filename }, "text/plain");
}

where the variable filename is assigned a value.

Its Remove method in the controller looks very similar:

public ActionResult RemoveAsync(string[] fileNames)
{
    string filename;
    // ... do things ...
    return Json(new { ImageName = filename }, "text/plain");
}

I verified that both controller methods are called correctly and the variable filename is assigned to in both cases.

The upload works as expected, and the Success event also works as expected. (The alert is simply for testing.)

function onSuccessfulUpload(e) {
    alert(e.response.ImageName);
}

The issue comes on removal of a file.

When I get to the Remove event, e does not have a .response. It has e.files and e.sender, but no response.

function onRemoveFile(e) {
    alert(e.response);                    // undefined!
    alert(JSON.stringify(e.files));       // works, but does not have what I need
}

How do I access what the RemoveAsync method returns?

Upvotes: 1

Views: 3102

Answers (2)

CarenRose
CarenRose

Reputation: 1341

After some time poking around, I found the answer.

The key lay in the order of the events. My first assumption was that the Success event was called after successful upload, and the Remove event was called after successful(?) removal. This was wrong.

The actual order of the events is:

  • JS onUpload > Controller UploadAsync > JS onSuccess

  • JS onRemoveFile > Controller RemoveAsync > JS onSuccess

My Solution:

I created two parallel arrays in javascript to represent the files uploaded in the client-side e.files, which contains uid's for each file, and the filenames created by the server-side controller method (which renames the files).

var fileUids = [];
var fileSaveNames = [];

I changed the onSuccessfulUpload function to this, when I discovered that there is an e.operation that specifies which operation was the successful one:

function onSuccess(e) {
    if (e.operation == "upload") {
        var filename = e.response.ImageName;
        var uid = e.files[0].uid;

        // add to the arrays
        fileUids.push(uid);
        fileSaveNames.push(filename)

        // ...
    }
    else if (e.operation == "remove") {
        var uid = e.files[0].uid;
        var saveIdx = fileUids.indexOf(uid);

        // remove from the arrays
        fileSaveNames.splice(saveIdx, 1);
        fileUids.splice(saveIdx, 1);

        // ...
    }
}

Then I updated the removeFile function, which I now knew was called before the method in the controller.

function removeFile(e) {
    var uid = e.files[0].uid;
    var idx = fileUids.indexOf(uid);
    e.data = { fileToRemove: fileSaveNames[idx] };
}

That last line, where I assign to e.data, was because of this thread on the Telerik forums, which has the following info:

Solution: All that's needed it to define a function for the upload event and modify the "data" payload.

Add the upload JS function to add a parameter "codeID" in my case.

$("#files").kendoUpload({
    [...]
    upload: function (e) {
        e.data = { codeID: $("#id").val() };
    }
});

Now on the controller add the parameter and that's it.

[HttpPost]
public ActionResult Save(IEnumerable<HttpPostedFileBase> files, Guid codeID) { }

(Instead of being in the Upload event, mine is in the Remove event.)

I chose the parameter name fileToRemove, and now the new RemoveAsync method in the controller is as such:

public ActionResult RemoveAsync(string[] fileNames, string fileToRemove)
{
    string returnName = "";

    if (!string.IsNullOrWhiteSpace(fileToRemove))
    {
        // ... do things ...
    }

    return Json(new { ImageName = returnName }, "text/plain");
}

Upvotes: 0

thmshd
thmshd

Reputation: 5847

It looks like the remove event doesn't provide this kind of data, so I see only a workaround to solve this.

You could try to put the result name to the headers, and you should be able to read the result:

// Controller
Response.AddHeader("ImageName", imageName); // before Json(...)

// View/JS
alert(e.headers['ImageName']);

I haven't tested that and I see a risk that that the remove event doesn't really read the async response, that would explain why the response object is not available.

In that case, you could try to use the following workaround: Don't call any Url on remove (or use some Action without any body, just a plain result) and inside of the event callback, execute RemoveAsync yourself.

// View/JS
function onRemoveFile(e) {
    $.post('@Html.Url("RemoveAsync", "Intel")', e.files, function(response) {
        alert(response);
    });
}

It's not pretty, but it should work and provide the results you need.

Upvotes: 0

Related Questions