Zadanie101

Angular
Priorytet: Normalny Szkic

Zadanie 101: Dynamiczne Komponenty Formularzy (Input, Select, Checkbox)

Wstęp

Kontynuując temat reużywalności (rozpoczęty w Zadaniu 100), zajmiemy się tworzeniem dynamicznych elementów formularzy. Formularze są nieodłączną częścią aplikacji webowych, a standardowe inputy HTML często wymagają powtarzania tego samego kodu (etykiety, klasy walidacyjne, style Bootstrap).

W tym zadaniu stworzymy zestaw komponentów formularzowych, które będą konfigurowalne przez parametry @Input.


Część 1: Komponent Input (custom-input)

Krok 1: Generowanie komponentu

ng generate component custom-input

Krok 2: Definiowanie parametrów

Edytuj custom-input.component.ts. Chcemy przekazywać:

  • label (tekst etykiety)
  • type (typ inputa: 'text', 'password', 'email', 'number')
  • placeholder (tekst pomocniczy)
  • id (unikalny identyfikator, ważny dla accessibility)
import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-custom-input',
  standalone: true,
  templateUrl: './custom-input.component.html',
  styleUrl: './custom-input.component.scss' // Plik SCSS może być pusty, jeśli używamy tylko klas Bootstrapa
})
export class CustomInputComponent {
  @Input() label: string = '';
  @Input() type: string = 'text';
  @Input() placeholder: string = '';
  @Input() id: string = '';
}

Krok 3: Szablon HTML

Edytuj custom-input.component.html:

<div class="mb-3">
  <label [for]="id" class="form-label">{{ label }}</label>
  <input 
    [type]="type" 
    class="form-control" 
    [id]="id" 
    [placeholder]="placeholder">
</div>

Część 2: Komponent Select (custom-select)

Krok 1: Generowanie komponentu

ng generate component custom-select

Krok 2: Logika i parametry

Selektor potrzebuje listy opcji. Przyjmijmy, że każda opcja to prosty string lub obiekt (dla uproszczenia zaczniemy od stringów).

import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common'; // Potrzebne dla dyrektywy *ngFor

@Component({
  selector: 'app-custom-select',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './custom-select.component.html',
  styleUrl: './custom-select.component.scss'
})
export class CustomSelectComponent {
  @Input() label: string = '';
  @Input() id: string = '';
  @Input() options: string[] = [];
}

Krok 3: Szablon HTML

<div class="mb-3">
  <label [for]="id" class="form-label">{{ label }}</label>
  <select class="form-select" [id]="id">
    <option selected disabled>Wybierz opcję...</option>
    <option *ngFor="let opt of options" [value]="opt">{{ opt }}</option>
  </select>
</div>

Część 3: Komponent Checkbox (custom-checkbox)

Krok 1: Generowanie

ng generate component custom-checkbox

Krok 2: Kod TypeScript

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-custom-checkbox',
  standalone: true,
  templateUrl: './custom-checkbox.component.html',
  styleUrl: './custom-checkbox.component.scss'
})
export class CustomCheckboxComponent {
  @Input() label: string = '';
  @Input() id: string = '';
  @Input() checked: boolean = false;
}

Krok 3: Szablon HTML

Struktura checkboxa w Bootstrapie jest specyficzna:

<div class="form-check mb-3">
  <input class="form-check-input" type="checkbox" [id]="id" [checked]="checked">
  <label class="form-check-label" [for]="id">
    {{ label }}
  </label>
</div>

Część 4: Budowa formularza rejestracji

Teraz użyjmy naszych "klocków" w app.component.ts (lub innym komponencie rodzicu).

Krok 1: Dane w komponencie rodzica

W app.component.ts przygotuj dane dla selecta:

export class AppComponent {
  roles = ['Administrator', 'Moderator', 'Użytkownik'];
}

Pamiętaj o zaimportowaniu stworzonych komponentów w tablicy imports.

Krok 2: Składanie widoku

W app.component.html:

<div class="container mt-5" style="max-width: 500px;">
  <h2>Formularz Rejestracji</h2>
  <form>
    <!-- Dynamiczne Inputy -->
    <app-custom-input 
      label="Nazwa użytkownika" 
      id="username" 
      placeholder="Wpisz login...">
    </app-custom-input>

    <app-custom-input 
      label="Email" 
      type="email" 
      id="email" 
      placeholder="[email protected]">
    </app-custom-input>

    <app-custom-input 
      label="Hasło" 
      type="password" 
      id="pass" 
      placeholder="********">
    </app-custom-input>

    <!-- Dynamiczny Select -->
    <app-custom-select 
      label="Rola w systemie" 
      id="role" 
      [options]="roles">
    </app-custom-select>

    <!-- Dynamiczny Checkbox -->
    <app-custom-checkbox 
      label="Akceptuję regulamin serwisu" 
      id="terms">
    </app-custom-checkbox>

    <!-- Przycisk z Zadania 100 (jeśli masz) lub zwykły -->
    <button type="submit" class="btn btn-primary w-100 mt-2">Zarejestruj</button>
  </form>
</div>

Zadanie domowe (dla chętnych)

  1. Spraw, aby komponenty faktycznie zwracały wartość do rodzica (użyj @Output() i EventEmitter lub spróbuj zaimplementować ControlValueAccessor dla obsługi Reactive Forms).
  2. Dodaj parametr @Input() errorMessage: string, który wyświetli czerwony komunikat pod polem, jeśli wystąpi błąd walidacji.