Cannot update cell in Grid after save new row

Hello,

I have a razor page with a grid that manages a database table from an API (it has a list of countries). All operations work fine, but I need to update the row Id when creating a new entry in the database. I receive the Id from a POST operation (CreateNationalities) but I am not sure how to update that specific field after the new item is created in the database. The code I have is the following:

@page "/nationality"
@inject INationalitiesService NationalitiesService

@{
var Tool = (new List<string>() {"Add", "Edit", "Delete", "Search"});
}

<h1>Nacionalidades</h1>

<SfGrid DataSource="@NationalitiesList" AllowPaging="true" AllowSorting="true" AllowFiltering="false" AllowGrouping="false" Toolbar="@Tool">
<GridPageSettings PageSize="5"></GridPageSettings>
<GridEvents OnActionBegin="ActionBegin" TValue="Nationalities"></GridEvents>
<GridEditSettings AllowAdding="true" AllowEditing="true" AllowDeleting="true" ShowDeleteConfirmDialog="true" Mode="EditMode.Dialog"></GridEditSettings>
<GridColumns>
<GridColumn Field=@nameof(Nationalities.Id) HeaderText="Id" IsPrimaryKey="true" IsIdentity="true" AllowEditing="false"></GridColumn>
<GridColumn Field=@nameof(Nationalities.CountryName) HeaderText="Nombre"></GridColumn>
<GridColumn Field=@nameof(Nationalities.CountryNativeName) HeaderText="Nombre Nativo"></GridColumn>
<GridColumn Field=@nameof(Nationalities.CountryIso3) HeaderText="ISO"></GridColumn>
<GridColumn Field=@nameof(Nationalities.Flag) HeaderText="Bandera">
<Template>
@{
var nation = context as Nationalities;
string imgSrc = String.Format("data:image/png;base64,{0}", Convert.ToBase64String(nation.Flag, 0, nation.Flag.Length));
}
<div>
<img src="@imgSrc" class="rounded mx-auto img-fluid" alt="Flag" />
</div>
</Template>
</GridColumn>
</GridColumns>
</SfGrid>

@code {
SfGrid<Nationalities> Grid { get; set; }
protected IEnumerable<Nationalities> NationalitiesList { get; set; }
protected override async Task OnInitializedAsync()
{
NationalitiesList = (IEnumerable<Nationalities>)(await NationalitiesService.GetNationalities()).ToList();
}

public async void ActionBegin(ActionEventArgs<Nationalities> args)
{
Nationalities result = null;
System.Console.WriteLine(args.RequestType);
if (args.RequestType == Syncfusion.Blazor.Grids.Action.Save)
{
if (args.Data.Id == 0)
{
result = await NationalitiesService.CreateNationalities(args.Data);
System.Console.WriteLine("Inside create.");
System.Console.WriteLine($"Id: {result.Id}");
}
else
{
System.Console.WriteLine("Inside update.");
result = await NationalitiesService.UpdateNationalities(args.Data.Id, args.Data);
}
}
else if (args.RequestType == Syncfusion.Blazor.Grids.Action.Delete)
{
await NationalitiesService.DeleteNationalities(args.Data.Id);
System.Console.WriteLine("Inside Delete.");
}
}
}


Another question. Since the database table includes a flag image; How do I configure the edit dialog to allow png images and convert them to the format needed in the database (byte[])?

Thanks,

Erick 



9 Replies

RS Renjith Singh Rajendran Syncfusion Team June 8, 2020 04:08 PM UTC

Hi Erick, 

Greetings from Syncfusion support. 

Query 1 : but I need to update the row Id when creating a new entry in the database. 
We suggest you to use the OnActionBegin event of Grid. In this event handler, based on the RequestType as Save you can update the corresponding fields value.  

 
<GridEvents OnActionBegin="BeginHandler" TValue="Order"></GridEvents> 

public void BeginHandler(ActionEventArgs<Order> Args){    if (Args.RequestType == Syncfusion.Blazor.Grids.Action.Save)    {        Args.Data.OrderID = DefaultValue++;    //set the default value.        //save the file name / url in grid datasource. You can generate the byte and store here.        Args.Data.Imagesrc = "scripts/Images/Employees/"+UploadedFile;    }}

Query 2 : Since the database table includes a flag image; How do I configure the edit dialog to allow png images and convert them to the format needed in the database (byte[])? 
You can use the EditTemplate feature of Grid, with this you can render a SfUploader component in the edit form dialog and upload an image. And as like the above code, in the OnActionBegin handler, you can calculate the byte value and assign to the particular field(ImageSrc). 

Documentation :  
 
<GridColumn Field="Imagesrc" HeaderText="Customer Name" Width="200">    <Template>        @{            var imageUrl = (context as Order).Imagesrc;            <div class="image">                <img src="@imageUrl" />            </div>        }    </Template>    <EditTemplate>        <SfUploader ID="uploadFiles" AllowedExtensions=".jpg,.png,.jpeg" Multiple="false">            <UploaderEvents FileSelected="Selected"></UploaderEvents>        </SfUploader>    </EditTemplate></GridColumn>
 

We have prepared a sample based on the above scenarios, please download the sample from the link below, 
 
Please get back to us if you need further assistance. 

Regards, 
Renjith Singh Rajendran 



ER Erick June 22, 2020 11:59 PM UTC

Hi,

I was able to sort out the image issue.

However, I still haven't solved the row id issue. Let me explain with another example. I have a SfGrid that gets the info from an API service. I get the grid data with OnInitializedAsync. Please consider the following code .razor page:

@page "/salutations"
@inject IAPIService APIService

@{
var Tool = (new List<string>() {"Add", "Edit", "Delete"});
}

<h1>Saludos</h1>

<SfGrid @ref="Grid" DataSource="@SalutationsList" AllowPaging="false" AllowSorting="true" AllowReordering="false" AllowResizing="true" AllowFiltering="false" AllowGrouping="false" ShowColumnChooser="false" Toolbar="@Tool">
<GridEvents OnActionBegin="ActionBegin" TValue="Salutations"></GridEvents>
<GridEditSettings AllowAdding="true" AllowEditing="true" AllowDeleting="true" ShowDeleteConfirmDialog="true" Mode="EditMode.Dialog"></GridEditSettings>
<GridColumns>
<GridColumn Field=@nameof(Salutations.Id) HeaderText="Id" IsPrimaryKey="true" IsIdentity="true" AllowEditing="false" Visible="true" ShowInColumnChooser="false"></GridColumn>
<GridColumn Field=@nameof(Salutations.SalutationShort) HeaderText="Saludo" AutoFit="true"></GridColumn>
<GridColumn Field=@nameof(Salutations.SalutationDescription) HeaderText="Descripción" AutoFit="true"></GridColumn>
</GridColumns>
</SfGrid>


@code {
SfGrid<Salutations> Grid { get; set; }

protected IEnumerable<Salutations> SalutationsList { get; set; }

protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
SalutationsList = (IEnumerable<Salutations>)(await APIService.GetSalutations()).ToList();
}

public async void ActionBegin(ActionEventArgs<Salutations> args)
{
Salutations result = null;
System.Console.WriteLine(args.RequestType);
if (args.RequestType == Syncfusion.Blazor.Grids.Action.Save)
{
if (args.Data.Id == 0)
{
System.Console.WriteLine("Inside create.");
result = await APIService.CreateSalutations(args.Data);
System.Console.WriteLine($"Id: {result.Id}");
}
else
{
System.Console.WriteLine("Inside update.");
result = await APIService.UpdateSalutations(args.Data.Id, args.Data);
}
}
else if (args.RequestType == Syncfusion.Blazor.Grids.Action.Delete)
{
System.Console.WriteLine("Inside Delete.");
await APIService.DeleteSalutations(args.Data.Id);
}
}
}

I can get, update, and delete rows without any problem. However, when I add a new row I am unable to update the first column (Salutations.Id) to the correct value. On creation, this Id=0 since it is an autoincrement field in the database used by the API. From the CreateSalutations API call, I get the mentioned Id (result.Id).  I have tried updating this field in the grid with Grid.UpdateCell, Grid.RefreshColumns, and Grid.Refresh; none of this work and always get Id=0 in the Grid. I have to refresh the whole page in order to get the correct Id from the API again.


Thanks in advance for your comments.

Erick



PS Pavithra Subramaniyam Syncfusion Team June 23, 2020 03:27 PM UTC

Hi Erick, 
 
You can achieve your requirement by resetting the Grid data List (“SalutationsList”) inside the “OnActionComplete” event with “Save” requestType. Please refer to the below code example and sample link for more information. 
 
<SfGrid @ref="Grid" DataSource="@Data" TValue="Order" AllowFiltering="true" Toolbar="@(new List<string> {"Add","Edit","Delete" })" AllowPaging="true"> 
    <GridEvents OnActionBegin="ActionBeginHandler" OnActionComplete="OnActionComplete" TValue="Order"></GridEvents> 
    <GridEditSettings AllowAdding="true" AllowEditing="true" AllowDeleting="true" Mode="Syncfusion.Blazor.Grids.EditMode.Dialog">        
    </GridEditSettings> 
    <GridColumns> 
        <GridColumn Field="OrderID" HeaderText="Order ID" IsPrimaryKey="true" IsIdentity="true" TextAlign="TextAlign.Right" Width="120"></GridColumn> 
        .   .  . 
    </GridColumns> 
</SfGrid> 
 
@code{ 
     .  . . 
    public async Task OnActionComplete(ActionEventArgs<Order> Args) 
    { 
        if(Args.RequestType == Syncfusion.Blazor.Grids.Action.Save) 
        { 
            Data = await OrderData.GetPeople(); 
        } 
    } 
} 
 
 
Please get back to us if you need any further assistance on this. 
 
Regards, 
Pavithra S 
 



ER Erick June 23, 2020 04:53 PM UTC

Thanks for the prompt answer.

At first, it didn't work, but just added Grid.Refresh() inside OnActionComplete method and it work ok.

Regards,

Erick


PS Pavithra Subramaniyam Syncfusion Team June 24, 2020 08:41 AM UTC

Hi Erick, 

Thanks for your update.  

We are glad to hear that the problem has been fixed. Please get back to us if you need further assistance on this.  

Regards, 
Pavithra S 



MI Michael June 26, 2020 03:00 AM UTC

I am having exactly the same problem.  The ID column will not refresh in the grid after being assigned a value from the api call.  It works if I hard code a value and assign it to the ID field, then the grid reflects the change.  But it doesn't work when assigned from an async api call.  Are you suggesting that the only way to overcome this is by reloading the entire dataset within the OnActionComplete event?  That seems a ridiculous work around.  Could you please review this again and determine whether there is a suitable solution?


ER Erick June 26, 2020 09:04 PM UTC

Hi Michael,

Just to let you know, I changed my API to use OData because of the future issues I might have when retrieving big chunks of information from the server. With OData you can make a call to retrieve only the info for the current page of the Grid. The ODataV4Adaptor makes it unnecessary for me to handle the grid events.

However, recently I realized that for the original code, I wasn't updating correctly the cell info. So, for the ActionBegin Event the following can be done:

    public async void ActionBegin(ActionEventArgs<People> args)
    {
         if (args.RequestType == Syncfusion.Blazor.Grids.Action.Save)
        {
            if (args.Data.Id == 0)
            {
                result = await APIService.CreatePeople(args.Data);
                await Grid.SetCellValue(0, "Id", result.Id);
                await Grid.SaveCell();
            }
            else
            {
                result = await APIService.UpdatePeople(args.Data.Id, args.Data);
            }
        }
        else if (args.RequestType == Syncfusion.Blazor.Grids.Action.Delete)
        {
            await APIService.DeletePeople(args.Data.Id);
        }

My mistake was on the "key" parameter of SetCellValue. It must match the primary key of the Grid (0 for the newly created row). Just tested it and it works fine.

Regards,

Erick






VN Vignesh Natarajan Syncfusion Team July 1, 2020 01:31 PM UTC

Hi Michael,  
  
Query: “Are you suggesting that the only way to overcome this is by reloading the entire dataset within the OnActionComplete event?  That seems a ridiculous work around.  Could you please review this again and determine whether there is a suitable solution?” 
  
From your query we understand that you are facing issues while updating the changes in your database using GridEvents. While binding the IQueryable data directly to Grid datasource property, CRUD operations changes (done using GridEvents) will not reflect in Grid unless, datasource property is updated. At that time, external Grid refresh or assigning the updated data to datasource property is necessary. So we have suggested you to assign the updated data to Grid datasource property in OnActionComplete event. 
  
If you do not want to refresh the datasource / grid externally. Then we suggest you to achieve your requirement using CustomAdaptor as component feature to bind and perform CRUD operation in Grid. Refer the below code example.  
  
<SfGrid TValue="Order" AllowFiltering="true" Toolbar="@(new List<string> { "Add""Edit""Delete""Update""Cancel""Search" })" AllowSorting="true" AllowPaging="true"> 
    <GridEvents OnActionFailure="Fail" TValue="Order"></GridEvents> 
    <SfDataManager Adaptor="Adaptors.CustomAdaptor"> 
        <CustomAdaptor></CustomAdaptor> 
    </SfDataManager> 
    <GridEditSettings AllowAdding="true" AllowEditing="true" AllowDeleting="true"></GridEditSettings> 
    <GridColumns> 
        <GridColumn Field="OrderID" HeaderText="Order ID" IsPrimaryKey="true" IsIdentity="true" TextAlign="TextAlign.Right" Width="120"></GridColumn> 
        <GridColumn Field="CustomerID" HeaderText="Customer Name" Width="150"></GridColumn> 
        <GridColumn Field="EmployeeID" HeaderText="Freight" Format="C2" TextAlign="TextAlign.Right" EditType="EditType.NumericEdit" Width="120"></GridColumn> 
    </GridColumns> 
</SfGrid> 
  
// Performs data Read operation   public override async Task<objectReadAsync(DataManagerRequest dm, string key = null)   {       var data = await Service.GetPeople();       IEnumerable<Order> DataSource = (IEnumerable<Order>)data;. . . . ..        return dm.RequiresCounts ? new DataResult() { Result = DataSource, Count = count } : (object)DataSource;   }   // Performs Insert operation   public override async Task<objectInsertAsync(DataManager dm, object value, string key)   {       await Service.InsertOrderAsync(value as Order);              return value;   }    // Performs Remove operation   public override async Task<objectRemoveAsync(DataManager dm, object value, string keyField, string key)   {       await Service.DeleteOrderAsync((int)value);             return value;   }    // Performs Update operation   public override async Task<objectUpdateAsync(DataManager dm, object value, string keyField, string key)   {       await Service.UpdateOrderAsync((value as Order).OrderID, value as Order);             return value;   }
  
  
Kindly download the sample from below  
  
  
Note: change the connectionstring in OrderContext.cs file based on Northwnd.Mdf file located in App_Data folder  
  
Refer our UG documentation for your reference 
  
  
Please get back to us if you have further queries. 
  
Regards, 
Vignesh Natarajan  



PM Papa Momar January 18, 2021 10:10 PM UTC

Hello Michael
I had the same problem as you.

I resolved it by doing this.




Hope it helps!

Loader.
Up arrow icon