Component Model
An important concept in Nette is the component. We insert visual interactive components into pages, forms or all their elements are
also components. There are 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 with 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 presenter if the component is connected to it,
despite 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 the children and thus forming the tree structure. It has methods for easily
adding, retrieving and removing components. It is the ancestor of, for example, the form or classes Control
and
Presenter
.
getComponent(string $name): ?Component
Returns a component. Attempt to call undefined child causes invoking of factory createComponent($name).
Method createComponent($name)
invokes method createComponent<component name>
in current component
and it passes name of the component as a parameter. Created component is then passed to current component as its child. We call
theese component factories, 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
Returns the entire hierarchy of components, including all nested child components as indexed array. The search goes in depth first.
Monitoring of Ancestors
The Nette component model allows for very dynamic tree work (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.
An example: Class UploadControl
, representing form element for uploading files in Nette Forms, has to set
form's attribute enctype
to value multipart/form-data
. But in the time of the creation of the object it
does not have to be attached to any form. When to modify the form? The solution is simple – we create a request for monitoring
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 when the form is available, the callback is called. (Previously, the common methods attached
and
detached
were used instead.)