# Документация: FormArray в компоненте KnockPageComponent

## Обзор

В компоненте `KnockPageComponent` используется Angular FormArray для управления динамическими формами целей (targets) в режиме "form". Это позволяет пользователям добавлять, удалять и редактировать неограниченное количество целей для пропинывания портов.

## Архитектура FormArray

### 1. Структура данных

```typescript
form = this.fb.group({
  // ... другие поля
  targetForms: this.fb.array([])  // FormArray для динамических форм
});

get targetForms(): FormArray {
  return this.form.get('targetForms') as FormArray;
}
```

### 2. Структура отдельной формы цели

Каждая форма цели содержит следующие поля:

```typescript
private createTargetForm(): FormGroup {
  return this.fb.group({
    protocol: ['tcp', Validators.required],           // Протокол (TCP/UDP)
    host: ['127.0.0.1', Validators.required],          // IP адрес хоста
    port: [22, [Validators.required, Validators.min(1), Validators.max(65535)]], // Порт
    gateway: ['']                                      // Шлюз (опционально)
  });
}
```

## Основные методы работы с FormArray

### 1. Создание новой формы цели

```typescript
addTarget(): void {
  const newTargetForm = this.createTargetForm();
  this.targetForms.push(newTargetForm);
  this.serializeFormTargets();
}
```

**Что происходит:**

- Создается новая FormGroup с полями по умолчанию
- Форма добавляется в FormArray через `push()`
- Автоматически вызывается сериализация данных

### 2. Удаление формы цели

```typescript
removeTarget(index: number): void {
  if (this.targetForms.length > 1) {
    this.targetForms.removeAt(index);
    this.serializeFormTargets();
  }
}
```

**Особенности:**

- Защита от удаления последней формы (минимум 1 форма)
- Используется `removeAt(index)` для удаления по индексу
- Автоматическая сериализация после удаления

### 3. Сериализация данных форм

```typescript
private serializeFormTargets(): void {
  if (this.form.value.mode !== 'form') return;
  
  const targets: string[] = [];
  this.targetForms.controls.forEach(targetForm => {
    const value = targetForm.value;
    if (value.protocol && value.host && value.port) {
      let targetString = `${value.protocol}:${value.host}:${value.port}`;
      if (value.gateway && value.gateway.trim()) {
        targetString += `:${value.gateway.trim()}`;
      }
      targets.push(targetString);
    }
  });
  
  this.form.patchValue({ targets: targets.join(';') });
}
```

**Процесс сериализации:**

1. Проверка, что текущий режим - "form"
2. Итерация по всем формам в FormArray
3. Сборка строки в формате `protocol:host:port:gateway`
4. Объединение всех целей через `;`
5. Обновление поля `targets` в основной форме

## Интеграция с HTML шаблоном

### 1. Отображение форм

```html
<div class="form-targets-list">
  <div 
    *ngFor="let targetForm of targetForms.controls; let i = index" 
    class="form-target-item"
    [formGroup]="$any(targetForm)"
  >
    <!-- Поля формы -->
  </div>
</div>
```

**Ключевые моменты:**

- `*ngFor` итерируется по `targetForms.controls`
- `[formGroup]` связывает каждую форму с FormGroup
- `$any(targetForm)` решает проблему типизации TypeScript

### 2. Поля формы

```html
<div class="col-12 md:col-3">
  <label>Protocol</label>
  <p-dropdown
    formControlName="protocol"
    [options]="[
      { label: 'TCP', value: 'tcp' },
      { label: 'UDP', value: 'udp' }
    ]"
    optionLabel="label"
    optionValue="value"
    class="w-full"
  ></p-dropdown>
</div>
```

## Автоматическое сохранение и восстановление

### 1. Подписка на изменения

```typescript
private setupAutoSave() {
  // Подписка на изменения в формах целей
  this.targetForms.valueChanges.subscribe(() => {
    if (this.form.value.mode === 'form') {
      this.serializeFormTargets();
      setTimeout(() => this.saveStateToLocalStorage(), 300);
    }
  });
}
```

### 2. Сохранение в localStorage

```typescript
private saveStateToLocalStorage() {
  const state: any = {
    // ... другие поля
  };

  // Сохраняем данные форм целей для режима form
  if (formValue.mode === 'form' && this.targetForms.length > 0) {
    state.targetForms = this.targetForms.value;
  }
  
  localStorage.setItem(this.STORAGE_KEY, JSON.stringify(state));
}
```

### 3. Восстановление из localStorage

```typescript
private loadStateFromLocalStorage() {
  // ... загрузка других полей

  // Загружаем сохраненные формы целей для режима form
  if (state.mode === 'form' && state.targetForms && Array.isArray(state.targetForms)) {
    this.targetForms.clear();
    state.targetForms.forEach((targetData: any) => {
      const targetForm = this.fb.group({
        protocol: [targetData.protocol || 'tcp', Validators.required],
        host: [targetData.host || '127.0.0.1', Validators.required],
        port: [targetData.port || 22, [Validators.required, Validators.min(1), Validators.max(65535)]],
        gateway: [targetData.gateway || '']
      });
      this.targetForms.push(targetForm);
    });
  }
}
```

## Преобразование между режимами

### 1. Конвертация в режим Form

```typescript
private convertInlineToForm() {
  const targetsString = this.form.value.targets || '';
  const targets = targetsString.split(';').filter(t => t.trim());
  
  targets.forEach(target => {
    const parts = target.trim().split(':');
    if (parts.length >= 3) {
      const targetForm = this.fb.group({
        protocol: [parts[0] || 'tcp', Validators.required],
        host: [parts[1] || '127.0.0.1', Validators.required],
        port: [parseInt(parts[2]) || 22, [Validators.required, Validators.min(1), Validators.max(65535)]],
        gateway: [parts[3] || '']
      });
      this.targetForms.push(targetForm);
    }
  });
}
```

### 2. Конвертация из режима Form

```typescript
private handleModeChangeFromForm(previousMode: string, newMode: string) {
  // Сначала сериализуем данные формы
  this.serializeFormTargets();
  
  if (newMode === 'inline') {
    // Данные уже в targets, ничего дополнительно не нужно
  } else if (newMode === 'yaml') {
    this.convertInlineToYaml();
  }
}
```

## Жизненный цикл FormArray

### 1. Инициализация

```typescript
private initializeFormMode(): void {
  // Если нет форм целей, создаем одну по умолчанию
  if (this.targetForms.length === 0) {
    this.addTarget();
  }
}
```

### 2. Очистка при смене режима

```typescript
private handleModeChangeToForm(previousMode: string) {
  // Очищаем существующие формы
  this.targetForms.clear();
  
  // Конвертируем данные из предыдущего режима
  if (previousMode === 'inline') {
    this.convertInlineToForm();
  } else if (previousMode === 'yaml') {
    this.convertYamlToForm();
  }
  
  // Инициализируем формы если их нет
  if (this.targetForms.length === 0) {
    this.addTarget();
  }
}
```

## Валидация

### 1. Валидация полей формы

```typescript
private createTargetForm(): FormGroup {
  return this.fb.group({
    protocol: ['tcp', Validators.required],
    host: ['127.0.0.1', Validators.required],
    port: [22, [Validators.required, Validators.min(1), Validators.max(65535)]],
    gateway: ['']
  });
}
```

### 2. Защита от удаления всех форм

```typescript
removeTarget(index: number): void {
  if (this.targetForms.length > 1) {
    this.targetForms.removeAt(index);
    this.serializeFormTargets();
  }
}
```

## Преимущества использования FormArray

1. **Динамичность**: Возможность добавления/удаления форм в runtime
2. **Валидация**: Встроенная валидация для каждой формы
3. **Реактивность**: Автоматическое обновление UI при изменениях
4. **Типобезопасность**: TypeScript поддержка
5. **Интеграция**: Легкая интеграция с Angular Reactive Forms
6. **Сериализация**: Простое преобразование в различные форматы

## Потенциальные проблемы и решения

### 1. Проблема типизации в шаблоне

**Проблема:**
```html
[formGroup]="targetForm"  <!-- TypeScript ошибка -->
```

**Решение:**
```html
[formGroup]="$any(targetForm)"  <!-- Приведение типа -->
```

### 2. Защита от пустого FormArray

**Проблема:** Пользователь может удалить все формы

**Решение:**
```typescript
removeTarget(index: number): void {
  if (this.targetForms.length > 1) {  // Минимум 1 форма
    this.targetForms.removeAt(index);
  }
}
```

### 3. Производительность при большом количестве форм

**Проблема:** Много форм может замедлить приложение

**Решение:** Виртуализация или пагинация (не реализовано в текущей версии)

## Заключение

FormArray в данном компоненте обеспечивает гибкую и мощную систему управления динамическими формами. Реализация включает:

- Полный жизненный цикл форм (создание, редактирование, удаление)
- Автоматическую сериализацию/десериализацию
- Интеграцию с системой режимов
- Сохранение состояния в localStorage
- Валидацию и защиту от некорректных данных

Этот подход делает компонент удобным для пользователей и легко расширяемым для разработчиков.
