跳至主要内容

Understanding Components

在 ChatGPT 中打开
Java API

在构建 webforJ 中的自定义组件之前,了解构成组件工作原理的基础架构非常重要。本文解释了组件层次结构、组件身份、生命周期概念,以及关注接口如何提供组件能力。

理解组件层次结构

webforJ 将组件组织成一个具有两个组的层次结构:你永远不应该扩展的框架内部类,以及专为构建自定义组件而设计的类。本节解释了为什么 webforJ 使用组合而非继承以及层次结构的每个层级提供了什么。

为什么选择组合而不是扩展?

在 webforJ 中,内置组件如 ButtonTextField 是最终类——你不能对其进行扩展:

// 这在 webforJ 中不起作用
public class MyButton extends Button {
// Button 是最终的 - 不能被扩展
}

webforJ 使用 组合而不是继承。你可以创建一个扩展 Composite 的类,并在内部组合组件。Composite 充当一个容器,包装一个单一组件(称为绑定组件),并允许你添加自己的组件和行为。

public class SearchBar extends Composite<FlexLayout> {
private final FlexLayout self = getBoundComponent();
private TextField searchField;
private Button searchButton;

public SearchBar() {
searchField = new TextField("搜索");
searchButton = new Button("去");

self.setDirection(FlexDirection.ROW)
.add(searchField, searchButton);
}
}

为什么你不能扩展内置组件

webforJ 组件被标记为最终类,以维护底层客户端网页组件的完整性。扩展 webforJ 组件类将授予对底层网页组件的控制,这可能导致意想不到的后果,并破坏组件行为的一致性和可预测性。

有关详细的解释,请参见架构文档中的 最终类和扩展限制

组件层次结构

开发者类(使用这些):

  • Composite
  • ElementComposite
  • ElementCompositeContainer

内部框架类(绝对不要直接扩展):

  • Component
  • DwcComponent
绝不要直接扩展 ComponentDwcComponent

永远不要直接扩展 ComponentDwcComponent。所有内置组件都是最终的。始终使用与 CompositeElementComposite 的组合模式。

尝试扩展 DwcComponent 会抛出运行时异常。

关注接口

关注接口是提供特定能力的 Java 接口。每个接口添加一组相关的方法。例如,HasSize 添加控制宽度和高度的方法,而 HasFocus 添加管理焦点状态的方法。

当你在组件上实现一个关注接口时,你无需编写任何实现代码即可访问这些能力。该接口提供默认实现,可以自动工作。

实现关注接口使你的自定义组件拥有与内置 webforJ 组件相同的 API:

// 实现 HasSize 以自动获取宽度/高度方法
public class SizedCard extends Composite<Div> implements HasSize<SizedCard> {
private final Div self = getBoundComponent();

public SizedCard() {
self.setText("卡片内容");
}

// 无需实现这些 - 你可以免费获得:
// setWidth(), setHeight(), setSize()
}

// 像任何 webforJ 组件一样使用它
SizedCard card = new SizedCard();
card.setWidth("300px")
.setHeight("200px");

该复合组件会自动将这些调用转发给底层的 Div。无需额外代码。

外观

这些接口控制组件的视觉呈现,包括其尺寸、可见性、样式和主题。

接口描述
HasSize控制宽度和高度,包括最小和最大限制。扩展 HasWidthHasHeight 及其最小/最大变体。
HasVisibility显示或隐藏组件而不从布局中移除它。
HasClassName管理组件根元素上的 CSS 类名。
HasStyle应用和移除行内 CSS 样式。
HasHorizontalAlignment控制内容在组件内的水平对齐方式。
HasExpanse使用标准扩展标记(XSMALLXLARGE)设置组件的大小变体。
HasTheme应用主题变体,如 DEFAULTPRIMARYDANGER
HasPrefixAndSuffix向组件内部的前缀或后缀插槽添加组件。

内容

这些接口管理组件显示的内容,包括文本、HTML、标签、提示和其他描述性内容。

接口描述
HasText设置和检索组件的纯文本内容。
HasHtml设置和检索组件的内部 HTML。
HasLabel添加一个与组件关联的描述性标签,用于辅助功能。
HasHelperText在组件下方显示次要提示文本。
HasPlaceholder设置在组件没有值时显示的占位符文本。
HasTooltip附加一个在悬停时出现的工具提示。

状态

这些接口控制组件的交互状态,包括是否启用、可编辑、必填或在加载时获得焦点。

接口描述
HasEnablement启用或禁用组件。
HasReadOnly将组件置于只读状态,其中值可见但无法更改。
HasRequired将组件标记为必填,通常用于表单验证。
HasAutoFocus当页面加载时,自动将焦点移到该组件上。

焦点

这些接口管理组件如何接收和响应键盘焦点。

接口描述
HasFocus管理焦点状态以及组件是否可以接收焦点。
HasFocusStatus检查组件当前是否有焦点。需要往返到客户端。
HasHighlightOnFocus控制组件内容在获得焦点时是否高亮显示,以及如何高亮显示(KEYMOUSEKEY_MOUSEALL 等)。

输入约束

这些接口定义组件接受的值,包括当前值、允许的范围、长度限制、格式掩码和区域特定行为。

接口描述
HasValue获取和设置组件的当前值。
HasMin设置允许的最小值。
HasMax设置允许的最大值。
HasStep设置数字或范围输入的步长增量。
HasPattern应用正则表达式模式以限制接受的输入。
HasMinLength设置组件值中所需的最小字符数。
HasMaxLength设置组件值中允许的最大字符数。
HasMask对输入应用格式掩码。由掩码字段组件使用。
HasTypingMode控制输入字符是插入或覆盖现有字符(INSERTOVERWRITE)。由掩码字段和 TextArea 使用。
HasRestoreValue定义用户按 Escape 键或调用 restoreValue() 时组件重置为的值。由掩码字段使用。
HasLocale为区域敏感格式存储每个组件的区域设置。由掩码日期和时间字段使用。
HasPredictedText设置预测的或自动完成的文本值。由 TextArea 使用以支持内联建议。

验证

这些接口添加客户端验证行为,包括标记组件为无效、显示错误消息以及控制验证何时运行。

接口描述
HasClientValidation将组件标记为无效,设置错误消息并附加客户端验证器。
HasClientAutoValidation控制组件是否在用户输入时自动验证。
HasClientAutoValidationOnLoad控制组件在首次加载时是否验证。
HasClientValidationStyle控制验证消息的显示方式:INLINE(在组件下方)或 POPOVER

DOM 访问

这些接口提供对组件底层 HTML 元素和客户端属性的低级访问。

接口描述
HasAttribute读取和写入组件元素上的任意 HTML 属性。
HasProperty直接在客户端元素上读取和写入 DWC 组件属性。

i18n

该接口为需要显示本地化文本的组件提供翻译支持。

接口描述
HasTranslation提供 t() 辅助方法,用于使用应用程序当前的区域设置解析翻译键为本地化字符串。
警告

如果底层组件不支持接口能力,你会收到运行时异常。在这种情况下提供你自己的实现。

有关可用关注接口的完整列表,请参见 webforJ JavaDoc

组件标识符

webforJ 组件具有三种不同类型的标识符,服务于不同的目的:

  • 服务器端组件 ID (getComponentId()) - 由框架自动分配,用于内部组件跟踪。需要查询特定组件或实现自定义组件注册表时使用。
  • 客户端组件 ID (getClientComponentId()) - 从 JavaScript 访问底层网页组件。需要调用本机网页组件方法或与客户端库集成时使用。
  • HTML id 属性 (setAttribute("id", "...")) - 标准 DOM 标识符。用于 CSS 定位、测试自动化选择器以及将表单标签链接到输入。

了解这些差异有助于你为用例选择正确的标识符。

服务器端组件 ID

每个组件在创建时自动分配一个服务器端标识符。该标识符被框架用于内部跟踪组件。通过 getComponentId() 获取:

Button button = new Button("点击我");
String serverId = button.getComponentId();

当你需要在容器中查询特定组件或实现自定义组件跟踪逻辑时,服务器端 ID 很有用。

客户端组件 ID

客户端组件 ID 提供从 JavaScript 访问底层网页组件的能力。这允许你在需要时直接与客户端组件交互:

Button btn = new Button("点击我");
btn.onClick(e -> {
OptionDialog.showMessageDialog("按钮被点击", "发生了一个事件");
});

btn.whenAttached().thenAccept(e -> {
Page.getCurrent().executeJs("objects.get('" + btn.getClientComponentId() + "').click()");
});

使用 getClientComponentId() 和 JavaScript 中的 objects.get() 访问网页组件实例。

important

客户端组件 ID 并不是 DOM 元素的 HTML id 属性。有关设置 HTML ID 以便测试或 CSS 定位的信息,请参见 使用组件

组件生命周期概述

webforJ 自动管理组件的生命周期。框架处理组件创建、附加和销毁,而无需手动干预。

生命周期钩子 在你需要时可用:

  • onDidCreate(T container) - 在组件附加到 DOM 后调用
  • onDidDestroy() - 在组件被销毁时调用

这些钩子是 可选的。当你需要时使用它们:

  • 清理资源(停止间隔、关闭连接)
  • 初始化需要 DOM 附加的组件
  • 与客户端 JavaScript 集成

对于大多数简单情况,你可以直接在构造函数中初始化组件。在必要时使用生命周期钩子如 onDidCreate() 来推迟工作。