Search code examples
c#asp.net-coretelerikblazor-client-side

Telerik for Blazor dropdown select event not updating data in grid


I'm using a Telerik grid that is showing rows of data generated from a database. Each database item has a time slot associated with it (by the hour). I have a TelerikDropDownList that adds filtering options to the grid (select an hour). What I'm trying to do is only show the data associated with the hour selected from the dropdown in the grid. I'm using WebAssembly btw.

Here is the grid component:

    <ShowSelect />            

    <TelerikGrid Data="_showItem" Height="80%"
                 Pageable="true" Sortable="true" Groupable="true"
                 FilterMode="Telerik.Blazor.GridFilterMode.FilterMenu"
                 Resizable="true" Reorderable="true" >
        <GridColumns>                
                <GridColumn Field="@(nameof(ShowItem.planned_sequence))" Title="SEQ" />
                <GridColumn Field="@(nameof(ShowItem.matrix_id))" Title="Matrix ID" />
                <GridColumn Field="@(nameof(ShowItem.item_id))" Title="Item ID" />
                <GridColumn Field="@(nameof(ShowItem.item_desc))" Title="Description" />
                <GridColumn Field="@(nameof(ShowItem.item_type_id))" Title="Item Type ID" />
                <GridColumn Field="@(nameof(ShowItem.planned_selling_price))" Title="Planned Price" />
                <GridColumn Field="@(nameof(ShowItem.planned_availabe_qty))" Title="Available Qty" />
                <GridColumn Field="@(nameof(ShowItem.planned_minutes))" Title="P Mins" />
        </GridColumns>
    </TelerikGrid>

@code{
public ShowItem[] _showItem;            

[Inject] HttpClient HttpClient { get; set; }       
[Inject] public AppData ShowData { get; set; }

protected override async Task OnInitializedAsync()
{
    _showItem = await HttpClient.GetJsonAsync<ShowItem[]>("API call...");

    _showItem = _showItem.Where(i => i.time_slot_id == ShowData.SelectedShow).ToArray();
}        
}

My ShowSelect component:

<TelerikDropDownList Value="ShowData.SelectedShow" Data="@Shows"
                 TextField="ShowName" ValueField="ShowId"
                 ValueChanged="@((int s) => ShowSelected(s))"></TelerikDropDownList>

@code {
public List<Show> Shows { get; set; } = new List<Show>();

public SelectedShow SelectedShow { get; set; } = new SelectedShow();

[Inject] public AppData ShowData { get; set; }

protected override void OnInitialized()
{
    base.OnInitialized();

    var time = DateTime.UtcNow.AddHours(-5); //Fix for no WebAssembly time zone option (Eastern Standard Time)

    for(int i = 15; i > 0; i--)
    {
        var hour = time.AddHours(-i);
        Shows.Add(new Show { ShowId = hour.Hour, ShowName = $"{Convert.ToChar(int.Parse(hour.ToString("HH")) + 65)} - {hour.ToString("hh tt").ToLower()} {hour.ToString("MM/dd/yy")}" }); 
    }

    Shows.Add(new Show { ShowId = time.Hour, ShowName = $"{Convert.ToChar(int.Parse(time.ToString("HH")) + 65)} - {time.ToString("hh tt").ToLower()} {time.ToString("MM/dd/yy")}" });

    for(int i = 1; i < 15; i++)
    {
        var hour = time.AddHours(i);
        Shows.Add(new Show { ShowId = hour.Hour, ShowName = $"{Convert.ToChar(int.Parse(hour.ToString("HH")) + 65)} - {hour.ToString("hh tt").ToLower()} {hour.ToString("MM/dd/yy")}" });
    }
}

public void ShowSelected(int showId)
    {
        ShowData.SelectedShow = showId;

        Show Show = Shows.Where(s => s.ShowId == showId).First();
        SelectedShow.ShowId = Show.ShowId;
        SelectedShow.ShowName = Show.ShowName;
    }
}

My AppData service:

public class AppData
{
    public int SelectedShow { get; set; } = DateTime.UtcNow.AddHours(-5).Hour;
}

And my Models:

public class ShowItem
{
    public int network_id { get; set; }
    public DateTime spt_date_id { get; set; }
    public int time_slot_id { get; set; }
    public string show_num { get; set; }
    public int planned_sequence { get; set; }
    public int item_id { get; set; }
    public int matrix_id { get; set; }
    public int item_type_id { get; set; }
    public int planned_selling_price { get; set; }
    public int planned_availabe_qty { get; set; }
    public int planned_minutes { get; set; }
    public string item_desc { get; set; }
}

public class Show
{
    public int ShowId { get; set; }
    public string ShowName { get; set; }
}

public class SelectedShow
{
    public int ShowId { get; set; }
    public string ShowName { get; set; }
}    

The problem I'm having is the grid is not changing when selecting a different time slot from the dropdown. Anyone know where I'm going wrong here?


Solution

  • I will assume that the grid component gets the following value correctly set [Inject] public AppData ShowData { get; set; } after the dropdown change.

    The issue is that in the grid component there is no event handler or any code that will update the grid data.

    The OnInitializedAsync event will fire once when the grid component is added to the page for the first time and that's it - it won't be called again, so your service won't be called again to get new grid data.

    The way I would personally handle this is to expose an event from the ShowSelect component that I can consume in the grid component in order to call my service again.

    With this, I won't really need the AppState if that's all it contains.

    I would also add a parameter to my API so the server will do my filtering and send me back only the relevant data.

    EDIT: Because of the comments, I made an example of exposing the event, here it is (I cut some corners in the data binding to make it shorter, but I hope it still illustrates the point).

    First, the ShowSelect component

    <TelerikDropDownList Value="@ShowId" Data="@Shows"
                         ValueChanged="@((int s) => ShowSelected(s))"></TelerikDropDownList>
    
    @code {
        List<int> Shows { get; set; } = new List<int> { 1, 2, 3 };
        [Parameter]
        public int ShowId { get; set; }
        [Parameter]
        public EventCallback<int> OnShowIdChanged { get; set; }
    
        async Task ShowSelected(int showId)
        {
            ShowId = showId;
            await OnShowIdChanged.InvokeAsync(ShowId);
        }
    }
    

    Then, consuming that event in the main component with the grid:

    <ShowSelect ShowId="@ShowId" OnShowIdChanged="@ShowIdChangedHandler" />
    
    <TelerikGrid Data="@MyData" Height="400px" Pageable="true">
        <GridColumns>
            <GridColumn Field="@(nameof(SampleData.Id))" Width="120px" />
            <GridColumn Field="@(nameof(SampleData.Name))" Title="Employee Name" Groupable="false" />
            <GridColumn Field="@(nameof(SampleData.GenerationDate))" Title="Benchmark - data generated at" />
            <GridColumn Field="@(nameof(SampleData.ShowId))" Title="Show ID - see the dropdown" />
        </GridColumns>
    </TelerikGrid>
    
    @code {
        public List<SampleData> MyData { get; set; }
        int ShowId { get; set; } // you may not even want this parameter here, but it helps with keeping the dropdown in sync with the main page
    
        protected override async Task OnInitializedAsync()
        {
            ShowId = 2;//maybe you fetch that from a service too
            MyData = await GetDataFromService(ShowId);
        }
    
        async Task ShowIdChangedHandler(int showId)
        {
            ShowId = showId;
            MyData = await GetDataFromService(ShowId);
        }
    
        async Task<List<SampleData>> GetDataFromService(int showId)
        {
            await Task.Delay(500);
            //simulate service here
            var data = Enumerable.Range(1, 30).Select(x => new SampleData
            {
                Id = x,
                Name = "name " + x,
                GenerationDate = DateTime.Now,
                ShowId = showId
            }).ToList();
    
            return await Task.FromResult(data);
        }
    
        public class SampleData
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public DateTime GenerationDate { get; set; }
            public int ShowId { get; set; }
        }
    }