Skip to content

DataGrid: Export: Architecture, csv exporter, external binary data exporter #6067

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: master
Choose a base branch
from

Conversation

tesar-tech
Copy link
Collaborator

Description

Closes #6042

This pull request introduces a new exporter architecture for the DataGrid component.

As part of this architecture, I've also included two example supported formats

  • Csv – a text-based format that is bundled in the Blazorise.DataGrid package.
  • Bson – a binary format that is simple to work with and demonstrates external extensibility. It requires an additional dependency and is implemented in a separate project (Blazorise.DataGrid.Exporters). (This is where Excel exporters should go too).

While the new architecture is not small, it enables a powerful level of extensibility. Creating a CSV exporter, for example, could be done with much less code — but the purpose of this system is to ensure external exporters can be plugged into DataGrid easily.

With this architecture, no changes are needed in the Blazorise.DataGrid core codebase to support new exporters. A developer only needs to implement a single abstract class, focusing purely on the core export logic and options. Thanks to these design choices, the boilerplate for creating a new exporter (e.g. a potential JSON exporter) is reduced to a minimum (see the BsonToFileExporter).

While developing the architecture, I also re-evaluated what "export" should mean in the DataGrid context. It doesn't have to mean only exporting to a file — it can also mean exporting to a string (TextExporter), the clipboard, or any other target. Currently, we support 3 targets:

  • File
  • Clipboard
  • Text (in-memory string)

This concept of "targets" opens up future extensibility such as:

  • API-based export
  • Printer
  • Email or sharing
  • Chat/Telegram integration

(Many of these could work with the simple TextTarget or minor variations of it, so I din't go much deeper)


Nomenclature

  • Target: The destination where exported DataGrid content ends up (File, Clipboard, Text).
  • Format: The format in which data is encoded (CSV, BSON, XML, etc.).
  • Exporter: A combination of format and target, e.g. CsvToFileExporter. This is why the Csv directory contains multiple files — it supports multiple targets.

Getting data from DataGrid

This area still needs improvement, as my knowledge of the full DataGrid capabilities is limited. Currently, only columns with a defined Field are exported, and the value is either formatted (for text) or raw (for binary).

One potential future enhancement:
A property like BaseDataGridColumn.Func<object, object> ExportValue could be used to customize how each column is exported — giving the developer full control per column.

The data passed to exporters is structured as a List<List<object>> or List<List<string>>, depending on whether the export is binary or text-based.


Usage

As simple as:

// Simple export to CSV with default options
dataGridRef.Export(new CsvToFileExporter());

// Copy to clipboard without headers
dataGridRef.Export(new CsvToClipboardExporter(new() { ExportHeader = false }));

// Export to CSV file with a custom file name and only the first 3 rows
dataGridRef.Export(
    new CsvToFileExporter(new() { FileNameNoExtension = "custom-csv-file" }),
    new DataGridExportOptions { NumberOfRows = 3 }
);

// Bson export (defined in an external project)
dataGridRef.Export(new BsonToFileExporter());

As shown above, only one public method in DataGrid.razor.cs is needed to support all export formats and targets.


  • This PR does not include UI changes (e.g. export button). That could come in a future PR.
  • Blazorise.DataGrid.Exporters is an external test project designed to validate the extensibility of the exporter system. It serves as a foundation for future projects where we'll support additional exporters that require external dependencies (e.g., Excel). It can be merged in this test form - it does nothing wrong.

@tesar-tech tesar-tech requested a review from stsrki April 10, 2025 15:38
@stsrki
Copy link
Collaborator

stsrki commented Apr 10, 2025

There are many things to review here. But overall I like the approach that will help us to extend it, and with minimal changes to DataGrid.

I will properly review it later but for now I would like to focus more on high level architecture.

  1. I would keep only basic interfaces and utilities in the Blazorise.DataGrid.Exporters, and move each of specific implementation into a separate sub-package. eg, Blazorise.DataGrid.Exporters.Bson, Blazorise.DataGrid.Exporters.Excel, etc. This is because these dependencies can be quite large to download and we should keep the separate so that users can choose what they want without downloading everything at runtime.
  2. There are many new utilities and helpers in the DataGrid package. So I think we should move them to the core Blazorise package. Why? Because we might need them for other components. Like Markdown, Scheduler, etc. They can also have export at some point.
  3. I like the idea for ExportValue. We should add it but in a separate PR. I want to first merge this in a working state.

@tesar-tech
Copy link
Collaborator Author

Hmm. Ok, so now Sources are added alongside Targets and Formats. An Exporter is defined by these three components.

  • The DataGridExporter no longer exists. You can now export any type of data to any kind of result. The DataGrid.Export method still requires TabularData, but it can also handle other formats—image, text, vector, or any other data structure.
  • This makes it a universal data transfer mechanism:
    • Between Blazorise components (e.g., DataGrid to Chart)
    • To the outside world (e.g., DataGrid to Excel)
    • From the outside world to Blazorise components (e.g., CSV to Table)

Also, there's no place anymore for Blazorise.DataGrid.Exporters.Bson, because if it's intended for use with other components, it should be in Blazorise.Exporters.Bson. The same applies to Csv—which now has its own project. We can evaluate whether referencing it directly from DataGrid makes sense.


The common export logic has been moved to the Blazorise project. It might even be worth giving it its own project—Blazorise.Export.

@stsrki
Copy link
Collaborator

stsrki commented Apr 14, 2025

The common export logic has been moved to the Blazorise project. It might even be worth giving it its own project—Blazorise.Export.

Yeah, I think this might be a better approach, as Blazorise core might become too bloated.

So have:

  • Blazorise.Exporters project with all the shared logic
  • Blazorise.Exporters.Bson
  • Blazorise.Exporters.Csv

Copy link
Collaborator

@stsrki stsrki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are quite some inhertiance going around. I'm still trying to unwrap it in my head...

/// <summary>
/// -1 means all rows
/// </summary>
public int NumberOfRows { get; init; } = -1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can add more options here

public string[] Fields { get; init; } // if null, then all fields are exported

public bool UseCaptions { get; init; } // if true, the Caption will be used for Csv column names

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a question. My idea was to keep it consistent with the "rich markup properties". Basically specify these in markup using column parameters - for example the BaseDataGridColumn.Func<object, object> ExportValue or ExportHeader , SupressExport or similar.

And keep the DataGridExportOptions only for the export itself (that cannot be specified per-column).
The ExportValue is an ultimate customization that would have to be there anyway. And keeping the "column export customization" inside DataGridExportOptions will duplicate the functionality in potentially confusing way.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think adding them in markup makes much since we are already having an Export() method that works in an "imperative" way. So it is only natural that we want to expand on DataGridExportOptions on what to actually export.

/// <summary>
/// Indicates whether an operation was successful.
/// </summary>
bool Success { get; init; }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also have Errors if Success == false?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error handling isn't quite there yet.
Yeah, somehow yes.
It has to start with the js functions - for example when the clipboard isn't available or saving file fails.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

DataGrid export to Excel
2 participants