Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions adev/src/content/guide/forms/signals/form-logic.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,45 @@ export class Profile {
}
```

### Always hidden

To make a field permanently hidden, call `hidden()` with just the field path:

```angular-ts
import {Component, signal} from '@angular/core';
import {form, FormField, hidden} from '@angular/forms/signals';

@Component({
selector: 'app-profile',
imports: [FormField],
template: `
<label>
<input type="checkbox" [formField]="profileForm.isPublic" />
Make profile public
</label>

<!-- The publicUrl is permanently hidden, so this block never renders -->
@if (!profileForm.publicUrl().hidden()) {
<label>
Public URL
<input [formField]="profileForm.publicUrl" />
</label>
}
`,
})
export class Profile {
profileModel = signal({
isPublic: false,
publicUrl: '',
});

profileForm = form(this.profileModel, (schemaPath) => {
// This field is now permanently hidden and excluded from active validation
hidden(schemaPath.publicUrl);
});
}
```

## Display uneditable fields with `readonly()`

The `readonly()` rule prevents users from updating a field. The `[FormField]` directive automatically binds this state to the HTML `readonly` attribute, which prevents editing while still allowing users to focus and select text.
Expand Down
6 changes: 3 additions & 3 deletions goldens/public-api/forms/signals/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,12 +274,12 @@ export interface FormValueControl<TValue> extends FormUiControl<TValue> {
}

// @public
export function hidden<TValue, TPathKind extends PathKind = PathKind.Root>(path: SchemaPath<TValue, SchemaPathRules.Supported, TPathKind>, config: {
when: NoInfer<LogicFn<TValue, boolean, TPathKind>>;
export function hidden<TValue, TPathKind extends PathKind = PathKind.Root>(path: SchemaPath<TValue, SchemaPathRules.Supported, TPathKind>, config?: {
when?: NoInfer<LogicFn<TValue, boolean, TPathKind>>;
}): void;

// @public @deprecated
export function hidden<TValue, TPathKind extends PathKind = PathKind.Root>(path: SchemaPath<TValue, SchemaPathRules.Supported, TPathKind>, logic: NoInfer<LogicFn<TValue, boolean, TPathKind>>): void;
export function hidden<TValue, TPathKind extends PathKind = PathKind.Root>(path: SchemaPath<TValue, SchemaPathRules.Supported, TPathKind>, logic?: NoInfer<LogicFn<TValue, boolean, TPathKind>>): void;

// @public
export interface HttpValidatorOptions<TValue, TResult, TPathKind extends PathKind = PathKind.Root> {
Expand Down
11 changes: 6 additions & 5 deletions packages/forms/signals/src/api/rules/hidden.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import type {LogicFn, PathKind, SchemaPath, SchemaPathRules} from '../types';
*/
export function hidden<TValue, TPathKind extends PathKind = PathKind.Root>(
path: SchemaPath<TValue, SchemaPathRules.Supported, TPathKind>,
config: {when: NoInfer<LogicFn<TValue, boolean, TPathKind>>},
config?: {when?: NoInfer<LogicFn<TValue, boolean, TPathKind>>},
): void;

/**
Expand All @@ -46,20 +46,21 @@ export function hidden<TValue, TPathKind extends PathKind = PathKind.Root>(
*/
export function hidden<TValue, TPathKind extends PathKind = PathKind.Root>(
path: SchemaPath<TValue, SchemaPathRules.Supported, TPathKind>,
logic: NoInfer<LogicFn<TValue, boolean, TPathKind>>,
logic?: NoInfer<LogicFn<TValue, boolean, TPathKind>>,
): void;

export function hidden<TValue, TPathKind extends PathKind = PathKind.Root>(
path: SchemaPath<TValue, SchemaPathRules.Supported, TPathKind>,
configOrLogic:
| {when: NoInfer<LogicFn<TValue, boolean, TPathKind>>}
configOrLogic?:
| {when?: NoInfer<LogicFn<TValue, boolean, TPathKind>>}
| NoInfer<LogicFn<TValue, boolean, TPathKind>>,
): void {
assertPathIsCurrent(path);

const pathNode = FieldPathNode.unwrapFieldPath(path);

const logic = typeof configOrLogic === 'function' ? configOrLogic : configOrLogic.when;
const logic =
typeof configOrLogic === 'function' ? configOrLogic : (configOrLogic?.when ?? (() => true));

pathNode.builder.addHiddenRule(logic);
}
16 changes: 16 additions & 0 deletions packages/forms/signals/test/node/api/hidden.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,20 @@ describe('hidden', () => {
f.name().value.set('hidden-cat');
expect(f.name().hidden()).toBe(true);
});

it('it returns true when configOrLogic is omitted', () => {
const cat = signal({name: 'Pirojok-the-cat', age: 5});
const f = form(
cat,
(p) => {
hidden(p.name);
},
{injector: TestBed.inject(Injector)},
);

expect(f.name().hidden()).withContext('Name is permanently hidden').toBeTrue();

f.name().value.set('some-other-cat');
expect(f.name().hidden()).toBeTrue();
});
});
Loading