vikramlearning / blazorbootstrap

An Enterprise-class Blazor Bootstrap Component library built on the Blazor and Bootstrap CSS frameworks.
https://docs.blazorbootstrap.com/
Apache License 2.0
793 stars 44 forks source link

LineChart inside a GridDetailView #908

Open ymoona opened 1 month ago

ymoona commented 1 month ago

Describe the bug I have a LineChart that I would like to show in the GridDetailView of a Grid. When placing this LineChart just above the grid (with some test data) it is rendered just fine. But placing the LineChart inside GridDetailView it does not render, and the area stays blank.

The code is based on this example.

Versions (please complete the following information):

Sample code Sample code to reproduce the issue. <GridDetailView TItem="TheObject"> <Button Type="ButtonType.Button" Class="mb-2" Color="ButtonColor.Primary" Size="ButtonSize.Small" @onclick="async () => await Init()"> Init </Button> <Button Type="ButtonType.Button" Class="mb-2" Color="ButtonColor.Primary" Size="ButtonSize.Small" @onclick="async () => await Update()"> Update </Button> <LineChart @ref="lineChart" Width="800" Class="mb-4" /> </GridDetailView>

I based the code on this example: https://demos.blazorbootstrap.com/charts/line-chart#how-it-works

GitHub repo GitHub repo with minimal code to reproduce the issue.

Desktop (please complete the following information):

Additional context Is what I am trying supported? I was expecting the rendering issue to be in the initialization, so I created button to init and update the LineChart. They work fine when the LineChart is rendered outside the Grid.

gvreddy04 commented 1 month ago

@ymoona Thank you for using BlazorBootstrap. I'll check this scenario and get back to you.

ymoona commented 1 month ago

@gvreddy04 I spend a bit more time this morning trying to figure out what is going on, and I found why it won't work. The issue is that a grid has multiple items and therefor multiple reference to the same chart.

I fix that by referencing from a dictionary based on the key. Then I had the issue that I could the right moment to initialize the chart as "OnAfterRenderAsync" is too early (not sure why, but it does not work). So I now do that on the OnRowClick, that fires anyway when GridDetailView is requested by the user.

@page "/"

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>
<Grid TItem="Employee1"
      Class="table table-hover border-top"
      Data="employees"
      AllowDetailView="true"
      AllowRowClick="true"
      OnRowClick="OnRowClick">

    <GridColumns>
        <GridColumn TItem="Employee1" HeaderText="Id" PropertyName="Id">
            @context.Id
        </GridColumn>
        <GridColumn TItem="Employee1" HeaderText="Employee Name" PropertyName="Name">
            @context.Name
        </GridColumn>
        <GridColumn TItem="Employee1" HeaderText="Designation" PropertyName="Designation">
            @context.Designation
        </GridColumn>
        <GridColumn TItem="Employee1" HeaderText="DOJ" PropertyName="DOJ">
            @context.DOJ
        </GridColumn>
        <GridColumn TItem="Employee1" HeaderText="Active" PropertyName="IsActive">
            @context.IsActive
        </GridColumn>
    </GridColumns>

    <GridDetailView TItem="Employee1" >
        <div class="row">
            <div class="col-2 fw-bold">Id</div>
            <div class="col">@context.Id</div>
        </div>
        <div class="row">
            <div class="col-2 fw-bold">Name</div>
            <div class="col">@context.Name</div>
        </div>
        <div class="row">
            <div class="col-2 fw-bold">Designation</div>
            <div class="col">@context.Designation</div>
        </div>
        <div class="row">
            <div class="col-2 fw-bold">DOJ</div>
            <div class="col">@context.DOJ</div>
        </div>
        <div class="row">
            <div class="col-2 fw-bold">IsActive</div>
            <div class="col">@context.IsActive</div>
        </div>
        <LineChart @ref="@_charts[context.Id]" Width="800" Class="mb-4" />
    </GridDetailView>

</Grid>

@code {
    private readonly Dictionary<int, LineChart> _charts = new();
    private LineChart _lineChart = default!;
    private LineChartOptions _lineChartOptions = default!;
    private ChartData _chartData = default!;

    protected override void OnInitialized()
    {
        var colors = ColorUtility.CategoricalTwelveColors; // This is not correct in the examples

        var labels = new List<string> { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
        var datasets = new List<IChartDataset>();

        var dataset1 = new LineChartDataset
            {
                Label = "Windows",
                Data = [7265791, 5899643, 6317759, 6315641, 5338211, 8496306, 7568556, 8538933, 8274297, 8657298, 7548388, 7764845],
                BackgroundColor = colors[0],
                BorderColor = colors[0],
                BorderWidth = 2,
                HoverBorderWidth = 4,
                PointBackgroundColor = [colors[0]],
                PointRadius = [0], // hide points
                PointHoverRadius = [4],
            };
        datasets.Add(dataset1);

        var dataset2 = new LineChartDataset
            {
                Label = "macOS",
                Data = [1809499, 1816642, 2122410, 1809499, 1850793, 1846743, 1954797, 2391313, 1983430, 2469918, 2633303, 2821149],
                BackgroundColor = colors[1],
                BorderColor = colors[1],
                BorderWidth = 2,
                HoverBorderWidth = 4,
                PointBackgroundColor = [colors[1]],
                PointRadius = [0], // hide points
                PointHoverRadius = [4],
            };
        datasets.Add(dataset2);

        var dataset3 = new LineChartDataset
            {
                Label = "Other",
                Data = [1081241, 1100363, 1118136, 1073255, 1120315, 1395736, 1488788, 1489466, 1489947, 1414739, 1735811, 1820171],
                BackgroundColor = colors[2],
                BorderColor = colors[2],
                BorderWidth = 2,
                HoverBorderWidth = 4,
                PointBackgroundColor = [colors[2]],
                PointRadius = [0], // hide points
                PointHoverRadius = [4],
            };
        datasets.Add(dataset3);

        _chartData = new ChartData
            {
                Labels = labels,
                Datasets = datasets
            };

        _lineChartOptions = new()
            {
                Responsive = true,
                Interaction = new Interaction { Mode = InteractionMode.Index }
            };

        if (_lineChartOptions.Scales.X != null)
        {
            _lineChartOptions.Scales.X.Title = new ChartAxesTitle(); // Needed to initialize this myself
            if (_lineChartOptions.Scales.X.Title != null)
            {
                _lineChartOptions.Scales.X.Title.Text = "2019";
                _lineChartOptions.Scales.X.Title.Display = true;
            }
        }

        if (_lineChartOptions.Scales.Y != null)
        {
            _lineChartOptions.Scales.Y.Title = new ChartAxesTitle(); // Needed to initialize this myself 
            if (_lineChartOptions.Scales.Y.Title != null)
            {
                _lineChartOptions.Scales.Y.Title.Text = "Visitors";
                _lineChartOptions.Scales.Y.Title.Display = true;
            }
        }

        if (_lineChartOptions.Plugins.Title != null)
        {
            _lineChartOptions.Plugins.Title.Text = "Operating system";
            _lineChartOptions.Plugins.Title.Display = true;
        }
    }

    private List<Employee1> employees = new List<Employee1> {
        new Employee1 { Id = 107, Name = "Alice", Designation = "AI Engineer", DOJ = new DateOnly(1998, 11, 17), IsActive = true },
        new Employee1 { Id = 103, Name = "Bob", Designation = "Senior DevOps Engineer", DOJ = new DateOnly(1985, 1, 5), IsActive = true },
        new Employee1 { Id = 106, Name = "John", Designation = "Data Engineer", DOJ = new DateOnly(1995, 4, 17), IsActive = true },
        new Employee1 { Id = 104, Name = "Pop", Designation = "Associate Architect", DOJ = new DateOnly(1985, 6, 8), IsActive = false },
        new Employee1 { Id = 105, Name = "Ronald", Designation = "Senior Data Engineer", DOJ = new DateOnly(1991, 8, 23), IsActive = true }
    };

    public record class Employee1
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        public string? Designation { get; set; }
        public DateOnly DOJ { get; set; }
        public bool IsActive { get; set; }
    }

    private async Task OnRowClick(GridRowEventArgs<Employee1> args)
    {
        if (_charts.ContainsKey(args.Item.Id))
        {
            _lineChartOptions.Plugins.Title.Text = $"Id:{args.Item.Id}";
            await _charts[args.Item.Id].InitializeAsync(_chartData, _lineChartOptions);
        }
    }
}

The result looks like this: {5E10FD14-89EE-4B37-B404-957D8EF464A7}

What are your thoughts on this implementation?