Good afternoon,
I have a FileManager that I want to perform server-side checks when directories or files are renamed or deleted. For instance, the directory has files that must be kept.
I was hoping to use the command event:
command - API Reference - Kendo UI FileManager - Kendo UI for jQuery
Which mentions:
- Fired when server command is executed (copy, move, delete or rename).
- The event is useful to get feedback when server commands has failed or succeeded and take additional actions based on the status.
How, in my delete or update server methods, do I force an e.status of fail, and e.response text explaining why the delete or update was rejected, so I can handle that in the client Command event?
I can see that I could throw an error and then capture that in the Error event, but is it possible to do similar with the Command event? The scenario I'm describing isn't really an error, just some validation that failed.
Kind regards,
Richard
1 Answer, 1 is accepted
Hello Richard,
Thank you for the details provided.
You can handle server-side validation failures in the FileManager's Command event by customizing the server response in your delete or update methods. This approach allows you to communicate validation issues (such as protected files in a directory) to the client without triggering the Error event, since these are not actual errors but business rule validations.
How to return a custom response from the server:
- In your server-side action (for delete, rename, etc.), return a JSON object with a status property (e.g., "fail") and a message describing the validation issue.
Example server-side code (C#):
return Json(new {
status = "fail",
message = "Cannot delete directory: contains files that must be kept."
});
Handling the response in the client Command event:
- In your FileManager initialization, use the command event to check the response status and display the validation message.
Example client-side code:
$("#filemanager").kendoFileManager({
// ... other configuration ...
command: function(e) {
if (e.status === "fail") {
alert(e.response.message); // Show the validation failure to the user
// Optionally, prevent further actions or update UI as needed
}
// Handle successful operations as usual
}
});
Summary:
- This method allows you to treat validation failures separately from actual errors, using the Command event for feedback.
- You do not need to throw exceptions or use the Error event for validation logic.
For more details, refer to the FileManager Command event documentation:
I hope this information helps.
Kind Regards,
Anton Mironov
Progress Telerik
Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.
Hi Anton,
Many thanks for your reply.
I had already tried something similar, which didn't seem to work. I've now implemented your suggestion and it still doesn't appear to accept the status and message I'm sending.
Here's how I've got it set up.
Client-side code:
@(Html.Kendo().FileManager().Name("filemanager") .DataSource(ds => { ds.Read(operation => operation .Type(HttpVerbs.Post) .Action("Read", "FileManager") .Data("getEnvironment") ); ds.Destroy(operation => operation .Type(HttpVerbs.Post) .Action("Destroy", "FileManager") .Data("sendingAdditionalParameter") ); ds.Create(operation => operation .Type(HttpVerbs.Post) .Action("Create", "FileManager") .Data("sendingAdditionalParameter") ); ds.Update(operation => operation .Type(HttpVerbs.Post) .Action("Update", "FileManager") .Data("sendingAdditionalParameter") ); }) .Toolbar(tb => tb.Items(items => { items.Add("createFolder"); items.Add("sortDirection"); items.Add("sortField"); items.Add("changeView"); items.Add("spacer"); items.Add("details"); items.Add("search"); })) .ContextMenu(context => context.Items(items => { items.Add("rename"); items.Add("delete"); })) .Events(events => events .Command("onCommand") .Error("onError") ) )
function onCommand(e) {
console.log("Command " + e.action + " = item: " + e.data.item.path + "; status: " + e.status);
if (e.status === "fail") {
e.sender.refresh();
var respsonse = e.response.message;
if (response) {
$("#error-message").html(response + "<br /><br />");
}
}
}
Server-side code (C#):
[ValidateAntiForgeryToken]
public virtual async Task<ActionResult> DestroyAsync(FileModel entry, int environmentId)
{
if (environmentId > 0)
{
var flag = true;
string responseText = string.Empty;
// Get list of folders mapped in the selected Environment
var folders = GetFolders(environmentId);
var path = NormalizePath(entry.Path);
if (!string.IsNullOrEmpty(path))
{
if (entry.IsDirectory)
{
// Check for mapping in db
var folder = folders.Any(l => l.Location == entry.Path);
if (folder)
{
flag = false;
responseText = "This folder is mapped in the database and cannot be deleted.";
}
else
{
DeleteDirectory(path);
}
}
else
{
DeleteFile(path);
}
}
if (!flag)
{
return Json(new { status = "fail", message = responseText });
}
}
return Json(Array.Empty<object>());
}
This is the outcome shown in the console:
Is there something obvious that I'm missing?
Kind regards,
Richard
Hi Anton,
As a quick follow up, the response in the client Command event is showing as undefined:
Even though the server response for the Destroy POST is showing the correct Json returned:
Kind regards,
Richard
Hello Richard,
Thank you for the kind words and the additional details provided.
The issue is that the FileManager expects the server response for its CRUD operations (such as Destroy) to be in a specific format—typically an array of objects, not a plain object. If you return a plain object, the response will not be parsed as expected and e.response will be undefined in your Command event.
How to Fix the Response Format
Server-side:
Change your response for validation failures from a plain object to an array containing your object.
Example:
return Json(new[] { new { status = "fail", message = responseText } });
For successful operations, you can continue returning an empty array:
return Json(Array.Empty<object>());
Since the response is now an array, access the first element to get your custom status and message:
function onCommand(e) {
var response = e.response && e.response[0]; // Access the first item in the array
console.log("Full response:", e.response); // Debug: see the actual response
if (response && response.status === "fail") {
e.sender.refresh();
if (response.message) {
$("#error-message").html(response.message + "<br /><br />");
}
}
}
Best Regards,
Anton Mironov
Hi Anton,
Many thanks for your reply and explanation.
I've implemented your suggestion and I'm still seeing the same result - e.response is undefined:
However, the response for the Destroy operation is now showing as an array:
The code I'm running is similar to the demo, albeit I'm passing a parameter environmentId so that the server side code knows where to look for the folders/files. The FileManager display is based on an environment chosen from a dropdown list. I'm just returning a custom response from the Destroy operation.
I'm running Telerik UI for ASP.NET Core 2024 Q3
Kind regards,
Richard
Hi Anton,
I've upgraded to Telerik UI for ASP.NET Core 2025 Q3.
I've built a simple app which is based on the demo https://demos.telerik.com/aspnet-core/filemanager but has the OnCommand(e) client event, and the following amendment to the Destroy server action:
public virtual ActionResult Destroy(FileManagerEntry entry)
{
ICollection<FileManagerEntry> sessionDir = HttpContext.Session.GetObjectFromJson<ICollection<FileManagerEntry>>(SessionDirectory);
var path = Path.Combine(ContentPath, NormalizePath(entry.Path));
var currentEntry = sessionDir.FirstOrDefault(x => x.Path == path);
if (currentEntry != null)
{
var response = new[]
{
new { status = "fail", message = "This folder is mapped in the database and cannot be deleted." }
};
return Json(response);
}
throw new Exception("File Not Found");
}
This is also giving me the same result i.e. e.response is undefined.
Unsurprisingly, it behaves similarly for the Rename/Update action.
Kind regards,
Richard
Hi Richard,
Thank you for the additional details provided.
I decided to take a step back and prepare a sample project for the case with the code from 1 September.
There is actually a typo in the "response" - one additional "s":
function onCommand(e) {
console.log("Command " + e.action + " = item: " + e.data.item.path + "; status: " + e.status);
if (e.status === "fail") {
e.sender.refresh();
var respsonse = e.response.message;
if (response) {
$("#error-message").html(response + "<br /><br />");
}
}
}
In order to receive the error message, I used this Destroy method, where I am forcing the deletion to have an error:
public ActionResult Destroy([DataSourceRequest] DataSourceRequest request, FileManagerEntry entry)
{
ModelState.AddModelError("delete", "This folder is mapped in the database and cannot be deleted.");
return Json(new[] { entry }.ToDataSourceResult(request, ModelState));
}
.DataSource(ds =>
{
ds.Read(op => op
.Type(HttpVerbs.Post)
.Action("Read", "Home")
);
ds.Destroy(op => op
.Type(HttpVerbs.Post)
.Action("Destroy", "Home")
);
ds.Events(e => e.RequestEnd("onFileManagerRequestEnd"));
})
function onFileManagerRequestEnd(e) {
if (e.type === "destroy" && e.response && e.response.Errors) {
var messages = [];
$.each(e.response.Errors, function (key, value) {
if (value.errors) {
$.each(value.errors, function () {
messages.push(this);
});
}
});
if (messages.length) {
alert("Error:\n" + messages.join("\n"));
$("#filemanager").getKendoFileManager().dataSource.read();
}
}
}
Attached is the sample project that I prepared for the case. Feel free to test it on your side. I hope you will like the result.
Kind Regards,
Anton Mironov
Hi Anton,
Many thanks for your reply.
Using the sample project and the ModelState code that you provided, I have been able to achieve exactly what I needed, and in a neat way.
Thank you very much for all of your help with this.
Kind regards,
Richard
Hi Richard,
Thank you for the kind words. I am glad to hear that the desired result has been achieved.
If additional assistance or information is needed, do not hesitate to contact the Team.
Greetings,
Anton Mironov