Component Model
An important concept in Nette is the component. We insert visual interactive components into pages; forms and all their elements
are also components. The two basic classes from which all these components inherit are part of the
nette/component-model package and are responsible for creating the component tree hierarchy.
Component
Nette\ComponentModel\Component
is the common ancestor of all components. It contains the getName() method returning the name of the component and
the getParent() method returning its parent. Both can be set using the setParent() method – the first
parameter is the parent, and the second is the component name.
lookup (?string $type, bool $throw=true): ?Component
Searches up the hierarchy for an object of the desired class or interface. For example,
$component->lookup(Nette\Application\UI\Presenter::class) returns the presenter if the component is connected to
it, even across several levels. If no matching object is found, it throws an exception; pass false as the second
argument to return null instead. If you pass null as the $type, the method searches for the
topmost component in the tree, i.e., the root with no parent.
lookupPath (?string $type=null, bool $throw=true): ?string
Returns the so-called path, which is a string formed by concatenating the names of all components on the path between the
current component and the component being searched for. So, for example,
$component->lookupPath(Nette\Application\UI\Presenter::class) returns the unique identifier of the component
relative to the presenter. When $type is null (or omitted), the path is measured up to the root of
the tree.
Container
Nette\ComponentModel\Container
is the parent component, i.e., the component containing children and thus forming the tree structure. It has methods for easily
adding, retrieving, and removing objects. It is the ancestor of, for example, the form or classes Control and
Presenter. Descendants that use the ArrayAccess trait (such as Control and
Presenter) also allow accessing children via array notation, e.g. $container['child'].
addComponent (Component $component, ?string $name, ?string $insertBefore=null): static
Adds a component to the container as a child. If $name is null, the component's own name is used.
Using the optional $insertBefore – the name of an existing child – the new component is inserted right before
it; otherwise it is appended to the end. The method returns the container itself, so calls can be chained.
removeComponent (Component $component): void
Removes a child component from the container.
getComponent (string $name): ?Component
Returns a component. Attempting to retrieve an undefined child invokes the factory method createComponent($name).
The createComponent($name) method calls the method createComponent<component name> in the current
component, passing the component name as a parameter. The created component is then added to the current component as its child.
We call these methods component factories, and they can be implemented in classes inherited from Container.
getComponents(): IComponent[]
Returns direct descendants as an array; the keys contain the names of these components. To retrieve the entire subtree
recursively, use getComponentTree(), optionally combined with array_filter() for type filtering. (The
$deep and $filterType parameters known from older versions were removed in version 4.0.)
getComponentTree(): list<IComponent>
Gets the entire hierarchy of components, including all nested child components, as an indexed array. The search is depth-first.
Monitoring Ancestors
The Nette component model allows for very dynamic work with the tree (we can remove, move, add components), so it would be a mistake to rely on the fact that after creating a component, the parent, parent's parent, etc., are known immediately (in the constructor). Usually, the parent is not known at all when the component is created.
How can a component find out the moment it is attached below a presenter – or below any other ancestor of a given type?
Watching the direct parent isn't enough, because the connection may happen higher up the tree, for example when the
parent's parent gets attached. That is what the monitor($type, $attached,
$detached) method is for: a component declares that it wants to be notified whenever an ancestor of class or interface
$type appears above it in the tree, or disappears from it. A component can monitor any number of types; the
$attached callback fires when a matching ancestor connects and receives that ancestor as its argument, while
$detached fires when it disconnects. Monitoring can be stopped again with unmonitor($type).
Notifications follow the tree structure. When attaching, an ancestor is notified before its descendants (top-down), so a parent can prepare shared state first, or even remove a child before the child's own callback runs. When detaching, the order is reversed – descendants are notified first. Callbacks are also deduplicated, so the same callback is never called twice for the same object. For the reasoning behind this behavior, see the blog post about version 4.0.
For better understanding, here's an example: The UploadControl class, representing the form control for uploading
files in Nette Forms, must set the form's enctype attribute to multipart/form-data. However, at the time
the object is created, it might not be attached to any form. So, at what point should the form be modified? The solution is
simple – a request for monitoring is made in the constructor:
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');
});
// ...
}
// ...
}
and as soon as the form becomes available, the callback is invoked.