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