Model komponentów
Ważnym pojęciem w Nette jest komponent. Do stron wstawiamy wizualne komponenty interaktywne, komponentami są również formularze
lub wszystkie ich elementy. Podstawowe dwie klasy, od których dziedziczą wszystkie te komponenty, są częścią pakietu
nette/component-model
i mają za zadanie tworzyć hierarchię drzewa komponentów.
Component
Nette\ComponentModel\Component
jest wspólnym przodkiem wszystkich komponentów. Zawiera metody getName()
zwracającą nazwę komponentu i metodę
getParent()
zwracającą jego rodzica. Oboje można ustawić metodą setParent()
– pierwszy parametr
to rodzic, a drugi nazwa komponentu.
lookup (string $type): ?Component
Wyszukuje w hierarchii w górę obiekt żądanej klasy lub interfejsu. Na przykład
$component->lookup(Nette\Application\UI\Presenter::class)
zwraca presenter, jeśli komponent jest do niego
dołączony, nawet przez kilka poziomów.
lookupPath (string $type): ?string
Zwraca tzw. ścieżkę, czyli ciąg znaków powstały przez połączenie nazw wszystkich komponentów na ścieżce między
bieżącym a szukanym komponentem. Zatem np. $component->lookupPath(Nette\Application\UI\Presenter::class)
zwraca
unikalny identyfikator komponentu względem presentera.
Container
Nette\ComponentModel\Container
jest komponentem nadrzędnym, tj. komponentem zawierającym potomków i tworzącym w ten sposób strukturę drzewa. Dysponuje
metodami do łatwego dodawania, pobierania i usuwania obiektów. Jest przodkiem na przykład formularza czy klas
Control
i Presenter
.
getComponent (string $name): ?Component
Zwraca komponent. Przy próbie uzyskania niezdefiniowanego potomka jest wywoływana fabryka
createComponent($name)
. Metoda createComponent($name)
wywołuje w bieżącym komponencie metodę
createComponent<nazwa komponentu>
i jako parametr przekazuje jej nazwę komponentu. Utworzony komponent jest
następnie dodawany do bieżącego komponentu jako jego potomek. Te metody nazywamy fabrykami komponentów i mogą je
implementować potomkowie klasy Container
.
getComponents(): array
Zwraca bezpośrednich potomków jako tablicę. Klucze zawierają nazwy tych komponentów. Uwaga: w wersji 3.0.x metoda zamiast tablicy zwracała iterator, a jej pierwszy parametr określał, czy komponenty mają być przeglądane wgłąb, a drugi reprezentował filtr typów. Te parametry są przestarzałe.
getComponentTree(): array
Pobiera całą hierarchię komponentów, w tym wszystkie zagnieżdżone komponenty podrzędne, jako tablicę indeksowaną. Przeszukiwanie odbywa się najpierw wgłąb.
Monitorowanie przodków
Model komponentów Nette umożliwia bardzo dynamiczną pracę z drzewem (komponenty możemy usuwać, przenosić, dodawać), dlatego błędem byłoby polegać na tym, że po utworzeniu komponentu od razu (w konstruktorze) znany jest rodzic, rodzic rodzica itd. Zazwyczaj bowiem rodzic przy tworzeniu w ogóle nie jest znany.
Jak rozpoznać, kiedy komponent został dołączony do drzewa presentera? Śledzenie zmiany rodzica nie wystarczy, ponieważ do
presentera mógł zostać dołączony na przykład rodzic rodzica. Pomocna jest metoda monitor($type, $attached,
$detached). Każdy komponent może monitorować dowolną liczbę klas i interfejsów. Dołączenie lub odłączenie jest
sygnalizowane wywołaniem callbacku $attached
lub $detached
, i przekazaniem obiektu
śledzonej klasy.
Dla lepszego zrozumienia przykład: klasa UploadControl
, reprezentująca element formularza do przesyłania
plików w Nette Forms, musi ustawić atrybut enctype
formularza na wartość multipart/form-data
. W
momencie tworzenia obiektu nie musi być jednak dołączona do żadnego formularza. W którym momencie więc zmodyfikować
formularz? Rozwiązanie jest proste – w konstruktorze żąda się monitorowania:
class UploadControl extends Nette\Forms\Controls\BaseControl
{
public function __construct($label)
{
$this->monitor(Nette\Forms\Form::class, function ($form): void {
$form->setHtmlAttribute('enctype', 'multipart/form-data');
});
// ...
}
// ...
}
a gdy formularz jest dostępny, wywoływany jest callback. (Wcześniej zamiast niego używano wspólnej metody
attached
lub detached
).