npm package: @flexzap/form
Description: All the form components that makes part of the flexzap library
Author: flexzap
Latest version: 1.3.0

# @flexzap/form

Comprehensive form components and services for Angular applications, providing reusable form controls, validation utilities, and form management solutions. Part of the FlexZap Library ecosystem.

## Installation

```bash
npm install @flexzap/form
```

### Peer Dependencies

This library requires the following peer dependencies:

```bash
npm install @angular/common@^21.1.0 @angular/core@^21.1.0
```

## Usage

### 1. Native HTML Validation (Simple)

This approach relies on standard HTML attributes like `required`, `minlength`, `maxlength`, and `pattern`. The form automatically detects these constraints.

```typescript
import { Component } from '@angular/core';
import { ZapFormFieldset, ZapFormGroup, ZapFormField, ZapFormInput, ZapFormRow } from '@flexzap/form';
import { ZapButton } from '@flexzap/buttons';

@Component({
  imports: [ZapFormFieldset, ZapFormGroup, ZapFormField, ZapFormInput, ZapFormRow, ZapButton],
  template: `
    <!-- Validation happens on 'blur' (when user leaves the field) -->
    <form ZapFormGroup #myForm="ZapForm" [ZapFormValidationStrategy]="'blur'" (ZapFormSubmit)="onSubmit($event)">
      <zap-form-fieldset [title]="'User Info'" [description]="'Enter your basic details.'">
        <zap-form-row>
          <zap-form-field [label]="'Username'">
            <input ZapFormInput type="text" name="username" required minlength="3" />
          </zap-form-field>
        </zap-form-row>

        <zap-form-row>
          <zap-form-field [label]="'Email'">
            <input ZapFormInput type="email" name="email" required />
          </zap-form-field>
        </zap-form-row>
      </zap-form-fieldset>

      <zap-button [type]="'submit'" [disabled]="!myForm.isFormValid()"> Submit </zap-button>
    </form>
  `
})
export class SimpleFormComponent {
  onSubmit(data: Record<string, FormDataEntryValue | FormDataEntryValue[]>) {
    console.log(JSON.stringify(data, null, 2));
  }
}
```

### 2. Custom Validators (Advanced)

Use `ZapFormValidators` for more complex logic or when you need custom error messages. Custom validators take precedence over native attributes.

```typescript
import { Component } from '@angular/core';
import {
  ZapFormFieldset,
  ZapFormGroup,
  ZapFormField,
  ZapFormInput,
  ZapFormRow,
  ZapFormValidators
} from '@flexzap/form';
import { ZapButton } from '@flexzap/buttons';

@Component({
  imports: [ZapFormFieldset, ZapFormGroup, ZapFormField, ZapFormInput, ZapFormRow, ZapButton],
  template: `
    <!-- Validation happens only on 'submit' -->
    <form ZapFormGroup #myForm="ZapForm" [ZapFormValidationStrategy]="'submit'" (ZapFormSubmit)="onSubmit($event)">
      <zap-form-fieldset [title]="'Security'" [description]="'Setup your security credentials.'">
        <zap-form-row>
          <zap-form-field
            [label]="'Password'"
            [validators]="[
              validators.required('Password is mandatory'),
              validators.minLength(8, 'Password must be at least 8 chars')
            ]"
          >
            <input ZapFormInput type="password" name="password" />
          </zap-form-field>
        </zap-form-row>

        <zap-form-row>
          <zap-form-field
            [label]="'Custom Code'"
            [validators]="[validators.pattern(/^[A-Z]{3}-\\d{3}$/, 'Format must be ABC-123')]"
          >
            <input ZapFormInput type="text" name="code" />
          </zap-form-field>
        </zap-form-row>
      </zap-form-fieldset>

      <zap-button [type]="'submit'" [disabled]="!myForm.isFormValid()"> Submit </zap-button>
    </form>
  `
})
export class AdvancedFormComponent {
  readonly validators = ZapFormValidators;

  onSubmit(data: Record<string, FormDataEntryValue | FormDataEntryValue[]>) {
    console.log(JSON.stringify(data, null, 2));
  }
}
```

### 3. Selection Controls

Work with radio buttons and checkboxes using the dedicated components `zap-form-radio` and `zap-form-checkbox`.

```typescript
import { Component } from '@angular/core';
import {
  ZapFormFieldset,
  ZapFormGroup,
  ZapFormField,
  ZapFormRadio,
  ZapFormCheckbox,
  ZapFormRow,
  ZapFormInput,
  ZapFormValidators
} from '@flexzap/form';
import { ZapButton } from '@flexzap/buttons';

@Component({
  imports: [
    ZapFormFieldset,
    ZapFormGroup,
    ZapFormField,
    ZapFormRadio,
    ZapFormCheckbox,
    ZapFormRow,
    ZapFormInput,
    ZapButton
  ],
  template: `
    <form ZapFormGroup #myForm="ZapForm" (ZapFormSubmit)="onSubmit($event)">
      <zap-form-fieldset [title]="'Preferences'" [description]="'Choose your settings.'">
        <zap-form-row>
          <zap-form-field [label]="'Notifications'" [validators]="[validators.required('Please select an option')]">
            <zap-form-radio [label]="'Email'">
              <input ZapFormInput type="radio" name="notifications" value="email" />
            </zap-form-radio>
            <zap-form-radio [label]="'SMS'">
              <input ZapFormInput type="radio" name="notifications" value="sms" />
            </zap-form-radio>
          </zap-form-field>
        </zap-form-row>

        <zap-form-row>
          <zap-form-field [label]="'Terms'">
            <zap-form-checkbox [label]="'I agree to terms'">
              <input ZapFormInput type="checkbox" name="terms" required />
            </zap-form-checkbox>
          </zap-form-field>
        </zap-form-row>
      </zap-form-fieldset>

      <zap-button [type]="'submit'" [disabled]="!myForm.isFormValid()"> Submit </zap-button>
    </form>
  `
})
export class SelectionFormComponent {
  readonly validators = ZapFormValidators;

  onSubmit(data: Record<string, FormDataEntryValue | FormDataEntryValue[]>) {
    console.log(JSON.stringify(data, null, 2));
  }
}
```

## API Reference

### ZapFormGroup

A directive that manages the state of a form, including validation and submission.

#### Inputs

| Property                    | Type                                       | Default    | Description                                 |
| --------------------------- | ------------------------------------------ | ---------- | ------------------------------------------- |
| `ZapFormValidationStrategy` | `'blur' \| 'change' \| 'submit' \| 'none'` | `'submit'` | Determines when validation errors are shown |

#### Outputs

| Event           | Type                                                         | Description                                  |
| --------------- | ------------------------------------------------------------ | -------------------------------------------- |
| `ZapFormSubmit` | `Record<string, FormDataEntryValue \| FormDataEntryValue[]>` | Emitted when the form is valid and submitted |

#### Properties

| Property      | Type                      | Description                                            |
| ------------- | ------------------------- | ------------------------------------------------------ |
| `isFormValid` | `Signal<boolean>`         | Computed signal indicating if the entire form is valid |
| `onReset`     | `(event?: Event) => void` | Method to reset the form state and controls            |

### ZapFormFieldset

A structural component to group related form fields with a title and optional description.

#### Inputs

| Property      | Type     | Default     | Description                    |
| ------------- | -------- | ----------- | ------------------------------ |
| `title`       | `string` | Required    | The main title of the fieldset |
| `description` | `string` | `undefined` | Optional description text      |

### ZapFormRow

A layout component used to wrap form fields for consistent spacing and alignment.

#### Usage

```html
<zap-form-row>
  <zap-form-field [label]="'Name'">...</zap-form-field>
</zap-form-row>
```

### Data Normalization

The `ZapFormGroup` automatically normalizes form data output to ensure consistency:

- **Missing Fields**: Fields that are typically excluded from `FormData` when unchecked (like radio buttons and checkboxes) are automatically included in the output with an empty string value (`""`).
- **Consistency**: This ensures that every registered control in your form has a corresponding key in the submitted data object, matching the behavior of empty text inputs.

### ZapFormField

A component that wraps a form input and handles label, validation messages, and error states.

#### Inputs

| Property     | Type                   | Default     | Description                                     |
| ------------ | ---------------------- | ----------- | ----------------------------------------------- |
| `label`      | `string`               | Required    | The label text for the control                  |
| `message`    | `string`               | `undefined` | Helper text to display when there are no errors |
| `validators` | `ZapFormValidatorFn[]` | `[]`        | Array of custom validator functions             |

### ZapFormInput

A directive applied to native input elements to integrate them with the FlexZap form system.

#### Usage

```html
<input ZapFormInput type="text" name="username" required />
```

### ZapFormRadio

A component to style and wrap a native radio input.

#### Inputs

| Property | Type     | Default  | Description                  |
| -------- | -------- | -------- | ---------------------------- |
| `label`  | `string` | Required | The label text for the radio |

### ZapFormCheckbox

A component to style and wrap a native checkbox input.

#### Inputs

| Property | Type     | Default  | Description                     |
| -------- | -------- | -------- | ------------------------------- |
| `label`  | `string` | Required | The label text for the checkbox |

### ZapFormControl

An interface that all form controls (like `ZapFormField`) implement to participate in the `ZapFormGroup` validation and state management.

#### Interface

```typescript
interface ZapFormControl {
  validate(showError?: boolean): boolean;
  reset(): void;
  readonly controlNames?: () => string[];
}
```

### ZapFormValidators

A utility class providing common validator functions.

- `required(errorMessage?)`: Checks if value is present
- `minLength(length, errorMessage?)`: Checks minimum length
- `maxLength(length, errorMessage?)`: Checks maximum length
- `pattern(regex, errorMessage?)`: Checks against a regex pattern

## Testing

This library uses Jest for unit testing with zoneless Angular configuration.

### Running Tests

```bash
# From the monorepo root
npm run form:test            # Run all unit tests with coverage
npm run form:test:watch      # Run tests in watch mode (no coverage)
```

### Test Configuration

- **Framework**: Jest with jest-preset-angular
- **Environment**: jsdom
- **Configuration**: Zoneless Angular (mandatory)
- **Coverage**: Reports generated at `coverage/flexzap/form/`

## Development

### Building the Library

```bash
# From the monorepo root
npm run form:build       # Build directly
ng build @flexzap/form   # Build using Angular CLI
```

### Code Scaffolding

To generate new components within this library:

```bash
ng generate component [component-name] --project @flexzap/form
```

## Publishing

### Build for Publication

```bash
# From the monorepo root
npm run form:build
```

### Publish to NPM

```bash
cd dist/flexzap/form
npm publish --access public
```

## Contributing

This library is part of the FlexZap Library monorepo. Please refer to the [main repository](../../../README.md) for contribution guidelines.

### Development Guidelines

- Use standalone components (default behavior)
- Use `input()` and `output()` functions instead of decorators
- Set `changeDetection: ChangeDetectionStrategy.OnPush`
- Write comprehensive tests with Jest (zoneless configuration)
- Follow semantic versioning for releases

## License

MIT License - see the [LICENSE](../../../LICENSE) file for details.

## Links

- **Homepage**: [https://www.flexzap.dev](https://www.flexzap.dev)
- **Repository**: [https://github.com/vitorazevedo/flexzap-library](https://github.com/vitorazevedo/flexzap-library)
- **Monorepo Documentation**: [Main README](../../../README.md)