mariusmuntean / ChartJs.Blazor

Brings Chart.js charts to Blazor
https://www.iheartblazor.com/
MIT License
687 stars 152 forks source link

Bar chart truncating first and last columns when using multiple datasets #52

Closed cnickel closed 4 years ago

cnickel commented 4 years ago

Describe the bug

Bar chart truncating first and last columns when using multiple datasets. You can see that the width of the first and last columns appear to be half that of the columns in the middle. This can also be witnessed in a single dataset bar chart, but doesn't have the same impact of not previewing any data.

Which Blazor project type is your bug related to?

Which charts does this bug apply to?

ChartJsBarChart

To Reproduce

Steps to reproduce the behavior:

  1. Using this version of ChartJSBlazor '1.0.2'.
  2. Run this code 'The default VS template for Blazor server side'.
  3. With these arguments 'using the BarChart to chart the Celsius and Fahrenheit data from the forecasts service '.
  4. See error.

Expected behavior

All of the data should be visible within a bar chart

Screenshots

If applicable, add screenshots to help explain your problem. image

Additional context / logging

Replace the content of the FetchData.razor page with the code below. Note that in the screenshot I have charted Celsius twice for no other reason than to continue to review the behaviour of the chart.

Code example

Please provide full code examples below where possible to make it easier for the developers to check your issues.

@page "/fetchdata"

@using System.ComponentModel
@using logparser_results_dashboard.web.Data
@using ChartJs.Blazor.ChartJS.BarChart
@using ChartJs.Blazor.ChartJS.BarChart.Axes
@using ChartJs.Blazor.ChartJS.Common.Axes
@using ChartJs.Blazor.ChartJS.Common.Axes.Ticks
@using ChartJs.Blazor.ChartJS.Common.Properties
@using ChartJs.Blazor.ChartJS.Common.Wrappers
@using ChartJs.Blazor.Charts
@using ChartJs.Blazor.Util
@inject WeatherForecastService ForecastService

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from a service.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
<div style="width:600px;height:300px">
    <ChartJs.Blazor.Charts.ChartJsBarChart @ref="_barChart" Config="@_barChartConfig" Width="400" Height="200" />
</div>    

    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {

    WeatherForecast[] forecasts;

    private ReferenceConverter ReferenceConverter = new ReferenceConverter(typeof(ChartJsBarChart));

    private BarConfig _barChartConfig;
    private ChartJsBarChart _barChart;
    private BarDataset<DoubleWrapper> _barCelciusDataSet;
    private BarDataset<DoubleWrapper> _barFarenheitsDataSet;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);

        _barChartConfig = new BarConfig
        {
            Options = new BarOptions
            {
                Title = new OptionsTitle
                {
                    Display = true,
                    Text = "Weather Chart"
                },
                Scales = new BarScales
                {
                    XAxes = new List<CartesianAxis>
                                        {
                                            new BarCategoryAxis
                                            {
                                                BarPercentage = 0.5,
                                                BarThickness = BarThickness.Flex
            }
        },
                    YAxes = new List<CartesianAxis>
                                        {
                                            new BarLinearCartesianAxis
                                            {
                                                Ticks = new LinearCartesianTicks
                                                {
                                                    BeginAtZero = false
                                                }
                                            }
                                        }
                }
            }
        };

        _barChartConfig.Data.Labels.AddRange(forecasts.OrderBy(o => o.Date).Select(x => x.Date.ToShortDateString()).ToArray());

        _barCelciusDataSet = new BarDataset<DoubleWrapper>
        {
            Label = "Celcius",
            BackgroundColor = ColorUtil.FromDrawingColor(System.Drawing.Color.Blue),
            BorderWidth = 0,
            HoverBackgroundColor = ColorUtil.FromDrawingColor(System.Drawing.Color.Blue),
            HoverBorderColor = ColorUtil.FromDrawingColor(System.Drawing.Color.Blue),
            HoverBorderWidth = 1,
            BorderColor = "#ffffff"
        };

        _barFarenheitsDataSet = new BarDataset<DoubleWrapper>
        {
            Label = "Farenheit",
            BackgroundColor = ColorUtil.FromDrawingColor(System.Drawing.Color.Red),
            BorderWidth = 0,
            HoverBackgroundColor = ColorUtil.FromDrawingColor(System.Drawing.Color.Red),
            HoverBorderColor = ColorUtil.FromDrawingColor(System.Drawing.Color.Red),
            HoverBorderWidth = 1,
            BorderColor = "#ffffff"
        };

        var _AnotherBarCelciusDataSet = new BarDataset<DoubleWrapper>
        {
            Label = "Another celcius",
            BackgroundColor = ColorUtil.FromDrawingColor(System.Drawing.Color.Green),
            BorderWidth = 0,
            HoverBackgroundColor = ColorUtil.FromDrawingColor(System.Drawing.Color.Green),
            HoverBorderColor = ColorUtil.FromDrawingColor(System.Drawing.Color.Green),
            HoverBorderWidth = 1,
            BorderColor = "#ffffff"
        };

        var _cData = forecasts.OrderBy(o => o.Date).Select(x => (double)x.TemperatureC).ToArray();
        var _fData = forecasts.OrderBy(o => o.Date).Select(x => (double)x.TemperatureF).ToArray();

        _barCelciusDataSet.AddRange(_cData.Wrap());
        _barFarenheitsDataSet.AddRange(_fData.Wrap());
        _AnotherBarCelciusDataSet.AddRange(_cData.Wrap());

        _barChartConfig.Data.Datasets.Add(_barCelciusDataSet);
        _barChartConfig.Data.Datasets.Add(_barFarenheitsDataSet);
        _barChartConfig.Data.Datasets.Add(_AnotherBarCelciusDataSet);
    }
}
Joelius300 commented 4 years ago

Thank you for the detailed report! It really helps to know all those details when providing support.

You're correct that this is a bug but probably not in the sense you think.

This is the expected behaviour when using a cartesian axis that has the property Offset (or offset on js-side) set to false. The thing is, we've written in the property summary that this is set to true in category scales for bar-charts by default. However, the default for all the other cartesian axes is false and we (actually it was me) forgot to manually change it when I created the extended versions for the bar chart.
So even though you're using a category axis in a bar chart (which should satisfy what we wrote) Offset stays false.

FYI here's the property summary for CartesianAxis.Offset:

If true, extra space is added to the both edges and the axis is scaled to fit into the chart area. This is set to true for a category scale in a bar chart by default.

This is fixed easily by just setting Offset to true in BarCategoryAxiss constructor. However, we need to keep in mind that if you use a regular CategoryAxis in a bar-chart, Offset will still be set to false. Therefore we also need to update the summary to make that clear.
I will provide a fix for this (after the other thing is cleared up, see below). I don't know when it'll be updated in the nuget.

To work around the issue you can manually set Offset = true in the x-axis (BarCategoryAxis) you add in your chart (just add it to the other initalization options like BarPercentage = 0.5). I've tested it and it works exactly like expected.

@mariusmuntean When looking at this issue I've noticed that you've removed the ExtendedCartesianAxes.tt in 88ed13d28d which generated the extended versions of the cartesian axes for the bar chart. May I ask why you did that and also if we should opt that back in? If not, I'll just provide the fix for the "static" file. Keep in mind that any change to the regular cartesian axes will need to be mirrored manually to the bar axes if we don't use the code-template for this.

This issue will be closed once the fix is done and merged into master (aka do not close yet), thanks :)

cnickel commented 4 years ago

@Joelius300, Thank you so much for the swift reply, along with the simple work around, I appreciate it. As you have stated adding the Offset=true did in fact work in my project as well.

Thanks again.

mariusmuntean commented 4 years ago

That was an oversight on my part. I've reintroduced the T4 template and adjusted it such that the category axis for bar charts is initialized with the Offset property set to true.

mariusmuntean commented 4 years ago

Feel free to reopen this issue if you think there's more to do there. My commits: https://github.com/mariusmuntean/ChartJs.Blazor/commit/9aa52daff5eac98d690f2514a08c9598f9dadddc https://github.com/mariusmuntean/ChartJs.Blazor/commit/c718735b45b00245929b689607f7cb7f488f4303