New to Telerik UI for Blazor? Start a free 30-day trial
Popup Form Template
Updated on Sep 24, 2025
With the FormTemplate
feature, you can customize the appearance and content of the create/edit Popup window of the Grid.
In this article:
Using the Popup Form Template
- Declare the desired custom content inside the
<FormTemplate>
inner tag of the<GridPopupEditFormSettings>
. For example,TelerikForm
orEditForm
. - The
FormTemplate
provides acontext
of typeGridPopupEditFormTemplateContext
. It contains a clone of the Grid data item in itsItem
property, and reveals if the user is adding a new item or editing an existing one through itsIsNew
boolean property. Castcontext.Item
to your model type, so you can pass it to the custom form. - (optional) Use the
Context
attribute of the<FormTemplate>
tag to set the name of thecontext
variable.
Specifics
When using the template, the built-in popup form is replaced by the declared content in the FormTemplate
tag. This introduces the following specifics:
- The default Update and Cancel buttons are removed. This means that the
OnUpdate
andOnCancel
events do not fire. To modify or cancel the update of a record, you need to include custom components or events to manage these actions. - There are two ways to define custom Form buttons:
- Use the Form
<FormButtons>
template. - Use the Grid
<ButtonsTemplate>
, which is empty by default when using a<FormTemplate>
. Remove the duplicate built-in Form Submit button with an empty<FormButtons>
template.
- Use the Form
- If you leave the Grid popup footer (
<ButtonsTemplate>
) empty, it takes up space in the popup. You can remove this empty popup footer space with CSS. - The
FormTemplate
disables the built-in validation of the Grid. Implement a Form Validation instead. - The
<GridPopupEditFormSettings>
parameters do not apply to a customTelerikForm
that you may render inside the<FormTemplate>
tag. Set the desired Form configurations such asColumns
,Orientation
, and more on the Form component.
Example
Using a FormTemplate
to modify the Edit/Create Popup window.
@using System.ComponentModel.DataAnnotations
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
<TelerikGrid @ref="@GridRef"
OnRead="@OnGridRead"
TItem="@Product"
EditMode="@GridEditMode.Popup"
OnDelete="@OnGridDelete">
<GridToolBarTemplate>
<GridCommandButton Command="Add">Add Item</GridCommandButton>
</GridToolBarTemplate>
<GridSettings>
<GridPopupEditSettings Width="550px" MaxHeight="95vh" MaxWidth="95vw"></GridPopupEditSettings>
<GridPopupEditFormSettings Context="formContext">
<FormTemplate>
@{
if (GridEditItem is null)
{
// Setting GridEditItem unconditionally may
// reset the modified and unsaved values after re-render.
// Nullify GridEditItem when editing completes.
GridEditItem = (Product)formContext.Item;
}
<TelerikForm Model="@GridEditItem"
ColumnSpacing="20px"
Columns="2"
ButtonsLayout="@FormButtonsLayout.Stretch"
OnValidSubmit="@OnFormValidSubmit">
<FormItems>
<FormItem Field="@nameof(Product.Id)" Enabled="false" />
<FormItem Field="@nameof(Product.Name)" />
<FormItem Field="@nameof(Product.Description)"
ColSpan="2"
EditorType="@FormEditorType.TextArea" />
<FormItem Field="@nameof(Product.Price)">
<Template>
<label class="k-label k-form-label">Price</label>
<div class="k-form-field-wrap">
<TelerikNumericTextBox @bind-Value="@GridEditItem.Price"
DebounceDelay="0" />
<TelerikValidationMessage For="@( () => GridEditItem.Price)" />
</div>
</Template>
</FormItem>
<FormItem Field="@nameof(Product.Quantity)" />
<FormItem Field="@nameof(Product.ReleaseDate)" />
<FormItem Field="@nameof(Product.Discontinued)" />
</FormItems>
<FormButtons>
<TelerikButton Icon="@nameof(SvgIcon.Save)">Save</TelerikButton>
<TelerikButton Icon="@nameof(SvgIcon.Cancel)"
ButtonType="@ButtonType.Button"
OnClick="@ExitGridEditMode">Cancel</TelerikButton>
</FormButtons>
</TelerikForm>
}
</FormTemplate>
</GridPopupEditFormSettings>
</GridSettings>
<GridColumns>
<GridColumn Field="@nameof(Product.Name)" />
<GridColumn Field="@nameof(Product.Price)" DisplayFormat="{0:C2}" />
<GridColumn Field="@nameof(Product.Quantity)" DisplayFormat="{0:N0}" />
<GridColumn Field="@nameof(Product.ReleaseDate)" DisplayFormat="{0:d}" />
<GridColumn Field="@nameof(Product.Discontinued)" Width="120px" />
<GridCommandColumn Width="180px">
<GridCommandButton Command="Edit">Edit</GridCommandButton>
<GridCommandButton Command="Delete">Delete</GridCommandButton>
</GridCommandColumn>
</GridColumns>
</TelerikGrid>
@code {
private ProductService GridProductService { get; set; } = new();
private TelerikGrid<Product>? GridRef { get; set; }
private Product? GridEditItem { get; set; }
private async Task OnFormValidSubmit()
{
if (GridEditItem is null)
{
return;
}
if (GridEditItem.Id != default)
{
await GridProductService.Update(GridEditItem);
}
else
{
await GridProductService.Create(GridEditItem);
}
await ExitGridEditMode();
}
private async Task ExitGridEditMode()
{
if (GridRef is null)
{
return;
}
var state = GridRef.GetState();
state.OriginalEditItem = null!;
state.EditItem = null!;
state.InsertedItem = null!;
await GridRef.SetStateAsync(state);
GridEditItem = default;
}
private async Task OnGridDelete(GridCommandEventArgs args)
{
var deletedItem = (Product)args.Item;
await GridProductService.Delete(deletedItem);
}
private async Task OnGridRead(GridReadEventArgs args)
{
DataSourceResult result = await GridProductService.Read(args.Request);
args.Data = result.Data;
args.Total = result.Total;
args.AggregateResults = result.AggregateResults;
}
public class Product
{
public int Id { get; set; }
[Required]
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public decimal? Price { get; set; }
public int Quantity { get; set; }
[Required]
public DateTime? ReleaseDate { get; set; }
public bool Discontinued { get; set; }
}
#region Data Service
public class ProductService
{
private List<Product> Items { get; set; } = new();
private int LastId { get; set; }
public async Task<int> Create(Product product)
{
await SimulateAsyncOperation();
product.Id = ++LastId;
Items.Insert(0, product);
return LastId;
}
public async Task<bool> Delete(Product product)
{
await SimulateAsyncOperation();
if (Items.Contains(product))
{
Items.Remove(product);
return true;
}
return false;
}
public async Task<List<Product>> Read()
{
await SimulateAsyncOperation();
return Items;
}
public async Task<DataSourceResult> Read(DataSourceRequest request)
{
return await Items.ToDataSourceResultAsync(request);
}
public async Task<bool> Update(Product product)
{
await SimulateAsyncOperation();
int originalItemIndex = Items.FindIndex(x => x.Id == product.Id);
if (originalItemIndex != -1)
{
Items[originalItemIndex] = product;
return true;
}
return false;
}
private async Task SimulateAsyncOperation()
{
await Task.Delay(100);
}
public ProductService(int itemCount = 5)
{
Random rnd = Random.Shared;
for (int i = 1; i <= itemCount; i++)
{
Items.Add(new Product()
{
Id = ++LastId,
Name = $"Product {LastId}",
Description = $"Multi-line\ndescription {LastId}",
Price = LastId % 2 == 0 ? null : rnd.Next(0, 100) * 1.23m,
Quantity = LastId % 2 == 0 ? 0 : rnd.Next(0, 3000),
ReleaseDate = DateTime.Today.AddDays(-rnd.Next(365, 3650)),
Discontinued = LastId % 2 == 0
});
}
}
}
#endregion Data Service
}