New to Telerik UI for Blazor? Start a free 30-day trial
How to Upload File in Grid
Updated over 6 months ago
Environment
| Product |
Grid for Blazor, Upload for Blazor |
Description
How to upload and download files in a Blazor Grid?
How to display an icon and a download link in a Grid column?
How to add an upload button in every Grid row?
How to upload photos and files as an attachment in a Blazor Grid?
Solution
Here are the required steps to implement file uploading inside the Telerik Blazor Grid.
- The Upload occupies more space than a simple textbox. Use Grid popup editing with a wider popup edit form.
- Configure an Upload component inside a Grid column
<EditorTemplate>. - Handle the
OnUploadevent of the Upload to send custom information to the Upload controller, for example, information about the Grid data item. - Implement the Upload controller methods, which receive and delete the uploaded files. File deletion is optional.
- Handle the
OnSuccessevent of the Upload to confirm successful uploads or file deletions, and update the Grid data item, which is the<EditorTemplate>context. - The name of the saved file on the server can depend on the Razor UI or on the controller.
- If the file name depends on the UI, send it to the controller via the
OnUploadevent arguments (args.RequestData). - If the file name depends on the controller, receive it in the
OnSuccessevent arguments viaargs.Request.ResponseText.
- If the file name depends on the UI, send it to the controller via the
- Handle the Grid
OnUpdate,OnCreateandOnDeleteevents to commit changes to the Grid data source. Optionally, delete the respective saved files inOnDelete. The example below usesOnAddto provide theIdof the new Grid data item, which also affects the uploaded file's name. - Display the uploaded files as images or download links in a Grid column
<Template>.
Example
Upload Files in a Blazor Grid
The tabs below show a possible implementation for the Razor UI, Save and Remove controller methods.
RAZOR
@inject HttpClient HttpClient
@inject NavigationManager NavigationManager
<TelerikGrid Data="@GridData"
TItem="@Product"
EditMode="@GridEditMode.Popup"
OnAdd="@OnGridAdd"
OnUpdate="@OnGridUpdate"
OnCreate="@OnGridCreate"
OnDelete="@OnGridDelete">
<GridSettings>
<GridPopupEditSettings Width="600px" />
</GridSettings>
<GridToolBarTemplate>
<GridCommandButton Command="Add" Icon="@SvgIcon.Plus">Add New</GridCommandButton>
</GridToolBarTemplate>
<GridColumns>
<GridColumn Field="@nameof(Product.Id)" Width="120px" Editable="false" />
<GridColumn Field="@nameof(Product.ImageUrl)" Width="160px" Title="Product Image">
<Template>
@{
var dataItem = (Product)context;
if (!string.IsNullOrEmpty(dataItem.ImageUrl))
{
<img src="@dataItem.ImageUrl" alt="@dataItem.Name" class="product-image" />
<a class="download-link" href="@dataItem.ImageUrl">@dataItem.Name</a>
}
}
</Template>
<EditorTemplate>
@{
var editDataItem = (Product)context;
if (!string.IsNullOrEmpty(editDataItem.ImageUrl))
{
<p>
<img src="@editDataItem.ImageUrl" alt="@editDataItem.Name" class="product-image" />
<br />
<TelerikButton ButtonType="@ButtonType.Button"
Icon="@SvgIcon.Trash"
OnClick="@( () => OnRemoveButtonClick(editDataItem) )">
Delete Current Image
</TelerikButton>
</p>
}
}
<TelerikUpload AllowedExtensions="@( new List<string> { ".jpg", ".jpeg", ".png", ".gif" } )"
Accept=".jpg, .jpeg, .png, .gif"
MaxFileSize="@( 16 * 1024 * 1024 )"
Multiple="false"
SaveUrl="@UploadSaveUrl"
RemoveUrl="@UploadRemoveUrl"
OnUpload="@( (UploadEventArgs args) => OnUploadUpload(args, editDataItem.Id) )"
OnSuccess="@( (UploadSuccessEventArgs args) => OnUploadSuccess(args, editDataItem) )" />
</EditorTemplate>
</GridColumn>
<GridColumn Field="@nameof(Product.Name)" />
<GridCommandColumn Width="240px">
<GridCommandButton Command="Edit" Icon="@SvgIcon.Pencil">Edit</GridCommandButton>
<GridCommandButton Command="Delete" Icon="@SvgIcon.Trash">Delete</GridCommandButton>
</GridCommandColumn>
</GridColumns>
</TelerikGrid>
<style>
.product-image {
display: block;
margin: 0 auto;
max-width: 100px;
max-height: 100px;
}
td a.download-link {
display: block;
text-align: center;
text-decoration: underline;
}
</style>
@code {
private List<Product> GridData { get; set; } = new();
private string UploadSaveUrl => ToAbsoluteUrl("api/upload/save/");
private string UploadRemoveUrl => ToAbsoluteUrl("api/upload/remove/");
private int LastId { get; set; }
private void OnUploadUpload(UploadEventArgs args, int productId)
{
// Send the product ID to the controller to take part in the saved file name.
args.RequestData.Add("productId", productId);
}
private void OnUploadSuccess(UploadSuccessEventArgs args, Product product)
{
if (args.Operation == UploadOperationType.Upload)
{
// Set the image URL in the Grid data item, based on controller response.
product.ImageUrl = args.Request.ResponseText;
}
}
private async Task OnRemoveButtonClick(Product product)
{
var successfulDelete = await DeleteFileOnServer(product.ImageUrl);
if (successfulDelete)
{
// Remove the image URL from the Grid data item.
product.ImageUrl = string.Empty;
}
}
private async Task OnGridUpdate(GridCommandEventArgs args)
{
await Task.Delay(1); // simulate network delay
var updatedItem = (Product)args.Item;
var originalItemIndex = GridData.FindIndex(x => x.Id == updatedItem.Id);
if (originalItemIndex >= 0)
{
GridData[originalItemIndex] = updatedItem;
}
}
private async Task OnGridAdd(GridCommandEventArgs args)
{
await Task.Delay(100); // simulate network delay
var addedItem = (Product)args.Item;
addedItem.Id = ++LastId;
}
private async Task OnGridCreate(GridCommandEventArgs args)
{
await Task.Delay(100); // simulate network delay
var createdItem = (Product)args.Item;
GridData.Insert(0, createdItem);
}
private async Task OnGridDelete(GridCommandEventArgs args)
{
var itemToDelete = (Product)args.Item;
var originalItemIndex = GridData.FindIndex(x => x.Id == itemToDelete.Id);
if (originalItemIndex >= 0)
{
await DeleteFileOnServer(itemToDelete.ImageUrl);
GridData.Remove(itemToDelete);
}
}
private async Task<bool> DeleteFileOnServer(string imageUrl)
{
// Set the file name for delection to the form key, which matches the Upload RemoveField value.
var controllerData = new Dictionary<string, string>() {
{ "files", imageUrl }
};
// UploadRemoveUrl must be an absolute URL.
var result = await HttpClient.PostAsync(UploadRemoveUrl,
new FormUrlEncodedContent(controllerData));
return result.IsSuccessStatusCode;
}
protected override void OnInitialized()
{
GridData = new List<Product>();
for (int i = 1; i <= 3; i++)
{
GridData.Add(new Product()
{
Id = ++LastId,
Name = $"Product {LastId}"
});
}
}
private string ToAbsoluteUrl(string url)
{
return $"{NavigationManager.BaseUri}{url}";
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string ImageUrl { get; set; } = string.Empty;
}
}Notes
- If necessary, adjust the application settings to allow larger file uploads.
- The Upload component itself can't delete files, which have been uploaded in previous edit sessions. Use separate UI for that inside the column
<Template>or the<EditorTemplate>. In both cases, call the controller method directly viaHttpClient.PostAsync(). The UploadOnSuccessevent will not fire in this case. - It is also possible to use the Upload component in a custom edit form outside the Grid.
- Instead of an Upload, you can also implement a similar scenario with a FileSelect component. In that case, the file contents will be available directly in the Razor component, which holds the Grid.