Enable dynamic schema composition based on form state
- Dominant language
- TypeScript
- Stars
- 101k
- Forks
- 27.5k
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 288
Description
### Which @angular/* package(s) are relevant/related to the feature request?
_No response_
---
### Description
I have a form with different properties.
```ts
export interface Field {
fieldName: string;
fieldType?: string;
fieldRequired?: boolean;
fieldMaxSize?: number;
fieldVisible?: boolean;
}
export interface FieldConfig {
fields: Field[];
}
export interface Order {
label: string;
// ...
}
export interface ActivityType {
label: string;
fieldConfig: FieldConfig;
}
export function fieldConfigSchema(fieldConfig: FieldConfig) {
return schema(path => {
for (const field of fieldConfig.fields) {
required(path[field.fieldName], { when: () => field.fieldRequired });
hidden(path[field.fieldName], () => !field.fieldVisible);
maxLength(path[field.fieldName], () => field.fieldMaxSize);
}
});
}
export interface FormModel {
user: User;
activityType: ActivityType;
order: Order;
}
```
The form is defined as follows:
```ts
formModel = signal({
user: null,
activityType: null,
order: null
});
form = form(this.formModel, path => {
applyWhen(
path.order,
context => !!context.valueOf(path.activityType)?.fieldConfig,
fieldConfigSchema(
/* value of fieldConfig from activityType not available here */
)
);
});
```
The issue is that `fieldConfigSchema` needs access to the `fieldConfig`
coming from the selected `activityType`, but that value is only available
through the form context.
---
### Proposed solution
Allow building a schema dynamically based on the form context:
```ts
applyWhen(
path.order,
context => !!context.valueOf(path.activityType)?.fieldConfig,
context =>
fieldConfigSchema(
context.valueOf(path.activityType).fieldConfig
)
);
```
This would allow the schema to be constructed using runtime values from
the form context.
---
### Alternatives considered
```ts
export function fieldConfigSchema(fieldConfig: Signal) {
return schema(path => {
for (const field of fieldConfig().fields) {
required(path[field.fieldName], { when: () => field.fieldRequired });
hidden(path[field.fieldName], () => !field.fieldVisible);
maxLength(path[field.fieldName], () => field.fieldMaxSize);
}
});
}
form = form(this.formModel, path => {
applyWhen(
path.order,
context => !!context.valueOf(path.activityType)?.fieldConfig,
fieldConfigSchema(this.fieldConfig)
);
});
fieldConfig = computed(() => this.form.activityType().value()?.fieldConfig);
```
Contributor guide
Research direction
Start by tracing the form, applyWhen, schema, and context.valueOf APIs to understand how schemas are currently constructed and when form context is available. Done means a schema can be built from runtime form context, with coverage for the activityType fieldConfig example and the existing behavior preserved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- angular, typescript
- Domain
- frontend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100