Components

Components are the foundation of reusable code. They make your work easier and allow you to profit from community work. Components are wonderful. We'll explain

  • how to write components
  • what are signals
  • how to send flash messages
  • how to use AJAX

A component is a renderable object. It can be a form, menu, poll etc. Within one page there can be as many components as you wish. At https://addons.nette.org/cs you can find open-source components which were put there by volunteers from the Nette Framework community.

You can find an example of a component and it's use in a page in the example Fifteen.

A component is a descendant of class Nette\Application\UI\Control:

use Nette\Application\UI\Control;

class PollControl extends Control
{
}

When talking about components, we usually mean descendants of class Control. Components are basically controls in the framework's terminology.

Templates

A component has factory for its template. It creates a template, passes several variables (shown below) and registers default filters. Rendering is our job and it is done in method render(). This is where we choose the file from which the template will be loaded. render() method is also the right place to register variables used in the template. The template can be put inside the same folder as the component and with the same file name.

public function render()
{
    $template = $this->template;
    $template->setFile(dirname(__FILE__) . '/PollControl.latte');
    // insert some parameters to the template
    $template->param = 'some value';
    // render it
    $template->render();
}

It's possible to pass arguments from template to render().

{control poll $poll}
// PollControl.php
public function render($poll) { ... }
// PollPresenter.php
protected function createComponentPoll() { ... }

The method link() is made for linking to signals. Links are rendered using the macro {link} in the template, or by macro {plink} if we are in a component's template and the target is a presenter.

Use in a component:

$url = $this->link('click!', $x, $y);

Use in a template:

<a n:href="click! $x, $y"> ... </a>

Flash messages

A component has its own storage of flash messages independent of the presenter. These messages inform about the result of operations which are followed by redirection.

Sending is done by the method flashMessage. The first parameter is the message's text and the second (optional) parameter is its type (error, warning, info, etc.). The method flashMessage() returns an instance of flash message to which you can pass information.

Example:

public function deleteFormSubmitted(Form $form)
{
    ... delete record using model ...

    // pass flash message
    $this->flashMessage('Item was deleted.');

    $this->redirect(...); // and redirect
}

Template receives this messages automatically in variable $flashes. This variable contains an array of objects (stdClass) which contains properties message, type and can contain extra information mentioned above.

Example:

{foreach $flashes as $flash}
<div class="flash {$flash->type}">{$flash->message}</div>
{/foreach}

If flashMessage() is saved and redirection occurs, the same parameter $flashes will be present in the template. Messages then stay alive for the next three seconds – to prevent their loss in case that page was not loaded successfully and the user refreshed the request. If someone refreshes the page (F5) twice in a row the flash message persists, if s/he clicks elsewhere it disappears.

Signal (subrequest)

A signal (subrequest) is a communication with the server under the level of normal view. Signals are actions without a change of view. Only a presenter can change the view therefore components work only with signals. $component->link() links to a signal, $presenter->link() usually links to a view (unless an exclamation mark is used – $presenter->link('signal!')). But a component can call $component->presenter->link('view').

A signal causes a page reload like the original request (with the exception of AJAX) and invokes the method signalReceived($signal) whose default implementation in class Nette\Application\UI\Component tries to call a method composed of the words handle{Signal}. Further processing relies on the given object. Objects which are descendants of Component (i.e. Control and Presenter) try to call handle{Signal} with relevant parameters.

In other words: the definition of the method handle{Signal} is taken and all parameters which were received in the request are matched with the method's parameters. It means that the parameter id from the URL is matched to the method's parameter $id, something to $something and so on. And if the method doesn't exist, the method signalReceived throws an exception.

An example signal handler:

public function handleClick($x, $y)
{
    if (!$this->isClickable($x, $y)) {
        throw new Nette\Application\UI\BadSignalException('Action not allowed.');
    }

    // ... processing of signal ...
}

Signal can be received by any component, presenter of object which implements interface ISignalReceiver if it's connected to component tree.

The main receivers of signals are Presenters and visual components extending Control. A signal is a sign for an object that it has to do something – poll counts in a vote from user, box with news has to unfold, form was sent and has to process data and so on.

Signal is always called on current presenter and view therefore it is impossible to point it elsewhere.

The URL for the signal is created using the method Component::link(). As parameter $destination we pass string {signal}! and as $args an array of arguments which we want to pass to the signal handler. Signal parameters are attached to the URL of the current presenter/view. The parameter ?do in the URL determines the signal called.

Its format is {signal} or {signalReceiver}-{signal}. {signalReceiver} is the name of the component in the presenter. This is why hyphen (inaccurately dash) can't be present in the name of components – it is used to divide the name of the component and signal, but it's possible to compose several components.

The method isSignalReceiver() verifies whether a component (first argument) is a receiver of a signal (second argument). The second argument can be omitted – then it finds out if the component is a receiver of any signal. If the second parameter is TRUE it verifies whether the component or its descendants are receivers of a signal.

In any phase preceding handle{Signal} can be signal performed manually by calling the method processSignal() which takes responsibility for signal execution. Takes receiver component (if not set it is presenter itself) and sends it the signal.

Example:

if ($this->isSignalReceiver($this, 'paging') || $this->isSignalReceiver($this, 'sorting')) {
    $this->processSignal();
}

The signal is executed prematurely and it won't be called again.

Subrequest vs. request

The difference between a signal and a request:

  • a subrequest keeps all components
  • a request keeps only persistent components

Invalidation and snippets

During the processing of a signal changes can occur which require re-rendering of a component. These methods takes care of this: redrawControl() and isControlInvalid(). They are basic elements of using AJAX with Nette.

Nette also offers even higher granularity of validity, than only at the component level, with use of snippets.

Snippets can be invalidated one by one (any component can have as many snippets as needed). If the whole component is invalidated than each of its snippets is invalidated too. A component is considered invalid if any of its children (components) is invalid.

More information can be found on the page about AJAX.

Persistent parameters

It is often needed to keep some parameter in a component for the whole time of working with the component. It can be for example the number of the page in pagination. This parameter should be marked as persistent using the annotation @persistent.

class PollControl extends Control
{
    /** @persistent */
    public $page = 1;

    ...
}

This parameter will be automatically passed in every link as a GET parameter until the user leaves the page with this component.

Never trust persistent parameters blindly because they can be faked easily (by overwriting the URL). Verify, for example, if the page number is within the correct interval.

Components with dependencies

When our component needs some things to work, like PollControl needs PollManager to manage voting and save polls, it can be done by injecting these dependencies into the constructor:

class PollControl extends Control
{
    /**
     * @var App\Model\PollManager
     */
    private pollManager;

    /**
     * @var int Id of a poll, for which the component is created
     */
    private $pollId;

    /**
     * @param $pollId
     * @param App\Model\PollManager $pollManager model providing the voting
     */
    public function __construct($pollId, PollManager $pollManager)
    {
        $this->pollManager = $pollManager;
        $this->pollId = $pollId
    }

    /**
     * @param $voteId Option Id we voted for
     */
    public function handleVote($voteId)
    {
        $this->pollManager->vote($pollId, $voteId);
        //...
    }
}

OK, but how the PollManager is put into the PollControl's constructor? That's job for DI container. It put it there automatically through an interface we provide it:

interface IPollControlFactory
{
    /**
     * @param $pollId
     * @return PollControl
     */
    public function create($pollId);
}

And this interface has to be registered in our container in neon:

services:
    - IPollControlFactory

Finally, we will use this factory in our presenter:

class PollPresenter extends \Nette\UI\Application\Presenter
{
    /**
     * @var IPollControlFactory
     * @inject
     */
    public $pollControlFactory;

    protected function createComponentPollControl()
    {
        $pollId = 1; // we can pass our parameter
        return $this->pollControlFactory->create($pollId);
    }
}

That's all. Nette internally implements this interface and injects it to our presenter, where we can use it. It also magically passes our parameter $pollId and instance of class App\Model\PollManager into our component.

Advanced use of components

Components are mostly renderable. But also unrenderable components exist. Some components can have descendants some not. Nette Framework introduces several classes and interfaces for all these types of components.

Object inheritance allows us to have a hierarchic structure of classes like in real world. We can create new classes by extending. These extended classes are descendants of the original class and inherit its parameters and methods. Extended class can add its own parameters and methods to the inherited ones.

Knowledge class hierarchy is required for proper understanding how things work.

Nette\ComponentModel\Component  { IComponent }
|
+- Nette\ComponentModel\Container  { IContainer }
   |
   +- Nette\Application\UI\Component  { ISignalReceiver, IStatePersistent }
      |
      +- Nette\Application\UI\Control  { IPartiallyRenderable }
         |
         +- Nette\Application\UI\Presenter  { IPresenter }

Nette\ComponentModel\IComponent

Interface Nette\ComponentModel\IComponent has to be implemented by every component. It requires method getName() which is returning it's name and getParent() returning it's parent. Both name and parent can be set using method setParent() – first argument is parent and the second one is name.

Nette\ComponentModel\Component

Nette\ComponentModel\Component is a standard implementation of IComponent. It is common ancestor of all components including form elements. It has several methods for traversing:

lookup($type) looks up object of given class or interface up in the hierarchy. For example $component->lookup('Nette\Application\UI\Presenter') returns presenter if the components is bound to it (even deep in the hierarchy).

lookupPath($type) returns path – a string made by concatenating names of all components on the way between current and component of $type. For example $component->lookupPath('Nette\Application\UI\Presenter') returns unique identifier of component toward presenter.

Nette\ComponentModel\IContainer

Parent components implement not just IComponent but also Nette\ComponentModel\IContainer. Interface which contains methods for adding, removing, getting and iteration over components. Such components can create hierarchy – presenters can contain forms and these forms can contain form inputs. Whole tree hierarchy of components is created by branches of IContainer objects and IComponent leafs.

Nette\ComponentModel\Container

Nette\ComponentModel\Container is a standard implementation of IContainer interface. It is an ancestor of for example form or classes Control and Presenter.

It offers methods for correct adding, getting and removing objects and of course iteration over its contents. 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.

Nette\Application\UI\Component

Class Nette\Application\UI\Component is an ancestor of all components used in presenter. Components in presenter are object which are kept by presenter during its life cycle.

They have ability to influence each other, save their states into URL and respond to user commands (signals) and does not have to be renderable.

Nette\Application\UI\Control

Control is a renderable component. It is a reusable part of web application which is this whole chapter about. When talking about components this is the class we usually have on mind. It remembers which it's part should be rendered in AJAX request, which we've already talked about.

Control isn't representing visual sector of webpage, but it's logical part. It is possible to render it repeatedly, conditionally and every time with different template.

Component tree

The same component can be rendered more than once on page if given different names (this can be done dynamically). Parent can be presenter, component or other object implementing IContainer.

Hierarchy can look like following:

Nette\Application\UI\Presenter { root of tree of components is always a presenter }
  |
  --Nette\Application\UI\Control { implements IContainer => can be parent }
     |
     --Nette\ComponentModel\Component
     |
     --Nette\ComponentModel\Component { does not implement IContainer => cannot be parent }
     |
     --Nette\Application\UI\Control
        |
        --Nette\ComponentModel\Component

Delayed composition

Component model in Nette offers very dynamical tree workflow (components can be removed, added, moved). It would be a mistake to rely on knowing parent (in constructor) when the component is created. In most cases, parent is not not known when the component is created.

$control = new NewsControl;
// ...
$parent->addComponent($control, 'shortNews');

Monitoring changes

How to find out when was component added to presenter's tree? Watching the change of parent is not enough because a parent of parent might have been added to presenter. Method monitor($type) is here to help. Every component can monitor any number of classes and interfaces. Adding or removing is reported by invoking method attached($obj) (detached($obj) respectivelly), where $obj is the object of 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');
        // ...
    }

    // ...
}

and method attached is called when the form is available:

protected function attached($form)
{
    parent::attached($form);

    if ($form instanceof Nette\Forms\Form) {
        $form->getElementPrototype()->enctype = 'multipart/form-data';
    }
}

Monitoring and lookup of components or paths using lookup is very precisely optimized for maximal performance.

Iterating over children

There is a method getComponents($deep = FALSE, $type = NULL) for that. First parameter determines if the components should be looked up in depth (recursively). With TRUE, it not only iterates all it's children, but also all chilren of it's children, etc. Second parameter servers as an optional filter by class or interface.

For example, this is the way how validation of forms is internally performed:

$valid = TRUE;
foreach ($form->getComponents(TRUE, 'Nette\Forms\IControl') as $control) {
    if (!$control->getRules()->validate()) {
        $valid = FALSE;
        break;
    }
}