# GridLazyForm

> _Since v0.11.0_

Directive that provides lazy FormGroup creation for grid editing.

Unlike `GridFormArray` which creates all FormGroups upfront, this directive
creates FormGroups on-demand only when a row enters edit mode. This provides
much better performance for large datasets while still enabling full
Angular Reactive Forms integration.

## Key Benefits

- **Performance**: Only creates FormGroups for rows being edited (20-100x fewer controls)
- **Same DX**: Editors still receive `control` in their context for validation
- **Memory efficient**: FormGroups are cleaned up when rows exit edit mode

## Usage

```typescript
import { Component, inject, signal } from '@angular/core';
import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
import { Grid, GridLazyForm, TbwEditor } from '@toolbox-web/grid-angular';

@Component({
  imports: [Grid, GridLazyForm, TbwEditor, ReactiveFormsModule],
  template: `
    <tbw-grid
      [rows]="employees()"
      [lazyForm]="createRowForm"
      [gridConfig]="config">

      <tbw-grid-column field="firstName">
        <input *tbwEditor="let _; control as ctrl"
               [formControl]="ctrl"
               [class.is-invalid]="ctrl?.invalid && ctrl?.touched" />
      </tbw-grid-column>
    </tbw-grid>
  `
})
export class MyComponent {
  private fb = inject(FormBuilder);
  employees = signal(generateEmployees(1000));

  // Factory called when editing starts - only include editable fields!
  createRowForm = (row: Employee): FormGroup => this.fb.group({
    firstName: [row.firstName, Validators.required],
    lastName: [row.lastName, Validators.minLength(2)],
    salary: [row.salary, [Validators.required, Validators.min(0)]],
  });

  gridConfig = { columns: [...] };
}
```

## How It Works

1. Rows come from `[rows]` input (plain data array)
2. When a cell enters edit mode, the FormGroup is created lazily
3. Editors receive the FormControl in their template context
4. On commit, FormGroup values are synced back to the row
5. FormGroup is cleaned up when the row exits edit mode (configurable)

## Performance Comparison

| Rows | GridFormArray (20 fields) | GridLazyForm |
|------|---------------------------|--------------|
| 100  | 2,000 controls           | ~20 controls |
| 500  | 10,000 controls          | ~20 controls |
| 1000 | 20,000 controls          | ~20 controls |

#### Example

```html
<tbw-grid [rows]="employees()" [lazyForm]="createRowForm" [gridConfig]="gridConfig" />
```

## Properties

| Property | Type | Description |
| -------- | ---- | ----------- |
| `lazyForm` | <code>InputSignal&lt;<a href="/grid/angular/api/types/lazyformfactory/">LazyFormFactory</a>&lt;TRow&gt;&gt;</code> | Factory function to create a FormGroup for a row. Called lazily when the row first enters edit mode. |
| `syncValidation` | <code>InputSignal&lt;boolean&gt;</code> | Whether to automatically sync Angular validation state to grid's visual invalid styling. |
| `keepFormGroups` | <code>InputSignal&lt;boolean&gt;</code> | Whether to keep FormGroups cached after a row exits edit mode. |
| `rowFormChange` | <code>OutputEmitterRef&lt;<a href="/grid/angular/api/types/rowformchangeevent/">RowFormChangeEvent</a>&lt;TRow&gt;&gt;</code> | Emitted when a row's form values change. Useful for auto-save, validation display, or syncing to external state. |

### Property Details

#### lazyForm

```typescript
createRowForm = (row: Employee): FormGroup => this.fb.group({
  firstName: [row.firstName, Validators.required],
  lastName: [row.lastName],
  salary: [row.salary, [Validators.min(0)]],
});
```

---

#### syncValidation

Whether to automatically sync Angular validation state to grid's visual invalid styling.

When enabled:
- After a cell commit, if the FormControl is invalid, the cell is marked with `setInvalid()`
- When a FormControl becomes valid, `clearInvalid()` is called
- On `row-commit`, if the row's FormGroup has invalid controls, the commit is prevented

**Default:** `true`

---

#### keepFormGroups

Whether to keep FormGroups cached after a row exits edit mode.

- `true`: FormGroups are kept, preserving dirty/touched state across edit sessions
- `false`: FormGroups are disposed when the row exits edit mode (default)

**Default:** `false`

---

## Methods

### ngOnInit()

A callback method that is invoked immediately after the
default change detector has checked the directive's
data-bound properties for the first time,
and before any of the view or content children have been checked.
It is invoked only once when the directive is instantiated.

```ts
ngOnInit(): void
```

***

### ngOnDestroy()

A callback method that performs custom clean-up, invoked immediately
before a directive, pipe, or service instance is destroyed.

```ts
ngOnDestroy(): void
```

***

### getFormGroup()

Gets the FormGroup for a row, if it exists.
Unlike the context methods, this does NOT create a FormGroup lazily.

```ts
getFormGroup(rowIndex: number): FormGroup<any> | undefined
```

#### Parameters

| Name | Type | Description |
| ---- | ---- | ----------- |
| `rowIndex` | <code>number</code> | The row index |

#### Returns

`FormGroup<any> | undefined` - The FormGroup or undefined

***

### getAllFormGroups()

Gets all cached FormGroups.
Useful for bulk validation or inspection.

```ts
getAllFormGroups(): ReadonlyMap<TRow, FormGroup<any>>
```

#### Returns

`ReadonlyMap<TRow, FormGroup<any>>` - Map of row objects to their FormGroups

***

### clearAllFormGroups()

Clears all cached FormGroups.
Useful when the underlying data changes significantly.

```ts
clearAllFormGroups(): void
```

***

### validateAll()

Validates all currently cached FormGroups.

```ts
validateAll(): boolean
```

#### Returns

`boolean` - true if all FormGroups are valid, false otherwise

***
