从 Handsontable 15.3 迁移到 Handsontable 16.0,于 09/07/2025 发布。
更多关于此版本的信息,请参阅 16.0.0 版本的博客文章。
1. 介绍新的 DOM 结构
在 Handsontable 16.0 中,我们改变了表格在 DOM 中的挂载方式。以前,您提供的容器 <div> 成为表格的根元素。现在,该容器作为一个挂载点,Handsontable 在其中创建并注入自己的根元素。
以下是新旧 DOM 结构的并排比较:
旧的 DOM 结构:
body
├── #example.ht-wrapper.handsontable // Root Container/Element
│ ├── .htFocusCatcher // Focus Catcher (top)
│ ├── Data grid content
│ └── . htFocusCatcher // Focus Catcher (down)
├── .hot-display-license-info // License key notification bar
└── Context menus, dropdowns, pop-ups, sidebars
(absolutely positioned elements)
新的 DOM 结构:
body
├── #example // Root Wrapper
│ └── .ht-root-wrapper // Root Element
│ ├─ . htFocusCatcher // Focus Catcher (top)
│ ├── .ht-wrapper.handsontable // Root Container
│ │ └── Data grid content
│ ├── . htFocusCatcher // Focus Catcher (down)
│ ├── .hot-display-license-info // License key notification bar
└── .ht-portal // Portal Element
└── Context menus, dropdowns, pop-ups, sidebars
(absolutely positioned elements)
关键变化:
2. 更新了 CSS 变量
在 Handsontable 16.0 中,我们对 CSS 变量系统进行了重大改进,调整了主题颜色、变量顺序,并提供了更好的定制选项。以下是主要的变更:
新的 CSS 变量
我们引入了新的变量,让自定义变得更容易:
l--ht-letter-spacing: 控制字母间距,以改善可读性和视觉外观。
l--ht-radio-*: 使单选输入的样式更准确。
l--ht-cell-read-only-background-color: 实现更好的只读单元格背景自定义。
l--ht-checkbox-indeterminate: 允许您对复选框的不定状态进行风格设置。
重命名的 CSS 变量
我们已将部分变量重命名为更一致的名称:
旧变量名称 |
新变量名称 |
--ht-icon-active-button-border-color |
--ht-icon-button-active-border-color |
--ht-icon-active-button-background-color |
--ht-icon-button-active-background-color |
--ht-icon-active-button-icon-color |
--ht-icon-button-active-icon-color |
--ht-icon-active-button-hover-border-color |
--ht-icon-button-active-hover-border-color |
--ht-icon-active-button-hover-background-color |
--ht-icon-button-active-hover-background-color |
--ht-icon-active-button-hover-icon-color |
--ht-icon-button-active-hover-icon-color |
迁移说明
如果您在版本 15.3 中使用了自定义 CSS 变量,您需要:
1. 检查您的自定义变量名称是否符合新的命名规范
2. 更新变量引用以使用新的单选按钮输入(仅当复选框变量发生更改时)
3. 利用新变量实现更精细的控制
3. 更新自定义边框的位置
在版本 16.0 中,我们更新了自定义边框的定位方式,以提高准确性和一致性。此更改会影响边框的视觉定位,特别是对于具有自定义边框的单元格。
发生了什么变化?
边框位置已调整,以防止与相邻单元格和标题重叠。
为何这是重大变更?
虽然可能性极低,但若您的应用依赖特定边框位置,或基于边框位置实现了自定义样式,可能需要更新样式。
版本 16.0 中边框的视觉外观与版本 15.3 相比将略有不同。
迁移说明
无需修改代码——新版本会自动处理这些改进。
4. 切换至新的 Angular 包装器(适用于 Angular 16+)
Handsontable 16.0 引入了全新的 Angular 包装器。该包装器旨在与现代 Angular 应用程序实现更佳集成并提升开发体验。若您使用 Angular 16 或更高版本,建议迁移至新包装器。
为何要切换至新 Angular 包装器?
• 组件化架构:新包装器采用 Angular 的组件化架构,允许您将自定义编辑器和渲染器实现为 Angular 组件。
• 改进的 TypeScript 支持:新包装器提供了更完善的 TypeScript 定义。
• 独立组件支持:新包装器全面支持 Angular 的独立组件,使其在现代 Angular 应用中更易于使用。
• 全局配置:新包装器通过依赖注入提供了更好的全局配置管理。
• 模板语法:简化的模板语法减少了冗余代码,使配置更易于维护。
• 实例访问:现在可以通过 ViewChild 直接访问 Handsontable 实例。
步骤 1:更新包依赖项
用新包装器包替换旧的 Angular 包装器包:
npm uninstall @handsontable/angular
npm install @handsontable/angular-wrapper
步骤 2:更新组件配置
将所有配置选项移动到组件中的 GridSettings 对象中。
旧的包装组件:
@Component({
selector: ‘app-root’,
template: `
<hot-table
[data]=“data”
[colHeaders]=“true”
[licenseKey]=“‘non-commercial-and-evaluation’”>
<hot-column data="id" [readOnly]=“true” title="ID"></hot-column>
<hot-column data="name" title="Full name"></hot-column>
</hot-table>
`})export class AppComponent {
data = //...}
新包装组件:
import { GridSettings, HotTableModule } from ‘@handsontable/angular-wrapper’;
@Component({
standalone: true,
imports: [HotTableModule],
template: `<hot-table [data]=“data” [settings]=“gridSettings” />`})export class AppComponent {
data = //...;
gridSettings: GridSettings = {
colHeaders: true,
licenseKey: ‘non-commercial-and-evaluation’,
columns: [
{ data: ‘id’, readOnly: true, title: ‘ID’ },
{ data: ‘name’, title: ‘Full name’ },
]
};}
步骤 3:更新表格实例引用
您引用和与 Handsontable 实例交互的方式已发生变化。
旧的包装器实例引用:
export class AppComponent {
private hotRegisterer = new HotTableRegisterer();
id = ‘hotInstance’;
swapHotData() {
this.hotRegisterer.getInstance(this.id).loadData([[‘new’, ‘data’]]);
}}
新的包装器实例引用:
import { HotTableComponent } from ‘@handsontable/angular-wrapper’;
export class AppComponent {
@ViewChild(HotTableComponent, { static: false })
hotTable!: HotTableComponent;
swapHotData() {
this.hotTable.hotInstance!.loadData([[‘new’, ‘data’]]);
}}
步骤 4:更新全局设定
新的包装器提供更好的全局设定管理。
旧包装器的全局配置:
// Configuration was typically done per componentexport class AppComponent {
hotSettings = {
licenseKey: 'non-commercial-and-evaluation',
};}
使用 ApplicationConfig 的新包装器全局配置:
import { ApplicationConfig } from '@angular/core';import { HOT_GLOBAL_CONFIG, HotGlobalConfig, NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
const globalHotConfig: HotGlobalConfig = {
license: NON_COMMERCIAL_LICENSE,
language: 'en',
themeName: 'ht-theme-main',};
export const appConfig: ApplicationConfig = {
providers: [
{ provide: HOT_GLOBAL_CONFIG, useValue: globalHotConfig },
],};
使用服务的新包装器全局配置:
import { HotGlobalConfigService, NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
export class AppComponent {
constructor(private hotConfig: HotGlobalConfigService) {
this.hotConfig.setConfig({
themeName: 'ht-theme-main',
});
}}
步骤 5:更新自定义编辑器
新的包装器在传统以类为基础的方式之外,引入了以元件为基础的编辑器。
旧包装器自定义编辑器:
import { TextEditor } from 'handsontable/editors/textEditor';
export class CustomEditor extends TextEditor {
override createElements() {
super.createElements();
this.TEXTAREA = document.createElement('input');
this.TEXTAREA. setAttribute('placeholder', 'Custom placeholder');
this.TEXTAREA.setAttribute('data-hot-input', 'true');
this.textareaStyle = this.TEXTAREA.style;
this.TEXTAREA_PARENT.innerText = '';
this.TEXTAREA_PARENT.appendChild(this.TEXTAREA);
}}
// Usage in settings
hotSettings = {
columns: [{ editor: CustomEditor }]};
新的封装元件式编辑器:
import { Component, ViewChild, ElementRef, ChangeDetectionStrategy } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HotCellEditorComponent } from '@handsontable/angular-wrapper';
@Component({
selector: 'app-custom-editor',
imports: [FormsModule],
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div style="width: 100%; overflow: hidden">
<input
#inputElement
type="text"
[value]="getValue()"
(keydown)="onKeyDown($event)"
style="width: 100%; box-sizing: border-box"
/>
</div>
`,})export class CustomEditorComponent extends HotCellEditorComponent<string> {
@ViewChild('inputElement') inputElement! ElementRef;
onKeyDown(event: KeyboardEvent): void {
const target = event.target as HTMLInputElement;
this.setValue(target.value);
}
onFocus(): void {
this.inputElement.nativeElement.select();
}}
// Usage in settings
gridSettings: GridSettings = {
columns: [{ editor: CustomEditorComponent }]};
步骤 6:更新自定义渲染器
新的包装器除了支持基于函数的渲染器外,还支持基于元件的渲染器。
旧的包装器自定义渲染器:
export class AppComponent {
hotSettings = {
columns: [{
renderer(instance, td, row, col, prop, value, cellProperties) {
const img = document.createElement('img');
img.src = value;
td.innerText = 『』;
td.appendChild(img);
return td;
}
}]
};}
新的封装元件式渲染器:
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { HotCellRendererComponent } from '@handsontable/angular-wrapper';
@Component({
selector: 'app-custom-renderer',
template: `
<div class="container" [style.backgroundColor]="value">
{{ value }}
</div>
`,
styles: [`
.container {
height: 100%;
width: 100%;
}
:host {
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
padding: 0;
}
`],
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush})export class CustomRendererComponent extends HotCellRendererComponent<string> {}
// Usage in settings
gridSettings: GridSettings = {
columns: [{ renderer: CustomRendererComponent }]};
步骤 7:更新 CSS 导入
确认您导入的主题 CSS 档案是正确的。
CSS 导入 (两个包装器相同):
@import'handsontable/styles/handsontable.min.css';
@import 'handsontable/styles/ht-theme-main.min.css';
或在 angular.json 中:
{
"styles": [
"src/styles.scss",
"node_modules/handsontable/styles/handsontable.min.css",
"node_modules/handsontable/styles/ht-theme-main.min.css"
]}
常见迁移问题
问题:无法找到模块 ‘@handsontable/angular’
解决方案:确保您已将导入更新为使用 @handsontable/angular-wrapper
问题:hot-column 未被识别
解决方案:新包装器不再使用 <hot-column>。在设置对象中将列配置移动到列数组中。
问题: “未定义HotTableRegisterer ”
解决方案: 使用 @ViewChild(HotTableComponent) 并访问 hotInstance 属性。
问题: “自定义渲染器无法正常工作”
解决方案: 将基于函数的渲染器转换为继承自 HotCellRendererComponent 的组件。
问题:自定义编辑器无法正常工作
解决方案:将基于类的编辑器转换为继承自 HotCellEditorComponent 的组件。
本迁移指南涵盖了旧版和新版 Angular 包装器之间的主要变化。新版包装器提供了与现代 Angular 模式更好的集成、改进的类型安全性和更易于维护的代码库。
5. 引入 pnpm 作为仓库包管理器
自2025年7月1日起,我们已切换至pnpm作为仓库的主要包管理器。
随着仓库中包数量的增加,依赖项数量也随之增长。这使得管理依赖项并以一致的方式安装它们变得困难。为解决此问题,我们已切换至使用pnpm作为主要包管理器。
这会影响我吗?
除非您需要创建Handsontable或任何包装器的自定义构建,否则此更改不会影响您。
如果您正在进行此类操作,则需要使用 pnpm 安装主仓库依赖项。
注意:示例和文档包仍由 npm 管理,不属于主 pnpm 工作区。
如何迁移?
1.安装 pnpm,版本需与根目录 package.json 文件中 packageManager 字段定义的版本一致。
2.如果您之前在仓库的克隆版本上进行过开发,需要删除 node_modules 目录、package-lock.json 文件等。
您可以通过运行 npm run clean:node_modules -- --keep-lockfiles 来完成此操作。
3.运行 pnpm install 安装依赖项。
4.所有 npm 命令仍然可用,因此您可以像以前一样构建包,例如通过运行 npm run build。
京ICP备09015132号-996 | 网络文化经营许可证京网文[2017]4225-497号 | 违法和不良信息举报电话:4006561155
© Copyright 2000-2023 北京哲想软件有限公司版权所有 | 地址:北京市海淀区西三环北路50号豪柏大厦C2座11层1105室