Error:
blazor.server.js:19 [2021-02-03T06:17:43.996Z] Error: System.NullReferenceException: Object reference not set to an instance of an object.
Example:
private string SerializedState; private void OnStateInitHandler(GridStateEventArgs<SampleData> args) { args.GridState = JsonSerializer.Deserialize<GridState<SampleData>>(SerializedState); }
Reason:
FilterDescriptors property MemberType = null.
Note:
Method TelerikGrid.SetState() works correctly.
Indeed, that's a valid approach for a workaround. I just thought that using OnAfterRenderAsync is a bit easier and requires less code.
Regards,
Marin Bratanov
Progress Telerik
Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.
Hello,
Thank you for the details.
Here is what happens:
So, the grid should not throw this exception and, ideally, the filter should keep working in this scenario.
That said, I can offer two workarounds - the first is a tad more generic, the second might help for this particular case so that you can avoid loading state unless it is necessary.
Idea 1: Load the state in the AfterRender event. Caveat - there may be a second data request if you use the OnRead event.
@using System.Text.Json
<TelerikButton OnClick="SaveState">Save state</TelerikButton>
<TelerikButton OnClick="LoadState">Load state</TelerikButton>
<TelerikButton OnClick="SetCustomState">Set Custom state</TelerikButton>
<TelerikGrid @ref="Grid"
Data="Data"
FilterMode="GridFilterMode.FilterMenu"
TItem="SampleData">
<GridColumns>
<GridColumn Title="Name" Field="@nameof(SampleData.Name)" />
</GridColumns>
</TelerikGrid>
@SerializedState
@code {
private TelerikGrid<SampleData> Grid;
private IEnumerable<SampleData> Data;
private string SerializedState;
protected override void OnInitialized()
{
if (string.IsNullOrEmpty(SerializedState))
{
SerializedState = @"
{
""GroupDescriptors"": [],
""FilterDescriptors"": [
{
""LogicalOperator"": 0,
""FilterDescriptors"": [
{
""Member"": ""Name"",
""Operator"": 8,
""Value"": null
},
{
""Member"": ""Name"",
""Operator"": 8,
""Value"": null
}
]
}
],
""SortDescriptors"": [],
""Page"": 1,
""Skip"": 0,
""CollapsedGroups"": [],
""ColumnStates"": [
{
""Index"": 0,
""Width"": null,
""Visible"": null,
""Locked"": false
}
],
""ExpandedRows"": [],
""SelectedItems"": [],
""OriginalEditItem"": null,
""EditItem"": null,
""EditField"": null,
""InsertedItem"": null,
""TableWidth"": null
}";
}
Data = Enumerable.Empty<SampleData>();
//Data = Enumerable.Range(1, 15).Select(x => new SampleData { Name = $"name {x}" }).ToList();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await LoadState();
}
}
private async void SaveState()
{
var state = Grid.GetState();
SerializedState = JsonSerializer.Serialize(state);
}
private async Task LoadState()
{
if (!string.IsNullOrEmpty(SerializedState))
{
var state = JsonSerializer.Deserialize<GridState<SampleData>>(SerializedState);
if (state != null)
{
await Grid.SetState(state);
}
}
}
private async Task SetCustomState()
{
string customSerializedState = @"{""roupDescriptors"":[],""FilterDescriptors"":[{""LogicalOperator"":0,""FilterDescriptors"":[{""Member"":""Name"",""Operator"":8,""Value"":""1""},{""Member"":""Name"",""Operator"":8,""Value"":""2""}]}],""SortDescriptors"":[],""Page"":1,""Skip"":0,""CollapsedGroups"":[],""ColumnStates"":[{""Index"":0,""Width"":null,""Visible"":null,""Locked"":false}],""ExpandedRows"":[],""SelectedItems"":[],""OriginalEditItem"":null,""EditItem"":null,""EditField"":"""",""InsertedItem"":null,""TableWidth"":null}";
SerializedState = customSerializedState;
var state = JsonSerializer.Deserialize<GridState<SampleData>>(customSerializedState);
if (state != null)
{
await Grid.SetState(state);
}
}
private class SampleData
{
public int Id { get; set; }
public string Name { get; set; }
// example of comparing stored items (from editing or selection)
// with items from the current data source - IDs are used instead of the default references
public override bool Equals(object obj)
{
if (obj is SampleData)
{
return this.Id == (obj as SampleData).Id;
}
return false;
}
}
}
Idea 2: Try to load state into the grid when it actually makes sense to load new information. The provided state object is, effectively, empty, and you could avoid saving that in the first place. If you want to save the state through an explicit user action such as a button click, you can at least raise a flag in the OnStateChanged event to store only a state that has changed. Here is an example of that:
@using System.Text.Json
<TelerikButton OnClick="SaveState">Save state</TelerikButton>
<TelerikButton OnClick="LoadState">Load state</TelerikButton>
<TelerikButton OnClick="SetCustomState">Set Custom state</TelerikButton>
<br />State has changed so it makse sense to save it: @HasStateChanged
<TelerikGrid @ref="Grid"
Data="Data"
OnStateInit="@((GridStateEventArgs<SampleData> args) => OnGridStateInit(args))"
OnStateChanged="@((GridStateEventArgs<SampleData> args) => HasStateChanged = true)"
FilterMode="GridFilterMode.FilterMenu"
TItem="SampleData">
<GridColumns>
<GridColumn Title="Name" Field="@nameof(SampleData.Name)" />
</GridColumns>
</TelerikGrid>
@SerializedState
@code {
string UniqueStorageKey = "TestGridKey";
private TelerikGrid<SampleData> Grid;
private IEnumerable<SampleData> Data;
bool HasStateChanged { get; set; }
private string SerializedState; // this is null when there is no state to actually save - see the bool flag above it
protected override void OnInitialized()
{
Data = Enumerable.Empty<SampleData>();
//Data = Enumerable.Range(1, 15).Select(x => new SampleData { Name = $"name {x}" }).ToList();
}
private async Task OnGridStateInit(GridStateEventArgs<SampleData> args)
{
if (!string.IsNullOrEmpty(SerializedState))
{
var state = JsonSerializer.Deserialize<GridState<SampleData>>(SerializedState);
if (state != null)
{
args.GridState = state;
}
}
}
private async void SaveState()
{
if (HasStateChanged)
{
var state = Grid.GetState();
SerializedState = JsonSerializer.Serialize(state);
}
}
private async Task LoadState()
{
if (!string.IsNullOrEmpty(SerializedState))
{
var state = JsonSerializer.Deserialize<GridState<SampleData>>(SerializedState);
if (state != null)
{
await Grid.SetState(state);
}
}
}
private async Task SetCustomState()
{
string customSerializedState = @"{""roupDescriptors"":[],""FilterDescriptors"":[{""LogicalOperator"":0,""FilterDescriptors"":[{""Member"":""Name"",""Operator"":8,""Value"":""1""},{""Member"":""Name"",""Operator"":8,""Value"":""2""}]}],""SortDescriptors"":[],""Page"":1,""Skip"":0,""CollapsedGroups"":[],""ColumnStates"":[{""Index"":0,""Width"":null,""Visible"":null,""Locked"":false}],""ExpandedRows"":[],""SelectedItems"":[],""OriginalEditItem"":null,""EditItem"":null,""EditField"":"""",""InsertedItem"":null,""TableWidth"":null}";
var state = JsonSerializer.Deserialize<GridState<SampleData>>(customSerializedState);
if (state != null)
{
await Grid.SetState(state);
}
}
private class SampleData
{
public int Id { get; set; }
public string Name { get; set; }
// example of comparing stored items (from editing or selection)
// with items from the current data source - IDs are used instead of the default references
public override bool Equals(object obj)
{
if (obj is SampleData)
{
return this.Id == (obj as SampleData).Id;
}
return false;
}
}
}
Regards,
Marin Bratanov
Progress Telerik
Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.
Hello,
Clarify:
Extended example:
@page "/"
@using System.Text.Json
<TelerikButton OnClick="SaveState">Save state</TelerikButton>
<TelerikButton OnClick="LoadState">Load state</TelerikButton>
<TelerikGrid @ref="Grid"
Data="Data"
OnStateInit="OnGridStateInit"
FilterMode="GridFilterMode.FilterMenu"
TItem="SampleData">
<GridColumns>
<GridColumn Title="Name" Field="@nameof(SampleData.Name)" />
</GridColumns>
</TelerikGrid>
@code {
private TelerikGrid<SampleData> Grid;
private IEnumerable<SampleData> Data;
private string SerializedState;
protected override void OnInitialized()
{
// load serialized state from DB, for example
SerializedState = @"
{
""GroupDescriptors"": [],
""FilterDescriptors"": [
{
""LogicalOperator"": 0,
""FilterDescriptors"": [
{
""Member"": ""Name"",
""Operator"": 8,
""Value"": null
},
{
""Member"": ""Name"",
""Operator"": 8,
""Value"": null
}
]
}
],
""SortDescriptors"": [],
""Page"": 1,
""Skip"": 0,
""CollapsedGroups"": [],
""ColumnStates"": [
{
""Index"": 0,
""Width"": null,
""Visible"": null,
""Locked"": false
}
],
""ExpandedRows"": [],
""SelectedItems"": [],
""OriginalEditItem"": null,
""EditItem"": null,
""EditField"": null,
""InsertedItem"": null,
""TableWidth"": null
}";
Data = Enumerable.Empty<SampleData>();
}
private void OnGridStateInit(GridStateEventArgs<SampleData> args)
{
args.GridState = JsonSerializer.Deserialize<GridState<SampleData>>(SerializedState);
}
private void SaveState()
{
var state = Grid.GetState();
SerializedState = JsonSerializer.Serialize(state, new JsonSerializerOptions { WriteIndented = true });
// save serialized state to DB, for example
}
private void LoadState()
{
var state = JsonSerializer.Deserialize<GridState<SampleData>>(SerializedState);
Grid.SetState(state);
}
private class SampleData
{
public string Name { get; set; }
}
}
Reproduction steps:
Hello,
Can you confirm that:
If you are seeing a problem with the built-in supported setup (filter generated by the grid erroneously, or the System.Text.Json serializer failing) with the latest version (2.21.1 at the moment), please modify the sample from the documentation to showcase it: https://docs.telerik.com/blazor-ui/components/grid/state#save-and-load-grid-state-from-browser-localstorage so I can have a look.
Regards,
Marin Bratanov
Progress Telerik
Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.