I have a Telerik Grid and EditForm side-by-side on a page. The EditForm displays the selected object upon clicking a grid row. Below is an excerpt of my custom form component.
@typeparam TEntity where TEntity : class, IEntity, new()
<div>
<TelerikGrid
@ref="GridRef"
Pageable="true"
Sortable="true"
Resizable="true"
ShowColumnMenu="true"
Reorderable="true"
PageSize="50"
SelectionMode="@GridSelectionMode.Single"
SelectedItems="@_selectedItems"
SelectedItemsChanged="@((IEnumerable<TEntity> list) => OnGridSelectedItemsChanged(list))"
OnRowClick="@OnGridRowClick"
OnRead="GetGridData"
EnableLoaderContainer="true">
@* toolbar, columns, etc. *@
</TelerikGrid>
<EditForm EditContext="@_editContext" OnSubmit="OnSubmitAsync">
<TelerikDateTimePicker Placeholder=" "
Value="(DateTime?)PropertyValue"
ValueExpression="ValueExpression<DateTime?>()"
ValueChanged="(DateTime? val) => OnValueChanged(val)"
Readonly="true">
</TelerikDateTimePicker>
</EditForm>
</div>
@code {
private EditContext? _editContext;
private IEnumerable<TEntity> _selectedItems = Enumerable.Empty<TEntity>();
private TEntity? _selectedItem;
[CascadingParameter] public TEntity? Entity { get; set; }
protected override void OnInitialized()
{
_editContext = new(Entity);
_editContext.OnFieldChanged += OnFieldChanged!;
}
// Override the grid's built-in row selection by handling the SelectedItemsChanged event. Do not execute any logic in it to let the OnGridRowClick handle the selection.
// https://docs.telerik.com/blazor-ui/knowledge-base/grid-select-or-deselect-item-on-row-click
private void OnGridSelectedItemsChanged(IEnumerable<TEntity> selectedList)
{
}
private async Task OnGridRowClick(GridRowClickEventArgs args)
{
var currItem = args.Item as TEntity;
if (currItem?.Id == _selectedItem?.Id) return;
var discardChanges = await ConfirmDiscardFormChangesAsync();
if (!discardChanges) return;
_selectedItems = new[] { currItem };
_selectedItem = _selectedItems.First();
}
private async Task OnValueChanged(object? value)
{
// not getting fired when grid's selected item is changed, which is good
}
private void OnFieldChanged(object sender, FieldChangedEventArgs args)
{
// gets fired when grid's selected item is changed
// _editContext.IsModified() is true here
}
}
Whenever the form displays an item upon clicking a different row with DateTime field value different from the previously selected one, the EditContext's IsModified() becomes true. This is not the case when I replace the TelerikDateTimePicker with Blazor's InputDate component.
I attempted to develop a versatile grid component for Blazor to facilitate reusability.
@typeparam TItem
<TelerikGrid Data="Data" SelectionMode="GridSelectionMode.Multiple"
Pageable="true" PageSize="3" Page="1"
Sortable="true" SortMode="@SortMode.Multiple"
FilterMode="GridFilterMode.FilterMenu"
EditMode="@GridEditMode.Popup"
Resizable="true" Reorderable="true" ConfirmDelete="true">
<GridSettings>
<GridValidationSettings Enabled="true"></GridValidationSettings>
<GridPopupEditSettings MaxWidth="600px"
MaxHeight="300px"
Class="custom-popup"
Title="Update Details">
</GridPopupEditSettings>
<GridPopupEditFormSettings Orientation="@FormOrientation.Horizontal"
ButtonsLayout="FormButtonsLayout.Center"
Columns="3">
</GridPopupEditFormSettings>
</GridSettings>
<GridToolBarTemplate>
<GridCommandButton Command="ExcelExport" Icon="@SvgIcon.FileCsv">Export to Excel</GridCommandButton>
<GridCommandButton Command="Add" Icon="@SvgIcon.Plus">Add Employee</GridCommandButton>
</GridToolBarTemplate>
<GridExport>
<GridExcelExport FileName="EmployeeDetails Sheet" AllPages="true" />
</GridExport>
<GridColumns>
@GridCols
<GridCommandColumn>
<GridCommandButton Command="Save" Icon="@SvgIcon.Save" ShowInEdit="true">Update</GridCommandButton>
<GridCommandButton Command="Edit" Icon="@SvgIcon.Pencil">Edit</GridCommandButton>
<GridCommandButton Command="Delete" Icon="@SvgIcon.Trash">Delete</GridCommandButton>
<GridCommandButton Command="Cancel" Icon="@SvgIcon.Cancel" ShowInEdit="true">Cancel</GridCommandButton>
</GridCommandColumn>
</GridColumns>
</TelerikGrid>
@code
{
[Parameter]
public IEnumerable<TItem>? Data{get; set;}
[Parameter]
public RenderFragment<TItem>? GridCols { get; set; }
}
@page "/emp"
@inject HttpClient Http
@using System.Text.Json;
<h1>Employee Details</h1>
@if (employees == null)
{
<p><em>Loading...</em></p>
}
else
{
<GenericGrid TItem="EmpDetails" Data="@employees">
<GridCols>
<GridColumn Field="@nameof(EmpDetails.Name)" Title="Employee Name" />
<GridColumn Field="@nameof(EmpDetails.Age)" Title="Age" />
<GridColumn Field="@nameof(EmpDetails.Place)" Title="Place" />
</GridCols>
</GenericGrid>
}
<TelerikGrid class="NewPairsGrid"
Data="@Pairs"
AutoGenerateColumns="false"
RowHeight="15"
Height="1000px"
Pageable="true"
PageSize="25"
Sortable="true"
>
<GridColumns>
<GridColumn Field="PairCreatedTimeStamp" Title="Token Age" width="75px">
<Template Context="dataItem">
@if (dataItem is EthPairTradeInfoVDto ethPairTradeInfoVDto)
{
var timestamp = CalculateElapsedTime(dataItem as EthPairTradeInfoVDto);
<span class="tooltip-target">@timestamp</span>
<TelerikTooltip TargetSelector=".tooltip-target" Width="auto" Height="auto" Position="@TooltipPosition.Right">
<Template Context="tooltipContext">
<span>
@RenderTooltipContent(ethPairTradeInfoVDto.PairCreatedTimeStamp)
</span>
</Template>
</TelerikTooltip>
</Template>
</GridColumn>
RenderFragment RenderTooltipContent(DateTime? pairCreatedTimeStamp) =>
builder =>
{
if (pairCreatedTimeStamp.HasValue)
{
builder.OpenElement(0, "span");
builder.AddContent(1, $"Pair Timestamp: {pairCreatedTimeStamp.Value}");
builder.CloseElement();
}
};
Hi,
is there a way to use css to change the telerik menu item border or color when user click on it. After user click on other menu item, the existing old menu item color will remove and the new selected menu item will change the color.
Hello,
I have followed the Blazor 8 Getting started instructions but am getting the error Error: Microsoft.JSInterop.JSException: Could not find 'TelerikBlazor.initMediaQuery'.
I have double checked that the <script src="_content/Telerik.UI.for.Blazor/js/telerik-blazor.js"></script> is in the <head> of the app.razor
i have double checked that the builder.Services.AddTelerikBlazor(); is in the program.cs
and I have the
<TelerikRootComponent>
@Body
</TelerikRootComponent>
in the root layout component MainLayout.razor.
This error causes my stylesheets not to load. Occasionally the page will load with the stylesheets, but if a clear the cache or do a CTRL-SHIFT-R, I will get the error and my styles will unload.
HALP!
Hello,
I'm working on a .NET8 Blazor webapp with some InteractiveAutoRenderMode. I'm trying to figure out how to code the HTML/Razor in a Blazor component to be able to display a pair of inputs and then bind a collection of a complex type.
I also want the possibility for the user to add more instances to the collection within the UI/component by being able to add more inputs (through a button click) before submitting the form.
I am not sure how to capture the properties inside the complex type collection for the FieldIdentifier instance usually helping with binding and validation? And then how would that work within a loop for the user to be able to add more instances to the collection?
FieldIdentifier.Create(() => NewProductOfferModel!.LimitedAvailabilities[0].LimitedAvailabilityStartTime)
The HTML for the pair of inputs looks like this:
ManageProductOffer.razor :
<div class="input-group">
<label for="@($"""idAvailabilityStartInput""")">
<input type="datetime-local" id="@($"""idAvailabilityStartInput""")" name="fAvailabilityStart"
step="any" min="@(TimeProvider.GetLocalNow().LocalDateTime.ToString(SortableDateTimePatternFormat))"
class="@(ApplyStyleForValidationState(FieldIdentifier.Create(() => NewProductOfferModel!.LimitedAvailabilities)))"
value="@(NewProductOfferModel!.LimitedAvailabilities[0].LimitedAvailabilityStartTime.ToString(SortableDateTimePatternFormat))"
@onchange=@(changeEventArgs => TryBindAndValidateField(
changeEventArgs: changeEventArgs,
fieldIdentifier: FieldIdentifier.Create(() => NewProductOfferModel!.LimitedAvailabilities[0].LimitedAvailabilityStartTime),
validationDelegate: () => LimitedAvailability.ValidateLimitedAvailability(NewProductOfferModel!.LimitedAvailabilities[0]))) />
</label>
<label for="@($"""idAvailabilityEndInput""")">
<input type="datetime-local" id="@($"""idAvailabilityEndInput""")" name="fAvailabilityEnd"
step="any" min="@(TimeProvider.GetLocalNow().LocalDateTime.ToString(SortableDateTimePatternFormat))"
class="@(ApplyStyleForValidationState(FieldIdentifier.Create(() => NewProductOfferModel!.LimitedAvailabilities)))"
value="@(NewProductOfferModel!.LimitedAvailabilities[0].LimitedAvailabilityEndTime.ToString(SortableDateTimePatternFormat))"
@onchange=@(changeEventArgs => TryBindAndValidateField(
changeEventArgs: changeEventArgs,
fieldIdentifier: FieldIdentifier.Create(() => NewProductOfferModel!.LimitedAvailabilities[0].LimitedAvailabilityEndTime),
validationDelegate: () => LimitedAvailability.ValidateLimitedAvailability(NewProductOfferModel!.LimitedAvailabilities[0]))) />
</label>
<small class="">
<ValidationMessage For="@(() => NewProductOfferModel!.LimitedAvailabilities[0])" />
</small>
</div>
And the code for the model with the complex type collection looks like this:
ProductOfferModel.cs :
public sealed record class ProductOfferModel
{
public Guid ID { get; init; } = Guid.NewGuid();
public String ProductOfferDescription { get; set; } = String.Empty;
public Guid ProductID { get; set; } = Guid.Empty;
public String ProductDescription { get; set; } = String.Empty;
public List<LimitedAvailability> LimitedAvailabilities { get; set; } = [new LimitedAvailability()];
}
public sealed record class LimitedAvailability
{
public Guid ID { get; init; } = Guid.NewGuid();
public DateTime LimitedAvailabilityStartTime { get; set; } = DateTime.MinValue;
public DateTime LimitedAvailabilityEndTime { get; set; } = DateTime.MinValue;
internal static Boolean ValidateLimitedAvailability( LimitedAvailability limitedAvailability )
{
ArgumentNullException.ThrowIfNull(limitedAvailability);
return limitedAvailability.LimitedAvailabilityStartTime < limitedAvailability.LimitedAvailabilityEndTime ?
true
: false;
}
}
The (microsoft documentation) mentions supporting complex type and be able to nest and bind, however it does not seems to mention how to capture the FieldIdentifier accessor for a property inside complex type to customize @onchange or @oninput behavior for example.
Anyone has been solving something similar with using collections of complex types and letting the user add more inputs to the form before submit?
Can you explain the differences between the Dialog vs the Window Component? And maybe give an example of when you should use one over the other?
IN their current form, they seem pretty similar in implementation and functionality if you put aside the predefined dialogs.
I have a button inside of a form that I need to prevent from submitting the form when clicked. How can I add the following to a TelerikButton?
@onclick:preventDefault="true"
@onclick:stopPropagation="true"
Hi guys,
I am fairly new to Blazor/Telerik but trying to get this to work. I introduced OnRead to my code so I can try to capture the grid row count. The problem is when WorkRecord is called it quits refreshing the grid. I have to F5 to see my updates.
Its almost as if OnRead gets in the middle of things and doesn't allow things to complete.
Any idea what I am doing wrong here?
@using Telerik.DataSource.Extensions
@inject DapperRepository<S4PSector4UpdateList> S4GridManager
@inject IJSRuntime js
@if (@S4Records != null && @cpar != null && @cpar.Initialized == true)
{
//FilterMode="@GridFilterMode.FilterRow"
<TelerikGrid
@ref="@GridRef"
OnUpdate=@UpdateItem
Data="@S4Records.ToArray()"
EnableLoaderContainer="true"
OnRead="@OnGridRead"
Pageable="true"
Sortable="true"
Resizable="true"
EditMode="@GridEditMode.Incell"
Size="@ThemeConstants.Grid.Size.Small"
SelectionMode="@GridSelectionMode.Single"
SelectedItems="@SelectedRecord"
ScrollMode="@GridScrollMode.Scrollable">
<GridToolBarTemplate>
<span class="k-toolbar-spacer"></span> @* add this spacer to keep the searchbox on the right *@
<input type="checkbox" id="vehicle1" name="vehicle1" value="Bike">
<label for="vehicle1">Not Started/Assigned</label> 
<input type="checkbox" id="vehicle1" name="vehicle1" value="Bike">
<label for="vehicle1">In Progress</label> 
<input type="checkbox" id="vehicle1" name="vehicle1" value="Bike">
<label for="vehicle1">Update Complete</label> 
<input type="checkbox" id="vehicle1" name="vehicle1" value="Bike">
<label for="vehicle1">Show Only My Records</label> 
<select name="cars" id="cars">
<option value="volvo">100</option>
<option value="saab">50</option>
<option value="opel">20</option>
<option value="audi">10</option>
</select>
<button >Assign Records</button>
<GridCommandButton Command="ExcelExport" Icon="@FontIcon.FileExcel">Export to Excel</GridCommandButton>
<GridSearchBox Width="200px"/>
</GridToolBarTemplate>
<GridColumns>
<GridColumn Field="@nameof(@S4PSector4UpdateList.geid)" Width="75px" Title="GEID" Editable="false" HeaderClass="center-me"/>
<GridColumn Field="@nameof(@S4PSector4UpdateList.soeid)" Width="55px" Title="SOEID" Editable="false" HeaderClass="center-me"/>
<GridCommandColumn Width="45px">
<GridCommandButton Command="WorkRecord" OnClick="@WorkRecord" Size="@ThemeConstants.Button.Size.Small">Work</GridCommandButton>
</GridCommandColumn>
<GridColumn Field="@nameof(S4PSector4UpdateList.primaryfullname)" Width="175px" Title="Full Name" Editable="false" HeaderClass="center-me"/>
<GridColumn Field="@nameof(S4PSector4UpdateList.process_name)" Width="80px" Title="Process Name" Editable="false" HeaderClass="center-me"/>
<GridColumn Field="@nameof(S4PSector4UpdateList.worked_status)" Width="75px" Title="Status" Editable="false" HeaderClass="center-me"/>
<GridColumn Field="@nameof(S4PSector4UpdateList.worked_by_user_soeid)" Width="75px" Title="User SOEID" Editable="false" HeaderClass="center-me"/>
<GridColumn Field="@nameof(S4PSector4UpdateList.worked_by_user_date)" DisplayFormat="{0:MM/dd/yyyy}" Title="Worked Date" Width="85px" Editable="false" HeaderClass="center-me"/>
<GridColumn Field="@nameof(S4PSector4UpdateList.insertdate)" DisplayFormat="{0:MM/dd/yyyy}" Title="Insert Date" Width="85px" Editable="false" HeaderClass="center-me"/>
<GridColumn Field="@nameof(S4PSector4UpdateList.note)" Title="Note" Width="150px" HeaderClass="center-me"/>
<GridColumn Field="@nameof(S4PSector4UpdateList.note_added_by)" Width="80px" Title="Added By" Editable="false" HeaderClass="center-me"/>
<GridColumn Field="@nameof(S4PSector4UpdateList.note_added_date)" DisplayFormat="{0:MM/dd/yyyy}" Title="Note Date" Width="85px" Editable="false" HeaderClass="center-me"/>
<GridColumn Field="@nameof(S4PSector4UpdateList.insertdate)" DisplayFormat="{0:MM/dd/yyyy}" Title="Insert Date" Width="85px" Editable="false" HeaderClass="center-me" />
<GridColumn Field="@nameof(S4PSector4UpdateList.sector_4_eligible_group)" Width="130px" Title="Sector 4 Eligible Group" Editable="false" HeaderClass="center-me"/>
</GridColumns>
</TelerikGrid>
}
@* 👇 Not Visible Components *@
<DisplayAndLogErrorComponent @ref="displayandlogcomponent"></DisplayAndLogErrorComponent>
@code
{
[CascadingParameter(Name = "S4pars")] S4CascadingParameters cpar { get; set; }
[Parameter] public Action Refresh { get; set; }
List<S4PSector4UpdateList> S4Records { get; set; }
public TelerikGrid<S4PSector4UpdateList> GridRef { get; set; }
private DisplayAndLogErrorComponent displayandlogcomponent;
//public S4PSector4UpdateList SelectedRecord { get; set; }
public IEnumerable<S4PSector4UpdateList> SelectedRecord { get; set; } = Enumerable.Empty<S4PSector4UpdateList>();
public int recordcount;
private async Task OnGridRead(GridReadEventArgs args)
{
var result = S4Records.ToDataSourceResult(args.Request);
args.Data = result.Data;
args.Total = result.Total;
recordcount = result.Total;
}
protected override async Task OnInitializedAsync()
{
try
{
if (cpar.Initialized == true)
{
goto finish;
}
cpar.Initialized = true;
goto finish;
finish:;
await LoadAllRecords();
}
catch (Exception ex)
{
displayandlogcomponent.DisplayAndLogError(1, ex, cpar, MethodBase.GetCurrentMethod().DeclaringType.FullName);
}
finally
{
StateHasChanged();
Refresh?.Invoke();
}
}
async Task LoadAllRecords()
{
try
{
await Task.Delay(0);
var result = await S4GridManager.GetAsync("Select * from wta_application.fn_s4_get_active_records() order by id;");
S4Records = result.ToList();
}
catch (Exception ex)
{
displayandlogcomponent.DisplayAndLogError(1, ex, cpar, MethodBase.GetCurrentMethod().DeclaringType.FullName);
}
finally
{
StateHasChanged();
Refresh?.Invoke();
}
}
async Task UpdateItem(GridCommandEventArgs args)
{
try
{
S4PSector4UpdateList item = (S4PSector4UpdateList)args.Item;
var updated = await S4GridManager.UpdateAsync(item);
await LoadAllRecords();
cpar.message = new MessagePars(1, "Record updated successfully!", true);
}
catch (Exception ex)
{
displayandlogcomponent.DisplayAndLogError(1, ex, cpar, MethodBase.GetCurrentMethod().DeclaringType.FullName);
}
finally
{
StateHasChanged();
Refresh?.Invoke();
}
}
private async Task WorkRecord(GridCommandEventArgs args)
{
try
{
var model = args.Item as S4PSector4UpdateList;
//Update Table with user soeid and in progress.
var dictionary = new Dictionary<string, object>
{
{ "@in_soeid", cpar.authuserinfo.soeid },
{ "@in_id", model.id }
};
var parameters = new DynamicParameters(dictionary);
S4GridManager.ExecuteStoredProcedure("wta_application.sp_s4_work_record", parameters);
//Update Count Here
//Copy to clipboard, place success message and select row.
await js.InvokeVoidAsync("navigator.clipboard.writeText", model.soeid.Trim());
cpar.message = new MessagePars(1, model.soeid + " copied to clipboard.", true);
await LoadAllRecords();
//Highlight the row on the grid they selected
SelectedRecord = GridRef.Data.Where(x => x.id == model.id).ToList();
}
catch (Exception ex)
{
displayandlogcomponent.DisplayAndLogError(1, ex, cpar, MethodBase.GetCurrentMethod().DeclaringType.FullName);
}
finally
{
StateHasChanged();
Refresh?.Invoke();
}
}
public void Refreshed()
{
StateHasChanged();
}
}