reference-signal-forms

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Signal Forms Architecture

Signal表单架构

The
packages/forms/signals
directory contains an experimental, signal-based forms API for Angular. This system differs significantly from the existing Reactive and Template-driven forms.
packages/forms/signals
目录包含一个针对Angular的实验性、基于Signal的表单API。该系统与现有的响应式表单和模板驱动表单存在显著差异。

Mental Model

思维模型

  1. Model-Driven: The system is built around a
    WritableSignal<T>
    which serves as the single source of truth. Unlike Reactive Forms where the
    FormControl
    holds the value, here the
    Signal
    holds the value. The form is merely a view or projection of that signal, adding form-specific state (validity, dirty, touched).
  2. Proxy-Based Traversal: The form API (
    form(signal)
    ) returns a
    FieldTree
    . This object is a Proxy. It allows accessing nested fields (e.g.,
    myForm.user.name
    ) without manually creating control groups. Accessing a property on the proxy lazily resolves or creates the corresponding
    FieldNode
    .
  3. Schema-Based Logic: Validation, disabled state, and other metadata are defined separately via Schemas. Schemas are applied to the form structure using functions like
    apply
    ,
    applyEach
    (for arrays), and
    applyWhen
    . This separates the structure of the data from the rules governing it.
  4. Directives as Glue: The
    [formField]
    directive binds a DOM element (native input or custom control) to a
    FieldNode
    . It handles:
    • Syncing the value between the DOM and the Signal.
    • Reflecting state (valid, touched, etc.) to the UI.
    • Handling user interaction events (blur, input).
  1. 模型驱动:该系统围绕
    WritableSignal<T>
    构建,它作为单一数据源。 与响应式表单中由
    FormControl
    存储值不同,此处由
    Signal
    存储值。表单仅仅是该Signal的一个_视图_或_投影_,添加了表单特有的状态(有效性、脏值、已触碰)。
  2. 基于代理的遍历:表单API(
    form(signal)
    )返回一个
    FieldTree
    对象,这是一个Proxy(代理)。 它允许访问嵌套字段(例如
    myForm.user.name
    ),而无需手动创建控件组。 访问代理上的属性会惰性解析或创建对应的
    FieldNode
  3. 基于Schema的逻辑:验证规则、禁用状态和其他元数据通过Schema单独定义。 Schema通过
    apply
    applyEach
    (针对数组)和
    applyWhen
    等函数应用到表单结构上。 这将数据的_结构_与管理它的_规则_分离开来。
  4. 指令作为桥梁
    [formField]
    指令将DOM元素(原生输入框或自定义控件)与
    FieldNode
    绑定。 它负责:
    • 同步DOM与Signal之间的值。
    • 将状态(有效、已触碰等)反映到UI中。
    • 处理用户交互事件(失焦、输入)。

Key Components

核心组件

1.
FieldNode
(
src/field/node.ts
)

1.
FieldNode
src/field/node.ts

The central internal class representing a single field in the form graph. It aggregates several state managers:
  • structure
    : Manages parent/child relationships and signal slicing.
  • validationState
    : Computes
    valid
    ,
    invalid
    ,
    errors
    signals.
  • nodeState
    : Tracks
    touched
    ,
    dirty
    ,
    pristine
    .
  • metadataState
    : Stores metadata like
    min
    ,
    max
    ,
    required
    .
  • submitState
    : Tracks submission status and server errors.
代表表单图中单个字段的核心内部类。它聚合了多个状态管理器:
  • structure
    :管理父子关系和Signal切片。
  • validationState
    :计算
    valid
    invalid
    errors
    信号。
  • nodeState
    :跟踪
    touched
    dirty
    pristine
    状态。
  • metadataState
    :存储
    min
    max
    required
    等元数据。
  • submitState
    :跟踪提交状态和服务器错误。

2.
ValidationState
(
src/field/validation.ts
)

2.
ValidationState
src/field/validation.ts

Manages the complexity of validation:
  • Synchronous Errors: Derived from schema rules.
  • Asynchronous Errors: Handled via signals, including 'pending' states.
  • Tree Errors: Errors that bubble up or are targeted at specific fields.
  • Submission Errors: Server-side errors injected imperatively via
    submit()
    .
管理验证的复杂性:
  • 同步错误:源自Schema规则。
  • 异步错误:通过Signal处理,包括“pending(待处理)”状态。
  • 树形错误:向上冒泡或针对特定字段的错误。
  • 提交错误:通过
    submit()
    命令式注入的服务器端错误。

3.
FormField
Directive (
src/directive/form_field_directive.ts
)

3.
FormField
指令(
src/directive/form_field_directive.ts

The bridge between the
FieldNode
and the DOM.
  • Selector:
    [formField]
  • It supports:
    • Native Elements:
      <input>
      ,
      <select>
      ,
      <textarea>
      .
    • Custom Controls: Components implementing
      FormUiControl
      or
      FormValueControl
      .
    • Legacy Interop: Components implementing
      ControlValueAccessor
      (via
      InteropNgControl
      ).
FieldNode
与DOM之间的桥梁。
  • 选择器:
    [formField]
  • 支持:
    • 原生元素
      <input>
      <select>
      <textarea>
    • 自定义控件:实现
      FormUiControl
      FormValueControl
      接口的组件。
    • 遗留兼容:实现
      ControlValueAccessor
      的组件(通过
      InteropNgControl
      )。

4.
Schema
(
src/api/structure.ts
&
src/api/rules
)

4.
Schema
src/api/structure.ts
&
src/api/rules

Defines the behavior.
  • Created via
    schema(fn)
    .
  • Applied via
    apply(path, schema)
    .
  • Rules include validators (
    required
    ,
    pattern
    ,
    min
    ,
    max
    ) and state modifiers (
    disabled
    ,
    hidden
    ).
定义行为规则。
  • 通过
    schema(fn)
    创建。
  • 通过
    apply(path, schema)
    应用。
  • 规则包括验证器(
    required
    pattern
    min
    max
    )和状态修改器(
    disabled
    hidden
    )。

Data Flow

数据流

  1. Read:
    form.field.value()
    reads directly from the underlying signal (projected to the specific path).
  2. Write: Writing to the form (e.g., via UI) updates the underlying signal.
  3. Validation: A computed effect observes the value signal and runs validators defined in the schema.
  1. 读取
    form.field.value()
    直接从底层Signal读取(投影到特定路径)。
  2. 写入:向表单写入数据(例如通过UI)会更新底层Signal。
  3. 验证:一个计算效果会监听值Signal,并运行Schema中定义的验证器。

Usage Example (Conceptual)

使用示例(概念性)

typescript
// 1. Define Model
const user = signal({name: '', age: 0});

// 2. Define Schema
const userRules = schema((u) => {
  required(u.name);
  min(u.age, 18);
});

// 3. Create Form
const userForm = form(user, userRules); // OR apply(userForm, userRules)

// 4. Bind in Template
// <input [formField]="userForm.name">
typescript
// 1. 定义模型
const user = signal({name: '', age: 0});

// 2. 定义Schema
const userRules = schema((u) => {
  required(u.name);
  min(u.age, 18);
});

// 3. 创建表单
const userForm = form(user, userRules); // 或 apply(userForm, userRules)

// 4. 在模板中绑定
// <input [formField]="userForm.name">

Important Files

重要文件

  • packages/forms/signals/src/api/structure.ts
    : Public API entry points (
    form
    ,
    apply
    ).
  • packages/forms/signals/src/api/control.ts
    : Interfaces for custom controls (
    FormUiControl
    ).
  • packages/forms/signals/src/field/node.ts
    : The
    FieldNode
    implementation.
  • packages/forms/signals/src/directive/form_field_directive.ts
    : The
    [formField]
    directive.
  • packages/forms/signals/src/api/structure.ts
    :公共API入口(
    form
    apply
    )。
  • packages/forms/signals/src/api/control.ts
    :自定义控件的接口(
    FormUiControl
    )。
  • packages/forms/signals/src/field/node.ts
    FieldNode
    的实现。
  • packages/forms/signals/src/directive/form_field_directive.ts
    [formField]
    指令。

Supplemental Information

补充信息

  • Compiler & Core Integration: Details how
    [formField]
    hooks into type-checking and the runtime.
  • 编译器与核心集成:详细介绍
    [formField]
    如何接入类型检查和运行时。