Kerminator1973 / bbTableCallback

"Saving the grid's sort state as part of GridSetting" feature
0 stars 0 forks source link

Как реализован экспорт в Excel через GridSettings #1

Open Kerminator1973 opened 3 months ago

Kerminator1973 commented 3 months ago

Вступил в переписку с разработчиком Blazor Bootstrap, ожидается решение этой проблемы в начале мая.

Приостанавливаю исследования до 10 мая.

Kerminator1973 commented 3 months ago

Попробовал собрать BlazorBootstrap - собирается прекрасно, генерирует \blazorbootstrap\bin\Debug\net6.0\BlazorBootstrap.dll

Однако если подключить проект BlazorBootstrap как зависимость, то тестовое приложение не видит пространство имён BlazorBootstrap.

Пошёл по другому пути:

  1. Выбрал свой демо-проект в solution tree
  2. В контекстном меню "Add -> Project Reference... "
  3. Нажал кнопку "Browse..." и выбрал DLL
Kerminator1973 commented 3 months ago

Тем не менее, полноценно код не собрался, попал на ошибку, связанную с проблемой фильтрации:

1>D:\Sources\bbTableCallback\DataTableApp\Pages\FetchData.razor(22,5,22,86): error RZ9985: Multiple components use the tag 'DateInput'. Components: DataTableApp.Pages.DateInput, BlazorBootstrap.DateInput, BlazorBootstrap.DateInput, Microsoft.AspNetCore.Components.Bind

Временно удалил следующий код:

<div class="mb-3">
    <strong>Begin Date:</strong>:
</div>
<div class="mb-3">
    <DateInput TValue="DateOnly" @bind-Value="@BeginDate" Placeholder="Enter Date" />
    <Button Color="ButtonColor.Primary" Size="Size.Medium" @onclick="ChangeFilter">Set Filter</Button>
</div>

Приложение запустилось на BlazorBootstrap развалился - работала только небольшая часть приложения:

WARNING: Processing source-maps of https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js took longer than 987.7178000000004 ms so we continued execution without waiting for all the breakpoints for the script to be set.Debugging hotkey: Shift+Alt+D (when application has focus)
blazor Loaded 23.51 MB resources
This application was built with linking (tree shaking) disabled. Published applications will be significantly smaller.
Loaded 23.51 MB resources from cache
crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: Could not find 'window.blazorBootstrap.sidebar.initialize' ('sidebar' was undefined).
      Error: Could not find 'window.blazorBootstrap.sidebar.initialize' ('sidebar' was undefined).
Kerminator1973 commented 3 months ago

Однако в варианте с DLL, отладчик входит в код Bootstrap при регистрации сервиса:

builder.Services.AddBlazorBootstrap();

Первая причина - WebAssemblyRenderer не нашёл sidebar:

crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: Could not find 'window.blazorBootstrap.sidebar.initialize' ('sidebar' was undefined).
      Error: Could not find 'window.blazorBootstrap.sidebar.initialize' ('sidebar' was undefined).
          at https://localhost:7194/_framework/blazor.webassembly.js:1:328
          at Array.forEach (<anonymous>)

И тут проблема, похоже, кроется в том, что я не подготовил wwwroot-файлы, или что-то ещё.

Kerminator1973 commented 3 months ago

Попробую пойти по другому пути - соберу демонстрационное приложение \blazorbootstrap\BlazorBootstrap.Demo.WebAssembly из исходных текстов BlazorBootstrap

Загружается большое приложение, в котором можно поставить контрольные точки в код BlazorBootstrap и попасть в них. В частности, можно выполнять отладку Grid-а: \blazorbootstrap\BlazorBootstrap.Demo.RCL\Components\Pages\Grid\Grid_Demo_23_Multiple_Selection.razor

Т.е. это вариант, в котором можно что-то править в движке BlazorBootstrap, но это пока не отвечает на вопрос, как можно использовать результат сборки изменённого BlazorBootstrap в других приложениях.

Kerminator1973 commented 3 months ago

Не достаточно импортировать BlazorBootstrap.dll. Необходимо так же скопировать файлы из папки "wwwroot" проекта Blazor.Bootstrap в соответствующую папку нового проекта: "\DataTableApp\wwwroot_content\Blazor.Bootstrap". После этого проект запускается и отладчик позволяет входить из нашего приложения в исходные тексты BlazorBootstrap и выполнять его отладку.

Однако, разметка слегка поехала.

Kerminator1973 commented 3 months ago

Возможно, причина состоит в том, что не удаётся найти стилистический файл приложения "DataTableApp.styles.css":

Failed to load resource: the server responded with a status of 404 () : DataTableApp.styles.css:1

Нужно попробовать сгенерировать свежее приложение на актуальных шаблонах BlazorBootstrap.

Kerminator1973 commented 3 months ago

Пытаюсь всё сделать красиво:

Вижу, что появились косяки - часть js-файлов не загрузилась. Т.е. есть какие-то различия. Выбрать в шаблоне целевую платформу нельзя.

Kerminator1973 commented 3 months ago

Шаблон кривой. В частности, происходит дублирование sln-файлов в двух разных папках:

Новое сгенерированное приложение и переход на .NET 8 проблему с версткой не решили - она стала кривой при переходе обновлении компонентов.

Однако, в моменте на всё это можно забить, т.к. приложение, в целом работает и его можно отлаживать.

Kerminator1973 commented 3 months ago

Ключевым является метод SaveGridSettingsAsync():

private Task SaveGridSettingsAsync()
{
    if (!GridSettingsChanged.HasDelegate)
        return Task.CompletedTask;

    var settings = new GridSettings { PageNumber = AllowPaging ? gridCurrentState.PageIndex : 0, PageSize = AllowPaging ? pageSize : 0, Filters = AllowFiltering ? GetFilters() : null };

    return GridSettingsChanged.InvokeAsync(settings);
}

Через делегата вызывается GridSettingsChanged, которому передаётся объект GridSettings. Гипотетически, в него достаточно просто передать дополнительный параметр - текущий срез данных.

Всё это вызывается из класса Grid:

public partial class Grid<TItem> : BlazorBootstrapComponentBase
Kerminator1973 commented 3 months ago

Вызов GridSettingsChanged содержит то, что содержит, отражая GridState:

/// <summary>
/// Current grid state (filters, paging, sorting).
/// </summary>
internal GridState<TItem> gridCurrentState = new(1, null);

Включать в GridState текущий срез данных - не очень крутая идея, т.к. это влияет на конкретную сущность GridState.

По идее, должен быть заполнен HashSet selectedItems:

private HashSet<TItem> selectedItems = new();

Но в моём случае, когда я ввожу фильтрацию данных этот параметр равен null. Т.е. он используется для других целей.

Items тоже использовать нельзя, т.е. это срез на предыдущий момент, т.е. до применения фильтрации.

Kerminator1973 commented 3 months ago

Однако можно поменять местами вызовы в FilterChangedAsync():

internal async Task FilterChangedAsync()
{
    ...
    await SaveGridSettingsAsync();
    await RefreshDataAsync(false, token);
}
Kerminator1973 commented 3 months ago

Гипотетически мы могли бы добавить ещё один параметр - Items:

private Task SaveGridSettingsAsync()
{
    if (!GridSettingsChanged.HasDelegate)
        return Task.CompletedTask;

    var settings = new GridSettings { PageNumber = AllowPaging ? gridCurrentState.PageIndex : 0, PageSize = AllowPaging ? pageSize : 0, Filters = AllowFiltering ? GetFilters() : null, 
        Items = this.items };

    return GridSettingsChanged.InvokeAsync(settings);
}

Однако это не сработает поскольку items - это шаблонный контейнер, а класс GridSettings обычный Poco-класс, т.е. мы не можем просто добавить ссылку, т.к. нам нужна специализация - тип Grid-а.

Kerminator1973 commented 3 months ago

Костыльное решение, которые мы можем сделать без перепроектирования библиотеки - это изменить сигнатуру GridSettingsChanged и добавить ещё один параметр - ссылку на Item, с учётом специализации.

return GridSettingsChanged.InvokeAsync(settings);

Но на самом деле, это сложно сделать, т.е. делегат и так уже определён, как шаблон:

[Parameter]
public EventCallback<GridSettings> GridSettingsChanged { get; set; }

Можно, конечно, вытащить отдельный метод для доступа к items, но это совсем костыль, не по парадигме библиотеки.