If a new item for a grid is created and the CreateHandler is cancelled due to some validation error, the new loading indicator does not disappear.
protected async void CreateHandler(GridCommandEventArgs args)
{
var myItem = (MyModel) args.Item;
if (!IsValid(myItem))
{
args.IsCancelled = true;
return;
}
...
}
Regards,
René
If you set args.IsCancelled=true in any CUD event handler, the loading sign will never go away.
---
ADMIN EDIT
If you don't need to cancel the event (e.g., because remote validation or data operation failed), you should avoid cancelling it - its purpose is to keep the grid in the current mode (edit/insert) when the operation fails so the user does not lose data.
That said, the loading sing should disappear in thsoe cases too, and a workaround is to disable it by setting the EnableLoaderContainer="false" parameter of the grid.
---
I am getting the following error with them *only* when loading my site on an iPhone, after the page had been previously loaded on a PC.
2020-11-02T16:19:51.547Z] Error: Microsoft.JSInterop.JSException: An exception occurred executing JS interop: The JSON value could not be converted to System.Double. Path: $ | LineNumber: 0 | BytePositionInLine: 4.. See InnerException for more details.
---> System.Text.Json.JsonException: The JSON value could not be converted to System.Double. Path: $ | LineNumber: 0 | BytePositionInLine: 4.
---> System.InvalidOperationException: Cannot get the value of a token type 'Null' as a number.
at System.Text.Json.Utf8JsonReader.TryGetDouble(Double& value)
at System.Text.Json.Utf8JsonReader.GetDouble()
at System.Text.Json.Serialization.Converters.JsonConverterDouble.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
at System.Text.Json.JsonPropertyInfoNotNullable`4.OnRead(ReadStack& state, Utf8JsonReader& reader)
at System.Text.Json.JsonSerializer.HandleNull(Utf8JsonReader& reader, ReadStack& state)
at System.Text.Json.JsonSerializer.ReadCore(JsonSerializerOptions options, Utf8JsonReader& reader, ReadStack& readStack)
--- End of inner exception stack trace ---
at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& readStack, Utf8JsonReader& reader, Exception ex)
at System.Text.Json.JsonSerializer.ReadCore(JsonSerializerOptions options, Utf8JsonReader& reader, ReadStack& readStack)
at System.Text.Json.JsonSerializer.ReadValueCore(JsonSerializerOptions options, Utf8JsonReader& reader, ReadStack& readStack)
at System.Text.Json.JsonSerializer.ReadValueCore(Utf8JsonReader& reader, Type returnType, JsonSerializerOptions options)
at System.Text.Json.JsonSerializer.Deserialize(Utf8JsonReader& reader, Type returnType, JsonSerializerOptions options)
at Microsoft.JSInterop.JSRuntime.EndInvokeJS(Int64 taskId, Boolean succeeded, Utf8JsonReader& jsonReader)
--- End of inner exception stack trace ---
at Microsoft.JSInterop.JSRuntime.InvokeWithDefaultCancellation[T](String identifier, Object[] args)
at Telerik.Blazor.Components.TelerikTooltip.OnAfterRenderAsync(Boolean firstRender)
Using the keyboard to save items works inconsistently. When editing an item, hitting enter seems to save the change as expected. When inserting an item, however, the item is lost.
---
ADMIN EDIT
Tthe grid calls the OnUpdate handler again, not OnCreate which results in data loss - the newly inserted item is unlikely to match existing data and so it appears to be lost.
Workaround - call the OnCreate handler yourself when the ID of the record is the default for its type - for example, zero for an integer. This indicates a newly inserted record in the grid.
<TelerikGrid Data=@MyData EditMode="@GridEditMode.Inline" Pageable="true" Height="500px" Navigable="true"
OnUpdate="@UpdateHandler" OnDelete="@DeleteHandler" OnCreate="@CreateHandler" OnCancel="@CancelHandler">
<GridToolBar>
<GridCommandButton Command="Add" Icon="add">Add Employee</GridCommandButton>
</GridToolBar>
<GridColumns>
<GridColumn Field=@nameof(SampleData.ID) Title="ID" Editable="false" />
<GridColumn Field=@nameof(SampleData.Name) Title="Name" />
<GridCommandColumn>
<GridCommandButton Command="Save" Icon="save" ShowInEdit="true">Update</GridCommandButton>
<GridCommandButton Command="Edit" Icon="edit">Edit</GridCommandButton>
<GridCommandButton Command="Delete" Icon="delete">Delete</GridCommandButton>
<GridCommandButton Command="Cancel" Icon="cancel" ShowInEdit="true">Cancel</GridCommandButton>
</GridCommandColumn>
</GridColumns>
</TelerikGrid>
@code {
async Task UpdateHandler(GridCommandEventArgs args)
{
SampleData item = (SampleData)args.Item;
if (item.ID == 0)
{
await CreateHandler(args);
}
else
{
// perform actual data source operations here through your service
await MyService.Update(item);
// update the local view-model data with the service data
await GetGridData();
Console.WriteLine("Update event is fired.");
}
}
async Task DeleteHandler(GridCommandEventArgs args)
{
SampleData item = (SampleData)args.Item;
// perform actual data source operation here through your service
await MyService.Delete(item);
// update the local view-model data with the service data
await GetGridData();
Console.WriteLine("Delete event is fired.");
}
async Task CreateHandler(GridCommandEventArgs args)
{
SampleData item = (SampleData)args.Item;
// perform actual data source operation here through your service
await MyService.Create(item);
// update the local view-model data with the service data
await GetGridData();
Console.WriteLine("Create event is fired.");
}
async Task CancelHandler(GridCommandEventArgs args)
{
SampleData item = (SampleData)args.Item;
// if necessary, perform actual data source operation here through your service
Console.WriteLine("Cancel event is fired.");
}
// in a real case, keep the models in dedicated locations, this is just an easy to copy and see example
public class SampleData
{
public int ID { get; set; }
public string Name { get; set; }
}
public List<SampleData> MyData { get; set; }
async Task GetGridData()
{
MyData = await MyService.Read();
}
protected override async Task OnInitializedAsync()
{
await GetGridData();
}
// the following static class mimics an actual data service that handles the actual data source
// replace it with your actual service through the DI, this only mimics how the API can look like and works for this standalone page
public static class MyService
{
private static List<SampleData> _data { get; set; } = new List<SampleData>();
public static async Task Create(SampleData itemToInsert)
{
itemToInsert.ID = _data.Count + 1;
_data.Insert(0, itemToInsert);
}
public static async Task<List<SampleData>> Read()
{
if (_data.Count < 1)
{
for (int i = 1; i < 50; i++)
{
_data.Add(new SampleData()
{
ID = i,
Name = "Name " + i.ToString()
});
}
}
return await Task.FromResult(_data);
}
public static async Task Update(SampleData itemToUpdate)
{
var index = _data.FindIndex(i => i.ID == itemToUpdate.ID);
if (index != -1)
{
_data[index] = itemToUpdate;
}
}
public static async Task Delete(SampleData itemToDelete)
{
_data.Remove(itemToDelete);
}
}
}
---
Our Site supports multiple themes that we have built using the SASS Theme Builder. For all Telerik Components we have used so far the theme changes on them as expected when using the example found here (Change Theme at Runtime). The problem seems to be the chart component will not adhere to the newly applied theme after its initial load. Seems like this is because it is shown as an SVG with hardcoded attributes on the element. Is there a workaround for this (other than reloading the components entirely)?
Before & After Theme Change:
If the "Navigable" parameter of the Grid is set to false (its default state) and the EditMode is PopUp, there is no focus on the first input in Add/Edit mode.
If in a grid columns are bound to integer values which shall be treated as enum values it can be done with the FieldType parameter.
Unfortunately the new FilterMenuType.CheckBoxList does not work as expected then.
With
<GridColumn Field="@(nameof(MyModel.MyPropertyId))"
FieldType="@(typeof(MyPropertyEnum))"
Title="MyProperty"
FilterMenuType="@FilterMenuType.CheckBoxList">
the filter shows Integers instead of Enum-Strings.
(With FilterMenuType.Menu the filter correctly treats the values as enums)
Regards,
René
The orderby clause only has one column when multicolumn sorting is enabled, you can test it with this sample.
---
ADMIN EDIT
A workaround is to replace the orderby generated by the grid with your own clause that uses the DataSourceRequest to extract all sort descriptors, here's an example:
@using System.Net.Http.Json
@using System.Text.RegularExpressions
@using Telerik.Blazor.Extensions
@using WasmApp.Shared
@inject HttpClient Http
<TelerikGrid Data=@GridData
Height="460px"
RowHeight="60"
PageSize="10"
Pageable="true"
Sortable="true"
FilterMode="@GridFilterMode.FilterRow"
SortMode="@SortMode.Multiple"
OnRead=@ReadItems
TotalCount=@Total>
<GridColumns>
<GridColumn Field="ProductID" />
<GridColumn Field="ProductName" />
<GridColumn Field="Discontinued" />
</GridColumns>
</TelerikGrid>
@code{
public List<ODataProduct> GridData { get; set; } = new List<ODataProduct>();
public int Total { get; set; } = 0;
protected async Task ReadItems(GridReadEventArgs args)
{
var baseUrl = "https://demos.telerik.com/kendo-ui/service-v4/odata/Products?";
string OdataUrl = args.Request.ToODataString();
// replace the original orederby clause with one that contains all the order rules
Regex x = new Regex("(orderby=)(.*?)(&)", RegexOptions.IgnoreCase);
string actualOrderByClause = "orderby=";
for (int i = 0; i < args.Request.Sorts.Count; i++)
{
if (i > 0)
{
actualOrderByClause += ",";
}
string order = args.Request.Sorts[i].SortDirection == Telerik.DataSource.ListSortDirection.Ascending ? "" : "%20desc";
actualOrderByClause += $"{args.Request.Sorts[i].Member}{order}";
}
actualOrderByClause += "&";
string OdataQueryWithMultipleOrder = x.Replace(OdataUrl, actualOrderByClause);
//do the request as usual
var requestUrl = $"{baseUrl}{OdataQueryWithMultipleOrder}";
ODataResponseOrders response = await Http.GetFromJsonAsync<ODataResponseOrders>(requestUrl);
GridData = response.Products;
Total = response.Total;
}
}
---
Hover the mouse several times in a row at element c on the demo stand. OS Windows 10, Browsers: Chrome Version 87.0.4280.141 (Official Build) (64-bit) + Firefox Developer 85.0b9 (64-bit).
Tell me why the component periodically has 2 tooltips? This anomaly is also observed on your demo stand.
---
ADMIN EDIT
Here is a workaround - using a bit of JS to clear the checked state of the checkbox element when you clear the SelectedItems collection:
@inject IJSRuntime _js
@*You should move this to a proper place for scripts,
suppressing the error like this is just a hack to keep this sample code concise*@
<script suppress-error="BL9992">
function uncheckGrid(parentElem) {
var checkboxes = parentElem.querySelectorAll(".k-checkbox.k-grid-checkbox");
for (var i = 0; i < checkboxes.length; i++) {
checkboxes[i].checked = false;
}
}
</script>
<div @ref="@ParentElem">
<TelerikGrid Data=@GridData
SelectionMode="@GridSelectionMode.Multiple"
SelectedItemsChanged="@((IEnumerable<Employee> employeeList) => OnSelect(employeeList))"
SelectedItems="@SelectedEmployees"
Pageable="true"
Height="400px">
<GridColumns>
<GridCheckboxColumn />
<GridColumn Field=@nameof(Employee.Name) />
<GridColumn Field=@nameof(Employee.Team) Title="Team" />
</GridColumns>
</TelerikGrid>
</div>
@if (SelectedEmployees != null)
{
<ul>
@foreach (Employee employee in SelectedEmployees)
{
<li>
@employee.Name
</li>
}
</ul>
}
@code {
ElementReference ParentElem { get; set; }
public List<Employee> GridData { get; set; }
public IEnumerable<Employee> SelectedEmployees { get; set; } = Enumerable.Empty<Employee>();
protected override void OnInitialized()
{
GridData = new List<Employee>();
for (int i = 0; i < 15; i++)
{
GridData.Add(new Employee()
{
EmployeeId = i,
Name = "Employee " + i.ToString(),
Team = "Team " + i % 3
});
}
}
protected void OnSelect(IEnumerable<Employee> employees)
{
bool flag = true;
foreach (Employee item in employees)
{
if(item.EmployeeId % 3 == 0)
{
flag = false;
}
}
if (flag)
{
SelectedEmployees = employees;
}
else
{
SelectedEmployees = Enumerable.Empty<Employee>();
_js.InvokeVoidAsync("uncheckGrid", ParentElem);
}
}
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Team { get; set; }
}
}
---
Hi Telerik team,
In a Blazor Grid with filters enabled the event "OnStateChanged" is fired twice when I use a filter (set, change, remove).
---
ADMIN EDIT
This behavior is expected - when the grid is filtered, there are two actions that happen:
This is not something we intend to change at this point.
---
To reproduce this I took one of the provided examples and added the event handler:
@page "/"
<TelerikGrid Data=@GridData
SelectionMode="GridSelectionMode.Multiple"
SelectedItemsChanged="@((IEnumerable<Employee> employeeList) => OnSelect(employeeList))"
SelectedItems="@PersistedSelectedItems"
@bind-Page="@CurrentPage"
PageSize="@PageSize"
Pageable="true"
FilterMode="GridFilterMode.FilterRow"
OnStateChanged="@((GridStateEventArgs<Employee> args) => OnStateChangedHandler(args))">
<GridColumns>
<GridCheckboxColumn />
<GridColumn Field=@nameof(Employee.EmployeeId) />
<GridColumn Field=@nameof(Employee.Name) />
<GridColumn Field=@nameof(Employee.Team) />
</GridColumns>
</TelerikGrid>
@if (PersistedSelectedItems != null)
{
<ul>
@foreach (Employee employee in PersistedSelectedItems.OrderBy(e => e.EmployeeId))
{
<li>
@employee.EmployeeId
</li>
}
</ul>
}
@code {
public List<Employee> PersistedSelectedItems { get; set; } = new List<Employee>();
int CurrentPage { get; set; }
int PageSize { get; set; } = 5;
private async void OnStateChangedHandler(GridStateEventArgs<Employee> args)
{
await Task.Delay(5000);
}
protected void OnSelect(IEnumerable<Employee> employees)
{
IEnumerable<Employee> CurrentPageEmployees = GridData.Skip(PageSize * (CurrentPage - 1)).Take(PageSize);
if (employees == null || employees.Count() == 0)
{
//the user de-selected all items with the header checkbox
PersistedSelectedItems = PersistedSelectedItems.Except(CurrentPageEmployees).ToList();
}
else
{
//handle any deselected items
var UnselectedEmployees = CurrentPageEmployees.Except(employees);
PersistedSelectedItems = PersistedSelectedItems.Except(UnselectedEmployees).ToList();
//add any new items if they were not selected already
foreach (var item in employees)
{
if (!PersistedSelectedItems.Contains(item))
{
PersistedSelectedItems.Add(item);
}
}
}
}
//data binding and sample data
public List<Employee> GridData { get; set; }
protected override void OnInitialized()
{
GridData = new List<Employee>();
for (int i = 0; i < 15; i++)
{
GridData.Add(new Employee()
{
EmployeeId = i,
Name = "Employee " + i.ToString(),
Team = "Team " + i % 3
});
}
}
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Team { get; set; }
}
}
Best regards,
Rayko
Use any demo and rapidly hover between parent menu items:
I need to make a tooltip more accessible.
---
ADMIN EDIT
The first thing you can do is use the aria-describedby on the tooltip targets and use its value in the tooltip Id parameter to help screen readers. It is up to the screen reader to detect the change in the DOM and read out the new content.
This item is to add attributes to the tooltip such as role="tooltip", aria-live="polite", role="presentation", aria-hidden="false". The exact attributes will depend on more detailed research and may differ from what I have listed here.
---
Hi Team,
We tried to use the TelerikLoader in Blazor WASM app, but the loader is not appearing on the page. We tried the basic example given in the Demo but did not work.
We are using latest Telerik Version (2.20.0).
@if (IsLoading)
{
<TelerikLoader />
}
else
{
@Data
}
We have below references in index.html
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>THOR.UI</title>
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
<link href="css/toggle.css" rel="stylesheet" />
<link href="css/spinner.css" rel="stylesheet" />
<script src="_content/Telerik.UI.for.Blazor/js/telerik-blazor.js"></script>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet">
<link id="theme" rel="stylesheet" href="./css/thor-light-theme-material.css" />
<link href="_content/Blazored.Toast/blazored-toast.min.css" rel="stylesheet" />
<script src="./site.js"></script>
<script src="_content/BlazorInputFile/inputfile.js"></script>
<script src="_content/BlazorPro.BlazorSize/blazorSize.min.js"></script>
<script src="_content/Fluxor.Blazor.Web/scripts/index.js"></script>
<script src="./scripts/download.js"></script>
</head>
I tried to follow this below article and add the reference as below and CDN also but it did not work.
<!DOCTYPE html>
<html>
<head>
. . .
<link href="/css/kendo-themes/default/dist/all.css" rel="stylesheet"/>
<!-- Choose only one of the themes -->
<!-- <link href="/css/kendo-themes/bootstrap/dist/all.css" rel="stylesheet" />
<link href="/css/kendo-themes/material/dist/all.css" rel="stylesheet" /> -->
</head>
Please let us know what changes we have to make this to work.
Thanks
Chandra Vanama