microsoft / fluentui-blazor

Microsoft Fluent UI Blazor components library. For use with ASP.NET Core Blazor applications
https://www.fluentui-blazor.net
MIT License
3.63k stars 348 forks source link

fix: SelectColumn and PropertyColumn Sort in FluentDataGrid #2353

Closed chigivigi closed 1 month ago

chigivigi commented 1 month ago

🐛 Bug Report

We are developing a new Blazor project that needs server-side rendering, so Fluent UI Blazor Web App Template in Visual Studio was chosen. A new page was created where customers will interact with Fluent DataGrid, they will select one or more rows in grid and run actions on it. When running the project, we were not able to change the sort direction or change by which column should grid be sorted. We cannot select a row inside the grid.

💻 Repro or Code Sample

@page "/weather"
@* @attribute [StreamRendering]
 *@
<PageTitle>Weather</PageTitle>

<h1>Weather</h1>

<p>This component demonstrates showing data.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <FluentDataGrid Items="@forecasts">
        <ChildContent>
            <SelectColumn TGridItem="WeatherForecast" SelectMode="DataGridSelectMode.Multiple" @bind-SelectedItems="@selectedItems"></SelectColumn>
            <PropertyColumn Title="Date" Property="@(c => c!.Date)" Sortable="true" Align=Align.Start IsDefaultSortColumn="true" InitialSortDirection="SortDirection.Ascending" />
            <PropertyColumn Title="Temp. (C)" Property="@(c => c!.TemperatureC)" Sortable="true" Align=Align.Center />
            <PropertyColumn Title="Temp. (F)" Property="@(c => c!.TemperatureF)" Sortable="true" Align=Align.Center />
            <PropertyColumn Title="Summary" Property="@(c => c!.Summary)" Sortable="true" Align=Align.End />
        </ChildContent>
    </FluentDataGrid>
}

@code {
    private IQueryable<WeatherForecast>? forecasts;

    private IEnumerable<WeatherForecast> selectedItems = new List<WeatherForecast>();

    protected override async Task OnInitializedAsync()
    {
        // Simulate asynchronous loading to demonstrate streaming rendering
        await Task.Delay(500);

        var startDate = DateOnly.FromDateTime(DateTime.Now);
        var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
        forecasts = Enumerable.Range(1, 500).Select(index => new WeatherForecast
            {
                Date = startDate.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = summaries[Random.Shared.Next(summaries.Length)],
                Select = Convert.ToBoolean(Random.Shared.Next(0, 1))
            }).AsQueryable();
        selectedItems = forecasts.ToList().Where(x => x.Select);
    }

    private class WeatherForecast
    {
        public DateOnly Date { get; set; }
        public int TemperatureC { get; set; }
        public string? Summary { get; set; }
        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
        public bool Select { get; set; } = false;
    }
}

🤔 Expected Behavior

Columns should be sortable, and we should be able to select one or more rows in grid.

😯 Current Behavior

Columns are not sortable, and we cannot select one or more rows in grid.

🌍 Your Environment

Windows 11 Visual Studio version 17.10.4 Microsoft Edge Microsoft.NET.Sdk.Web net8.0 Microsoft.FluentUI.AspNetCore.Components 4.9.1

vnbaaij commented 1 month ago

When using the template (and depending on the choices you make), Blazor will uses SSR (Static Server-side Rendering) as the default. Without seeing your code, I cannot assess if that is the case her, but I'm guessing it is.

The DataGrid, as most other components, needs interactivity to function.

chigivigi commented 1 month ago

We created a demo project and used the same template and reproduced the issue inside it.

This is the template configuration for our project:

image

The program.cs file looks like this:

using BlazorApp1.Components;
using Microsoft.FluentUI.AspNetCore.Components;

namespace BlazorApp1
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Services.AddRazorComponents()
                .AddInteractiveServerComponents();
            builder.Services.AddFluentUIComponents();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (!app.Environment.IsDevelopment())
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();

            app.UseStaticFiles();
            app.UseAntiforgery();

            app.MapRazorComponents<App>()
                .AddInteractiveServerRenderMode();

            app.Run();
        }
    }
}

The Weather page code is the same as the one above.

vnbaaij commented 1 month ago

You have interactive render mode Server and interactivity location per page

What does your App.razor look like? Does it have <Router @rendermode="..." /> or just <Router />

If just the last one => Weather page is still using SSR then by default.

chigivigi commented 1 month ago

The App.razor looks like this:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="/" />
    <link rel="stylesheet" href="app.css" />
    <link rel="stylesheet" href="BlazorApp1.styles.css" />
    <link rel="icon" type="image/x-icon" href="favicon.ico" />
    <HeadOutlet />
</head>

<body>
    <Routes />
    <script src="_framework/blazor.web.js"></script>
</body>

</html>
vnbaaij commented 1 month ago

So, like I said, the Weather page is rendered with SSR in your situation and without interactivity. Sorting and selecting will not work then. This is not an issue in the library but a configuration thing. Not something we can act upon...

chigivigi commented 1 month ago

Thank you for your help

chigivigi commented 1 month ago

I was able to solve my issue. The project needed to be changed from Component/ page rendering to Global rendering.

I changed HeadOutlet and Routes inside App.razor page:


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="/" />
    <link rel="stylesheet" href="app.css" />
    <link rel="stylesheet" href="BlazorApp1.styles.css" />
    <link rel="icon" type="image/png" href="favicon.ico" />
    <HeadOutlet @rendermode="InteractiveServer" />
</head>

<body>
    <Routes @rendermode="InteractiveServer" />
    <script src="_framework/blazor.web.js"></script>
    <script src="_content/Microsoft.FluentUI.AspNetCore.Components/js/web-components-v2.5.16.min.js" type="module" async></script>
</body>

</html>