Search code examples
asp.net-mvckendo-uikendo-asp.net-mvckendo-ui-mvckendo-upload

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?


Solution

  • 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");
    }