angular / angular/angular

Support Directive Composition (`hostDirectives`) for `FormField`

未關閉
#67,982 8 則留言 1 個 reaction 已指派 0 人 在 GitHub 檢視
area: forms gemini-triaged
主要語言
TypeScript
星號
101k
分支
27.5k
平均合併
1 天 19 小時
30 天內合併 PR
288

描述

### Which @angular/* package(s) are relevant/related to the feature request?

forms

### Description

I'd like to use Angular's [Directive Composition API](https://angular.dev/guide/directives/directive-composition-api) (`hostDirectives`) to compose `FormField` into a custom directive. Since `FormField` is a standalone directive with a standard `InputSignal`, this seems like it should be a supported use case — but it currently doesn't work.

### Use case

A common pattern when building accessible forms is to create a reusable directive that manages ARIA attributes (`aria-invalid`, `aria-busy`, `aria-describedby`, `aria-errormessage`) based on the field's validation state. Ideally, this directive would compose `FormField` via `hostDirectives` so that consumers only need a single binding per input element instead of two.

### Desired behavior

The following directive composes `FormField` via `hostDirectives` and forwards its input through an alias:

```ts
import { computed, Directive, input } from '@angular/core';
import { FieldTree, FormField } from '@angular/forms/signals';

@Directive({
selector: '[formFieldAria]',
host: {
'[aria-invalid]': 'ariaInvalid()',
'[aria-busy]': 'ariaBusy()',
'[aria-describedby]': 'ariaDescribedBy()',
'[aria-errormessage]': 'ariaErrorMessage()',
},
hostDirectives: [
{ directive: FormField, inputs: ['formField: formFieldAria'] },
],
})
export class FormFieldAria {
readonly formFieldAria = input.required>();
readonly fieldDescriptionId = input();

readonly ariaInvalid = computed(() => {
const state = this.formFieldAria()();
return state.touched() && !state.pending()
? state.errors().length > 0
: undefined;
});

readonly ariaBusy = computed(() => {
const state = this.formFieldAria()();
return state.pending();
});

readonly ariaDescribedBy = computed(() => {
const id = this.fieldDescriptionId();
return !id || this.ariaInvalid() ? null : id;
});

readonly ariaErrorMessage = computed(() => {
const id = this.fieldDescriptionId();
return !id || !this.ariaInvalid() ? null : id;
});
}
```

This would allow a clean single-binding usage in templates:

```html

```

### Current behavior

The composition does not work. `FormField` is not correctly applied to the host element when used via `hostDirectives`, so the form control registration and two-way binding that `FormField` normally provides do not take effect.

### Current workaround

Both directives must be applied separately, which means duplicating the field binding on every input:

```html

```

This works but is verbose and error-prone, especially in larger forms.

## Minimal Reproduction

[StackBlitz](https://stackblitz.com/edit/signal-forms-directive-composition-demo?file=src%2Fapp%2Fform-field-aria.ts)

### Steps to reproduce

1. Open the StackBlitz link above
2. The `FormFieldAria` directive attempts to use `hostDirectives` with `FormField`
3. The form field binding does not take effect — the input is not registered with the form

## Environment

```text
Angular version: 22.0.0-next.6
```

## Additional context

I understand that Signal Forms is still experimental. `FormField` is standalone and uses a standard `InputSignal`, so from the outside it looks like it should be compatible with `hostDirectives`.
Supporting this would make it much easier to build reusable, accessible form field wrappers — which feels like a natural fit for the Directive Composition API.

### Proposed solution

It should be possible to compose the `FormField` with a custom directive, so I only have to apply one directive and pass the form field input binding.

### Alternatives considered

none

貢獻指南

開啟貢獻指南

研究方向

先執行連結的 StackBlitz 重現並閱讀 src/app/form-field-aria.ts,接著追蹤 FormField 如何透過 hostDirectives 套用。完成的條件是組合後的 FormField 會註冊 input 並提供與直接使用相同的雙向繫結,同時別名繫結能與單一 formFieldAria 指令搭配運作。

由索引模型根據 Issue 內容生成。

評估

技術堆疊
angular, typescript
領域
frontend
Issue 類型
功能
難度
4/5
預估耗時
3-5 天
活躍度
活躍
描述清晰度
基本清楚
新手友好度
48/100

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。