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): ?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.
lookupPath (string $type): ?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.
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
.
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(): array
Returns direct descendants as an array. The keys contain the names of these components. Note: in version 3.0.x, the method returned an iterator instead of an array, and its first parameter specified whether to iterate through the components in depth, and the second represented a type filter. These parameters are deprecated.
getComponentTree(): array
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 to find out when a component has been added to the presenter tree? Keeping track of the parent change is not enough,
because the parent of the parent could have been attached to the presenter, for example. The monitor($type, $attached,
$detached) method can help. Each component can monitor any number of classes and interfaces. Connection or disconnection is
announced by calling the callbacks $attached
and $detached
, respectively, and passing the object of the
monitored class.
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. (Previously, the common methods attached
and
detached
were used for this purpose.)