I have a grid that is bound through a CustomAdaptor. When I add a new row as per the following...
I see the Edit Row and add my data then press Update...
... and I see the newly added row, all good so far.. But when I attempt to add a second row...
.. I don't get the Edit Row.. but the Update and Cancel Buttons are enabled...
If I press Cancel nothing happens (Update and Cancel remain enabled). If I press Update, I see a new Row with no text...
At which point I can no longer select a row in the data grid.
If I change to Dialog model it pops up the edit window each time and I can add new rows, but I still can't select any rows.
I use the data grid a lot but this is the first time I am seeing such an issue.
<SfGrid @ref="_gridView" TValue="ValidatorClientViewModel" ID="ValidatorTreeView" Query="QueryData" AllowSorting="true"
AllowPaging="true" Toolbar="@ToolbarItems" Width="AutoWidth">
<GridTemplates>
<EmptyRecordTemplate>
<span>No Selected Aggregates</span>
</EmptyRecordTemplate>
</GridTemplates>
<GridSelectionSettings Type="Syncfusion.Blazor.Grids.SelectionType.Multiple"></GridSelectionSettings>
<SfDataManager @ref="DataManager" AdaptorInstance="@typeof(ValidatorsDataAdaptorModel)" Adaptor="Adaptors.CustomAdaptor"></SfDataManager>
<GridPageSettings PageSize="5"></GridPageSettings>
<GridEditSettings AllowAdding="true" AllowEditing="true" AllowDeleting="true" Mode="Syncfusion.Blazor.Grids.EditMode.Normal"></GridEditSettings>
<GridEvents OnBeginEdit="OnBeginEdit" OnActionFailure="OnActionFailure" OnActionBegin="OnActionBegin" OnActionComplete="OnActionComplete"
OnBatchAdd="OnBatchAdd" RowSelected="RowSelecthandler" TValue="ValidatorClientViewModel"> </GridEvents>
<GridColumns>
<GridColumn Field="Validator.ValidatorId.Id" HeaderText="Id" IsPrimaryKey="true" Visible=false Width="0"></GridColumn>
<GridColumn Field="Validator.ValidatorName" HeaderText="Validator Name" Width="50" ValidationRules="@(new Syncfusion.Blazor.Grids.ValidationRules { Required = true })"></GridColumn>
</GridColumns>
</SfGrid>
This took me a long time to figure out this odd behavior that I was experiencing and it's super easy to reproduce.
In my case there was nothing wrong with my custom data adaptor however the data that I was returning was originally derived from a List
I recently altered that backing ViewModel to derive from ReactiveObject (ReactiveUI) and in the process altered the backing collection from List
As such, the ReadAsync operation in my Custom Adaptor returned this collection of ObservableCollection
To reproduce my issue, create a similar data adaptor as per the one below and bind to a DataGrid....
public class Test
{
public string Id;
public string Name;
}
public partial class TestAdaptorModel : Syncfusion.Blazor.DataAdaptor
{
ObservableCollection
public override async Task<object> ReadAsync(DataManagerRequest dmr, string key = null)
{
return new DataResult()
{ Result = _fake, Count = _fake.Count };
}
public override async Task<object> InsertAsync(DataManager dataManager, object value, string key)
{
_fake.Add(value as Test);
return value;
}
public override async Task<object> UpdateAsync(DataManager dataManager, object value, string keyField, string key)
{
return value;
}
public override async Task<object> RemoveAsync(DataManager dataManager, object value, string keyField, string key)
{
foreach (var vm in _fake)
{
if (vm.Id.Equals(value.ToString()))
{
_fake.Remove(vm);
return null;
}
}
return null;
}
}
In my actual custom data adaptor I cast the backing ObservableCollection<T> back to a List<T> in the ReadAsync method which seems to resolve my issue. Can you explain this behavior please?
here is my full custom data adaptor...
using Autofac;
using Microsoft.AspNetCore.Components;
using Serilog;
using Syncfusion.Blazor;
using Syncfusion.Blazor.Data;
using System;
using System.Linq;
using System.Threading.Tasks;
using NextWare.Infrastructure.DDD;
using System.Collections.Generic;
using System.ComponentModel;
using Splat;
using AutoMapper;
using NextWare.Infrastructure.BaseViewModel;
using NextWare.Domain.ValidationServices.Validator.ClientViewModels;
namespace NextWare.Domain.ValidationServices.Validator.DataAdaptors
{
public partial class ValidatorsDataAdaptorModel : Syncfusion.Blazor.DataAdaptor, IViewModelCollection<ValidatorClientViewModel, ValidatorsClientViewModel>
{
private string _lastEditedId;
public ISearchResponse<ValidatorClientViewModel> SearchResponse
{
get;
private set;
}
public ValidatorsClientViewModel CientViewModel
{
get;
private set;
}
private Guid _adaptorID = Guid.NewGuid();
private IMapper _mapper;
public ValidatorsDataAdaptorModel()
{
_mapper = Locator.Current.GetService<IMapper>();
Log.Information("Created DataAdaptor for Validators {adaptorID}", _adaptorID.ToString());
}
public void AssignViewModel(ValidatorsClientViewModel vm)
{
CientViewModel = vm;
SearchResponse = CientViewModel as ISearchResponse<ValidatorClientViewModel>;
}
public override async Task<object> ReadAsync(DataManagerRequest dmr, string key = null)
{
try
{
if (CientViewModel == null)
return new DataResult()
{Result = new List<DataResult<ValidatorClientViewModel>>(), Count = 0};
Log.Information("Started ReadAsync for Validators {adaptorID}", _adaptorID.ToString());
long count = 0;
await CientViewModel.ExecuteQueryAsync(_mapper.Map<NextWare.Client.Infrastructure.Elastic.DataManagerRequest>(dmr), _lastEditedId);
_lastEditedId = null;
// Total documents in Elastic
count = CientViewModel.TotalDocuments;
IEnumerable<ValidatorClientViewModel> dataSource = CientViewModel.Validators;
if (dmr.Sorted != null && dmr.Sorted.Count > 0)
{
// Sorting
dataSource = DataOperations.PerformSorting(CientViewModel.Validators, dmr.Sorted);
}
SearchResponse = CientViewModel as ISearchResponse<ValidatorClientViewModel>;
// return response
var readResponse = dmr.RequiresCounts ? new DataResult()
{ Result = CientViewModel.Validators.ToList(), Count = Convert.ToInt32(count)} : (object)CientViewModel.Validators.ToList();
Log.Information("Started ReadAsync for Validators {adaptorID}", _adaptorID.ToString());
return readResponse;
}
catch (Exception ex)
{
Log.Error("Exception trying to ReadAsync { ex} for Validators", ex);
}
return null;
}
public override async Task<object> InsertAsync(DataManager dataManager, object value, string key)
{
try
{
if (value == null)
return value;
Log.Information("Started InsertAsync for Validators {adaptorID}", _adaptorID.ToString());
var vm = value as ValidatorClientViewModel;
_lastEditedId = vm.Validator.ValidatorId.Id; // when data is added or updated, we need to get the last page on next search
await CientViewModel.InsertValidatorClientViewModel(vm);
Log.Information("Completed InsertAsync for Validators {adaptorID}", _adaptorID.ToString());
return vm;
}
catch (Exception ex)
{
Log.Error("Exception trying to add { ex}", ex);
}
return value;
}
public override async Task<object> UpdateAsync(DataManager dataManager, object value, string keyField, string key)
{
try
{
if (value == null)
return value;
Log.Information("Started UpdateAsync for Validators {adaptorID}", _adaptorID.ToString());
var vm = value as ValidatorClientViewModel;
_lastEditedId = vm.Validator.ValidatorId.Id; // when data is added or updated, we need to get the last page on next search
await CientViewModel.UpdateValidatorClientViewModel(vm);
Log.Information("Completed UpdateAsync for Validators {adaptorID}", _adaptorID.ToString());
return vm;
}
catch (Exception ex)
{
Log.Error("Exception trying to update {ex}", ex);
}
return value;
}
public override async Task<object> RemoveAsync(DataManager dataManager, object value, string keyField, string key)
{
try
{
if (value == null)
return value;
Log.Information("Started RemoveAsync for Validators {adaptorID}", _adaptorID.ToString());
await CientViewModel.DeleteValidatorClientViewModel(value.ToString());
Log.Information("Completed RemoveAsync for Validators {adaptorID}", _adaptorID.ToString());
return null;
}
catch (Exception ex)
{
Log.Error("Exception trying to delete {Id} {ex} for Validators", value.ToString(), ex);
}
return null;
}
public override async Task SetParametersAsync(ParameterView parameters)
{
Log.Information("Started SetParametersAsync for Validators {adaptorID}", _adaptorID.ToString());
await base.SetParametersAsync(parameters);
Log.Information("Started SetParametersAsync for Validators {adaptorID}", _adaptorID.ToString());
}
public override async Task<object> BatchUpdateAsync(DataManager dataManager, object changedRecords, object addedRecords, object deletedRecords, string keyField, string key, int? dropIndex)
{
try
{
Log.Information("Started BatchUpdateAsync for Validators {adaptorID}", _adaptorID.ToString());
if (addedRecords != null)
{
foreach (var vm in (IEnumerable<ValidatorClientViewModel>)addedRecords)
{
Log.Information("Started InsertValidatorClientViewModel for Validators {adaptorID}", _adaptorID.ToString());
await CientViewModel.InsertValidatorClientViewModel(vm);
Log.Information("Completed InsertValidatorClientViewModel for Validators {adaptorID}", _adaptorID.ToString());
}
}
if (changedRecords != null)
{
foreach (var vm in (IEnumerable<ValidatorClientViewModel>)changedRecords)
{
Log.Information("Started UpdateValidatorClientViewModel for Validators {adaptorID}", _adaptorID.ToString());
await CientViewModel.UpdateValidatorClientViewModel(vm);
Log.Information("Completed UpdateValidatorClientViewModel for Validators {adaptorID}", _adaptorID.ToString());
}
}
if (deletedRecords != null)
{
foreach (var vm in (IEnumerable<ValidatorClientViewModel>)deletedRecords)
{
Log.Information("Started DeleteValidatorClientViewModel for Validators {adaptorID}", _adaptorID.ToString());
await CientViewModel.DeleteValidatorClientViewModel(vm.Validator.ValidatorId.Id);
Log.Information("Completed DeleteValidatorClientViewModel for Validators {adaptorID}", _adaptorID.ToString());
}
}
Log.Information("Completed BatchUpdateAsync for Validators {adaptorID}", _adaptorID.ToString());
return changedRecords;
}
catch (Exception ex)
{
Log.Error("Exception {ex} BatchUpdateAsync for Validators", ex);
}
return null;
}
}
}
I noticed that where I am only assigning the DataSource of a DataGrid to ObservableCollection<T> they do not seem to have the same issue as does via the Custom DataAdaptor inte