Kurs Angular · Moduł 6: Signals

Wejścia na sygnałach - jak samuraj przyjmuje rozkazy

3 min czytania ·
W tej lekcji3

Komponent-karta samuraja dostaje dane od rodzica: imię, poziom, klan. Kiedyś przyjmował je przez @Input(), dziś przez sygnały. Ta lekcja pokazuje trzy rodzaje takich wejść: zwykłe input() (rozkaz w jedną stronę), model() (rozmowa w dwie strony) i sygnałowe zapytania o dzieci. Wszystkie łączy jedno - odczytujesz je jak każdy inny sygnał, przez ().

input() - rozkaz z góry

input() zamienia dane od rodzica w sygnał tylko do odczytu. W szablonie czytasz go jak zwykły sygnał: name().

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

@Component({
  selector: 'app-samurai-card',
  template: `<h2>{{ name() }}</h2><p>Poziom: {{ level() }}</p>`
})
export class SamuraiCardComponent {
  name = input('Unknown');                 // opcjonalny, z domyslna wartoscia
  level = input.required<number>();         // wymagany
  clan = input('Ronin', {
    transform: (v: string) => v.toUpperCase()   // przeksztalc przy wejsciu
  });
}

Trzy warianty pokrywają większość potrzeb. input('Unknown') ma wartość domyślną, gdy rodzic nic nie poda. input.required<number>() przeciwnie - Angular zgłosi błąd, jeśli rodzic zapomni go przekazać, co chroni przed cichą pomyłką. A transform przepuszcza wartość przez funkcję już na wejściu - tu każdy klan zapisujesz wielkimi literami, bez ręcznego przetwarzania w komponencie. Rodzic używa ich jak zawsze: <app-samurai-card name="Musashi" [level]="10">.

model() - rozmowa w dwie strony

Czasem dziecko ma nie tylko czytać wartość, ale i ją zmieniać - jak suwak zdrowia. Kierunki są trzy: input() przekazuje dane od rodzica do dziecka, output() wysyła zdarzenie od dziecka do rodzica (deklarujesz je jako selected = output<string>() i wywołujesz selected.emit(...)), a model() łączy oba w dwukierunkowe wiązanie: rodzic podaje wartość, dziecko ją aktualizuje, a rodzic widzi zmianę.

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

@Component({
  selector: 'app-health-bar',
  template: `
    <input type="range" [value]="health()"
           (input)="health.set(+$any($event.target).value)">
  `
})
export class HealthBarComponent {
  health = model(100);   // dziala jak [(ngModel)]
}

Różnica względem input() jest jedna, ale zasadnicza: model() można ustawiać (health.set(...)), a zmiana wraca do rodzica. Dlatego rodzic wiąże go dwustronnie: <app-health-bar [(health)]="currentHealth">. Gdy przesuniesz suwak w dziecku, currentHealth w rodzicu zmieni się samo. To sygnałowy odpowiednik dawnego [(ngModel)].

Sygnałowe zapytania o dzieci

Ostatni rodzaj wejść to nie dane od rodzica, lecz uchwyty do własnych dzieci w szablonie. Dawne dekoratory @ViewChild mają teraz sygnałowe wersje.

import { viewChild, viewChildren, contentChild } from '@angular/core';

export class ParentComponent {
  childComponent = viewChild(ChildComponent);     // jedno dziecko z szablonu
  allCards = viewChildren(CardComponent);          // wszystkie takie dzieci
  projectedHeader = contentChild('header');        // dziecko wrzucone przez <ng-content>
}

Nazwy mówią, gdzie szukać: viewChild znajduje element z własnego szablonu komponentu, a contentChild - element wrzucony z zewnątrz przez projekcję treści. Liczba mnoga (viewChildren) zwraca wszystkie pasujące. Zysk z wersji sygnałowej jest ten sam co wszędzie: wynik to sygnał, więc możesz na nim budować computed i effect, zamiast czekać na zdarzenia cyklu życia.

Pamiętaj z tej lekcji jedną wspólną cechę: wszystkie te wejścia to sygnały, które czytasz przez (). input() przyjmuje rozkaz z góry, model() prowadzi rozmowę w dwie strony, a viewChild/contentChild dają sygnałowe uchwyty do dzieci.

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Jak zdefiniować wymagany signal input?

  2. 2. Która funkcja tworzy property wspierającą two-way binding [(value)] w komponencie standalone?

To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.

Zadania praktyczne w grze

  • Układanie w poziomie

    Ułóż w jednej linii deklarację property two-way binding health z wartością początkową 100.

  • Układanie w poziomie

    Ułóż w jednej linii deklarację opcjonalnego signal inputu name z wartością domyślną 'Unknown'.

  • Edytor kodu

    Uzupełnij kartę samuraja: imię ma przychodzić od rodzica jako wejście sygnałowe (nowe API zamiast dekoratora @Input), a wybór wraca do rodzica przez output().

  • Klikanie w kolejności

    Ułóż wywołanie tworzące wymagany input typu number (część po znaku =).

Przydatne artykuły