Blazor 站点中的表格
Blazor 是一个使用 C#、HTML、JS 和 CSS 构建用户界面的框架。
¥Blazor is a framework for building user interfaces using C#, HTML, JS and CSS.
SheetJS 是一个用于从电子表格读取和写入数据的 JavaScript 库。
¥SheetJS is a JavaScript library for reading and writing data from spreadsheets.
此演示使用 Blazor 和 SheetJS 来处理和生成电子表格。我们将探讨如何在 Razor 组件中加载 SheetJS,并比较常见的状态模型和数据流策略。
¥This demo uses Blazor and SheetJS to process and generate spreadsheets. We'll explore how to load SheetJS in Razor components and compare common state models and data flow strategies.
优秀的开源软件会随着用户测试和报告而不断成长。任何问题都应报告给 Blazor 项目以进行进一步诊断。
¥Great open source software grows with user tests and reports. Any issues should be reported to the Blazor project for further diagnosis.
dotnet
命令嵌入遥测技术。
¥The dotnet
command embeds telemetry.
DOTNET_CLI_TELEMETRY_OPTOUT
环境变量应设置为 1
。
¥The DOTNET_CLI_TELEMETRY_OPTOUT
environment variable should be set to 1
.
"平台配置" 包含有关在支持的平台上设置环境变量的说明。
¥"Platform Configuration" includes instructions for setting the environment variable on supported platforms.
集成详情
¥Integration Details
安装
¥Installation
可以在加载页面时加载 SheetJS 库,也可以在使用库功能时导入 SheetJS 库。
¥The SheetJS library can be loaded when the page is loaded or imported whenever the library functionality is used.
独立脚本
¥Standalone Script
SheetJS 独立脚本 可以在根 HTML 页面(通常为 wwwroot/index.html
)中加载:
¥The SheetJS Standalone scripts
can be loaded in the root HTML page (typically wwwroot/index.html
):
<!-- use version 0.20.3 -->
<script lang="javascript" src="https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js"></script>
ECMAScript 模块
¥ECMAScript Module
SheetJS ECMAScript 模块脚本可以从函数动态导入。这可确保仅在必要时加载库。以下 JS 示例加载库并返回解析为版本字符串的 Promise:
¥The SheetJS ECMAScript module script can be dynamically imported from functions. This ensures the library is only loaded when necessary. The following JS example loads the library and returns a Promise that resolves to the version string:
async function sheetjs_version(id) {
/* dynamically import the script in the event listener */
const XLSX = await import("https://cdn.sheetjs.com/xlsx-0.20.3/package/xlsx.mjs");
/* use the library */
return XLSX.version;
}
从 C# 调用 JS
¥Calling JS from C#
Razor 元素中事件的回调会调用 C# 方法。C# 方法可以使用 Blazor API 来调用在浏览器全局范围内可见的 JS 方法。
¥Callbacks for events in Razor elements invoke C# methods. The C# methods can use Blazor APIs to invoke JS methods that are visible in the browser global scope.
设置
¥Setup
从 Blazor 调用 JS 函数的主要机制是 IJSRuntime
[^1]。它应该注入到相关 Razor 组件脚本的顶部:
¥The primary mechanism for invoking JS functions from Blazor is IJSRuntime
[^1].
It should be injected at the top of relevant Razor component scripts:
@inject IJSRuntime JS
触发并忘记
¥Fire and Forget
使用 SheetJS writeFile
方法 [^2] 导出文件时,浏览器 API 不提供成功或错误反馈。因此,此演示使用 InvokeVoidAsync
静态方法 [^3] 调用函数。
¥When exporting a file with the SheetJS writeFile
method[^2], browser APIs do
not provide success or error feedback. As a result, this demo invokes functions
using the InvokeVoidAsync
static method[^3].
以下 C# 方法将在浏览器中调用 export_method
方法:
¥The following C# method will invoke the export_method
method in the browser:
private async Task ExportDataset() {
await JS.InvokeVoidAsync("export_method", data);
}
JS 方法必须在全局范围内定义!
¥The JS methods must be defined in the global scope!
在此演示中,脚本被添加到根 HTML 文件的 HEAD
块中:
¥In this demo, the script is added to the HEAD
block of the root HTML file:
<head>
<!-- ... meta / title / base / link tags -->
<link href="SheetJSBlazorWasm.styles.css" rel="stylesheet" />
<!-- script with `export_method` is in the HEAD block -->
<script>
/* In a normal script tag, Blazor JS can call this method */
async function export_method(...rows) {
/* display the array of objects */
console.log(rows);
}
</script>
</head>
使用 <script type="module">
时,默认情况下 Blazor 看不到顶层函数定义。它们必须附加到 globalThis
:
¥When using <script type="module">
, top-level function definitions are not
visible to Blazor by default. They must be attached to globalThis
:
<script type="module">
/* Using `type="module"`, Blazor JS cannot see this function definition */
async function export_method(...rows) {
/* display the array of objects */
console.log(rows);
}
/* Once attached to `globalThis`, Blazor JS can call this method */
globalThis.export_method = export_method;
</script>
Blazor 回调
¥Blazor Callbacks
方法通常使用 @onclick
绑定到 Razor 模板中的按钮。单击以下按钮时,Blazor 将调用 ExportDataset
:
¥Methods are commonly bound to buttons in the Razor template using @onclick
.
When the following button is clicked, Blazor will invoke ExportDataset
:
<button @onclick="ExportDataset">Export Dataset</button>
Blazor 中的状态
¥State in Blazor
示例 总统表 有一个标题行,其中包含 "名称" 和 "索引" 列。
¥The example presidents sheet has one header row with "Name" and "Index" columns.
C# 表示
¥C# Representation
单行的自然 C# 表示是类对象:
¥The natural C# representation of a single row is a class object:
public class President {
public string Name { get; set; }
public int Index { get; set; }
}
var PrezClinton = new President() { Name = "Bill Clinton", Index = 42 };
整个数据集通常存储在类对象数组中:
¥The entire dataset is typically stored in an array of class objects:
private President[] data;
数据交换
¥Data Interchange
InvokeVoidAsync
可以将数据从 C# 状态传递到 JS 函数:
¥InvokeVoidAsync
can pass data from the C# state to a JS function:
await JS.InvokeVoidAsync("export_method", data);
数据集中的每一行都将作为单独的参数传递给 JavaScript 方法,因此 JavaScript 代码应该收集参数:
¥Each row in the dataset will be passed as a separate argument to the JavaScript method, so the JavaScript code should collect the arguments:
/* NOTE: blazor spreads the C# array, so the ... spread syntax is required */
async function export_method(...rows) {
/* display the array of objects */
console.log(rows);
}
每一行都是一个简单的 JavaScript 对象。
¥Each row is a simple JavaScript object.
Blazor 自动展开数组。每一行都作为单独的参数传递给 JavaScript 方法。
¥Blazor automatically spreads arrays. Each row is passed as a separate argument to the JavaScript method.
示例方法使用 JavaScript 扩展语法来收集参数。
¥The example method uses the JavaScript spread syntax to collect the arguments.
导出数据
¥Exporting Data
使用收集的对象数组,SheetJS json_to_sheet
方法 [^4] 将从数据集生成 SheetJS 工作表 [^5]。使用 book_new
方法 [^7] 创建工作簿 [^6] 对象后,使用 writeFile
[^2] 写入文件:
¥With the collected array of objects, the SheetJS json_to_sheet
method[^4] will
generate a SheetJS worksheet[^5] from the dataset. After creating a workbook[^6]
object with the book_new
method[^7], the file is written with writeFile
[^2]:
/* NOTE: blazor spreads the C# array, so the spread is required */
async function export_method(...rows) {
const XLSX = await import("https://cdn.sheetjs.com/xlsx-0.20.3/package/xlsx.mjs");
const ws = XLSX.utils.json_to_sheet(rows);
const wb = XLSX.utils.book_new(ws, "Data");
XLSX.writeFile(wb, "SheetJSBlazor.xlsx");
}
HTML 表格
¥HTML Tables
显示数据集时,Razor 组件通常会生成 HTML 表:
¥When displaying datasets, Razor components typically generate HTML tables:
<table class="table" id="weather-table">
<thead>
<tr><th>日期</th><th>临时。(C)</th> <th>临时。(F)</th><th>摘要</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>
如果它有 id
,前端的 JS 代码可以使用 document.getElementById
DOM 方法找到表元素。可以使用 table_to_book
方法 [^8] 生成 SheetJS 工作簿对象并使用 writeFile
[^2] 导出:
¥If it has an id
, JS code on the frontend can find the table element using the
document.getElementById
DOM method. A SheetJS workbook object can be generated
using the table_to_book
method[^8] and exported with writeFile
[^2]:
async function export_method() {
const XLSX = await import("https://cdn.sheetjs.com/xlsx-0.20.3/package/xlsx.mjs");
const wb = XLSX.utils.table_to_book(document.getElementById("weather-table"));
XLSX.writeFile(wb, "SheetJSBlazor.xlsx");
}
此方法使用文档中已存在的数据,因此不会从 C# 向 JavaScript 传递任何额外数据。
¥This approach uses data that already exists in the document, so no additional data is passed from C# to JavaScript.
完整演示
¥Complete Demo
Blazor + WASM 入门应用包含一个 "天气" 组件,该组件显示来自 C# 管理的数据集的数据。此演示使用 SheetJS 以两种方式导出数据:
¥The Blazor + WASM starter app includes a "Weather" component that displays data from a C#-managed dataset. This demo uses SheetJS to export data in two ways:
-
"导出数据集" 会将行对象从底层 C# 数据存储发送到前端。SheetJS
json_to_sheet
方法 [^4] 构建工作表。¥"Export Dataset" will send row objects from the underlying C# data store to the frontend. The SheetJS
json_to_sheet
method[^4] builds the worksheet. -
"导出 HTML 表" 将使用 SheetJS
table_to_book
方法 [^8] 抓取表格。不会将任何额外数据发送到前端。¥"Export HTML Table" will scrape the table using the SheetJS
table_to_book
method[^8]. No extra data will be sent to the frontend.
该演示在以下部署中进行了测试:
¥This demo was tested in the following deployments:
架构 | 日期 |
---|---|
darwin-arm | 2024-10-15 |
平台配置
¥Platform Configuration
-
将
DOTNET_CLI_TELEMETRY_OPTOUT
环境变量设置为1
。¥Set the
DOTNET_CLI_TELEMETRY_OPTOUT
environment variable to1
.
How to disable telemetry (click to hide)
- Linux/MacOS
- Windows
将以下行添加到 .profile
、.bashrc
和 .zshrc
:
¥Add the following line to .profile
, .bashrc
and .zshrc
:
export DOTNET_CLI_TELEMETRY_OPTOUT=1
关闭并重新启动终端以加载更改。
¥Close and restart the Terminal to load the changes.
在搜索栏中输入 env
,然后选择 "编辑系统环境变量"。
¥Type env
in the search bar and select "Edit the system environment variables".
在新窗口中,单击 "环境变量..." 按钮。
¥In the new window, click the "Environment Variables..." button.
在新窗口中,查找 "系统变量" 部分并单击 "新的..."
¥In the new window, look for the "System variables" section and click "New..."
将 "变量名" 设置为 DOTNET_CLI_TELEMETRY_OPTOUT
,并将值设置为 1
。
¥Set the "Variable name" to DOTNET_CLI_TELEMETRY_OPTOUT
and the value to 1
.
在每个窗口(3 个窗口)中单击 "OK",然后重新启动计算机。
¥Click "OK" in each window (3 windows) and restart your computer.
-
安装.NET
¥Install .NET
Installation Notes (click to show)
For macOS x64 and ARM64, install the dotnet-sdk
Cask with Homebrew:
brew install --cask dotnet-sdk
For Steam Deck Holo and other Arch Linux x64 distributions, the dotnet-sdk
and
dotnet-runtime
packages should be installed using pacman
:
sudo pacman -Syu dotnet-sdk dotnet-runtime
https://dotnet.microsoft.com/en-us/download/dotnet/6.0 is the official source for Windows and ARM64 Linux versions.
-
在 macOS 中打开新的终端窗口或在 Windows 中打开 PowerShell 窗口。
¥Open a new Terminal window in macOS or PowerShell window in Windows.
应用创建
¥App Creation
-
创建一个新的
blazorwasm
应用:¥Create a new
blazorwasm
app:
dotnet new blazorwasm -o SheetJSBlazorWasm
cd SheetJSBlazorWasm
dotnet run
当 Blazor 服务运行时,终端将显示一个 URL:
¥When the Blazor service runs, the terminal will display a URL:
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:6969
-
在新的浏览器窗口中,打开步骤 3 中显示的 URL。
¥In a new browser window, open the displayed URL from Step 3.
-
单击 "天气" 链接并确认页面包含数据表。
¥Click the "Weather" link and confirm the page includes a data table.
-
停止服务器(在终端窗口中按 CTRL+C)。
¥Stop the server (press CTRL+C in the terminal window).
SheetJS 集成
¥SheetJS Integration
-
在
HEAD
块中向wwwroot/index.html
添加以下脚本标记:¥Add the following script tag to
wwwroot/index.html
in theHEAD
block:
<script>
/* NOTE: blazor spreads the C# array, so the spread is required */
async function export_dataset(...rows) {
const XLSX = await import("https://cdn.sheetjs.com/xlsx-0.20.3/package/xlsx.mjs");
const ws = XLSX.utils.json_to_sheet(rows);
const wb = XLSX.utils.book_new(ws, "Data");
XLSX.writeFile(wb, "SheetJSBlazorDataset.xlsx");
}
async function export_html(id) {
const XLSX = await import("https://cdn.sheetjs.com/xlsx-0.20.3/package/xlsx.mjs");
const wb = XLSX.utils.table_to_book(document.getElementById(id));
XLSX.writeFile(wb, "SheetJSBlazorHTML.xlsx");
}
</script>
-
在
Pages/Weather.razor
顶部附近注入IJSRuntime
依赖:¥Inject the
IJSRuntime
dependency near the top ofPages/Weather.razor
:
@page "/weather"
@inject HttpClient Http
@inject IJSRuntime JS
-
在
Pages/Weather.razor
中向TABLE
元素添加 ID:¥Add an ID to the
TABLE
element inPages/Weather.razor
:
{
<table class="table" id="weather-table">
<thead>
<tr>
-
在
Pages/Weather.razor
中向@code
部分添加回调:¥Add callbacks to the
@code
section inPages/Weather.razor
:
private async Task ExportDataset()
{
await JS.InvokeVoidAsync("export_dataset", forecasts);
}
private async Task ExportHTML()
{
await JS.InvokeVoidAsync("export_html", "weather-table");
}
-
在
Pages/Weather.razor
中向模板添加导出按钮:¥Add Export buttons to the template in
Pages/Weather.razor
:
<p>This component demonstrates fetching data from the server.</p>
<button @onclick="ExportDataset">Export Dataset</button>
<button @onclick="ExportHTML">Export HTML TABLE</button>
测试
¥Testing
-
再次启动
dotnet
进程:¥Launch the
dotnet
process again:
dotnet run
当 Blazor 服务运行时,终端将显示一个 URL:
¥When the Blazor service runs, the terminal will display a URL:
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:6969
-
在新的浏览器窗口中,打开步骤 12 中显示的 URL。
¥In a new browser window, open the displayed URL from Step 12.
-
单击 "天气" 链接。页面应与以下屏幕截图相匹配:
¥Click the "Weather" link. The page should match the following screenshot:
-
单击 "导出数据集" 按钮并将生成的文件保存到
SheetJSBlazorDataset.xlsx
。在电子表格编辑器中打开文件并确认数据与表格匹配。由于底层数据集使用不同的标签,列标签将有所不同。¥Click the "Export Dataset" button and save the generated file to
SheetJSBlazorDataset.xlsx
. Open the file in a spreadsheet editor and confirm the data matches the table. The column labels will differ since the underlying dataset uses different labels.
-
单击 "导出 HTML 表" 按钮并将生成的文件保存到
SheetJSBlazorHTML.xlsx
。在电子表格编辑器中打开文件并确认数据与表格匹配。列标签将与 HTML 表匹配。¥Click the "Export HTML TABLE" button and save the generated file to
SheetJSBlazorHTML.xlsx
. Open the file in a spreadsheet editor and confirm the data matches the table. The column labels will match the HTML table.
有点奇怪的是,官方 dotnet
Blazor 示例数据集将 1 C
和 -13 C
标记为 "freezing",但将 -2 C
标记为 "chilly"。按理说,-2 C
也应该冻结。
¥It is somewhat curious that the official dotnet
Blazor sample dataset marks
1 C
and -13 C
as "freezing" but marks -2 C
as "chilly". It stands to
reason that -2 C
should also be freezing.
[^1]: 请参阅 dotnet
文档中的 "Microsoft.JSInterop.IJSRuntime"。
¥See "Microsoft.JSInterop.IJSRuntime" in the dotnet
documentation.
[^2]: 见 writeFile
于 "写入文件"
¥See writeFile
in "Writing Files"
[^3]: 请参阅 dotnet
文档中的 "Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync"。
¥See "Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync" in the dotnet
documentation.
[^4]: 见 json_to_sheet
于 "实用工具"
¥See json_to_sheet
in "Utilities"
[^5]: 见 "Sheet 对象"
¥See "Sheet Objects"
[^6]: 见 "工作簿对象"
¥See "Workbook Object"
[^7]: 见 book_new
于 "实用工具"
[^8]: 见 "HTML" 实用程序中的 table_to_book