Может показаться, что пользовательские элементы управления являются обычными включаемыми файлами, но это не так. В пользовательских элементах можно размещать другие элементы. Пользовательские элементы могут иметь свойства и обработчики событий. Объекты пользовательских элементов можно легко описать в разметке.
Пользовательские элементы могут играть роль простых вставок (include), либо состоять из класса и разметки HTML.
В пользовательских элементах нельзя использовать директивы, и они не могут иметь собственных файлов локализации.
Пользовательские элементы могут содержать любой код, включая код PHP.
Каждый размещенный на странице экземпляр пользовательского элемента управления может иметь свои, уникальные, значения свойств.
Свойства пользовательских элементов могут ссылаться на другие классы и легко описываются в коде HTML.
Пользовательские элементы могут размещаться в шаблонах и страницах контента, без каких-либо ограничений.
Перед размещением элементов на странице, их нужно зарегистрировать. Это реализуется при помощи директивы #Register.
Регистрировать следует только те элементы, которые потребуются на конкретной странице.
В следующем примере показана регистрация на странице элемента Message
.
<?#Register Src="~/Controls/Message.php" TagPrefix="php" TagName="Message"?>
В параметре Src
указывается путь к основному файлу элемента. Параметры TagPrefix
и TagName
содержат имя для размещения элемента на странице.
В следующем примере показано размещение нескольких экземпляров зарегистрированного элемента Message
.
<php:Message>Привет, мир! Это сообщение #1!</php:Message>
<php:Message>Это сообщение #2!</php:Message>
<php:Message>Таких сообщений может быть сколько угодно!</php:Message>
Как правило, TagPrefix
имеет значение php, а TagName
соответствует имени элемента, но это не является обязательным требованием.
В следующем примере показано использование нестандартных значений TagPrefix
и TagName
.
<?#Register Src="~/Controls/Message.php" TagPrefix="my" TagName="Information"?>
<my:Information>Привет, мир!</my:Information>
На одной странице можно размещать неограниченное число элементов с различными значениями свойств.
Пользовательские элементы могут быть заменой обычным включаемым файлам (include).
В следующем примере показан простой элемент управления.
Это простой элемент управления, по сути просто вставка (include).
<br /><br />
<?="Элемент может содержать любой код и теги."?>
Но несмотря на то, что элемент управления не имеет явного класса, класс для него все равно создается. Это позволяет использовать свойства и передавать в экземпляр элемента любые текстовые значения для этих свойств.
В следующем примере показан исходный код элемента Panel
, который состоит лишь из разметки с блоками серверного кода и не имеет явного класса. Элемент ожидает два анонимных свойства Title
и Content
. Значения для этих свойств можно передать при размещении элемента на странице и они будут обработаны, как явно определенные свойства.
<div class="panel panel-default">
<?php if (isset($this->Title)) {?>
<div class="panel-heading"><?=$this->Title?></div>
<?php } ?>
<div class="panel-body">
<?=$this->Content?>
</div>
</div>
<?#Register Src="~/Controls/Panel.php" TagPrefix="php" TagName="Panel"?>
<php:Panel Title="Заголовок" Content="Привет, мир!" />
Как и в случае со страницами контента, при объектной модели, в основном файле элемента управления размещается класс, а в файле с расширением .html.php - разметка.
В следующем примере показан пользовательский элемент Message
, который используется на данном сайте. Класс элемента описан в файле Message.php, а разметка - в файле Message.html.php.
<div class="<?=$this->GetCssClass()?>">
<?=($this->ShowIcon ? $this->GetIcon() : '')?>
<?=$this->Content?>
</div>
<?php
/**
* Represents message control.
*/
class Message extends Nemiro\UI\Control
{
/**
* The message text.
*
* @var string
*/
public $Content = '';
/**
* Gets or sets the message type. Allowed: Danger (default), Warning, Info, Success.
* @var mixed
*/
public $Type = 'Danger';
/**
*
* @var bool
*/
public $ShowIcon = true;
/**
* Returns css class for the message block.
*
* @return string
*/
function GetCssClass()
{
switch (strtolower($this->Type))
{
case 'warning':
return 'alert alert-warning';
case 'info':
case 'question':
return 'alert alert-info';
case 'success':
return 'alert alert-success';
default:
return 'alert alert-danger';
}
}
/**
* Returns icon for the message.
*
* @return string
*/
function GetIcon()
{
switch (strtolower($this->Type))
{
case 'warning':
return '<span class="glyphicon glyphicon-warning-sign"></span> ';
case 'info':
return '<span class="glyphicon glyphicon-info-sign"></span> ';
case 'question':
return '<span class="glyphicon glyphicon-question-sign"></span> ';
case 'success':
return '<span class="glyphicon glyphicon-ok-sign"></span> ';
default:
return '<span class="glyphicon glyphicon-remove-sign"></span> ';
}
}
}
?>
Любому публичном свойству класса элемента управления можно установить значение при помощи кода HTML, либо программно в обработчике события загрузки страницы.
При программной работе, доступ к определенному элементу осуществляется по идентификатору элемента. По умолчанию, каждому элементу назначается идентификатор автоматически и это может быть не очень удобно. Идентификатор элементу управления можно указать вручную, для это используется свойство ID
.
В следующем примере показано явное определение идентификаторов у двух экземпляров элемента Message
.
<php:Message ID="MyMessage1">Сообщение #1</php:Message>
<php:Message ID="AnyMessage">Другое сообщение</php:Message>
В обработчике события загрузки страницы, при необходимости, можно программно указать текст для этих элементов.
function Load()
{
$this->Controls['MyMessage1']->Content = 'Текст этого сообщения установлен программно.';
$this->Controls['AnyMessage']->Content = 'И для этого сообщения, текст тоже установлен программно.';
}
Свойства элементов управления могут содержать ссылки на другие элементы или представлять коллекции. И всё это можно описать в разметке!
Например, на этом сайте используется пользовательский элемент управления TabControl
, который состоит из: файла разметки TabControl.html.php, класса TabControl
и класса TabItem
. Класс TabItem
представляет вкладку TabControl
и на уровне TabControl
представлен в виде массива Items
.
<?php
require_once 'TabItem.php';
/**
* Represents TabControl.
*/
class TabControl extends Nemiro\UI\Control
{
/**
* List of tabs.
*
* @var TabItem[]
*/
public $Items = array();
}
?>
<?php
class TabItem
{
public $Key = '';
public $Title = '';
public $Content = '';
}
?>
<ul class="nav nav-tabs" role="tablist">
<?php
$i = 0;
foreach ($this->Items as $item)
{
?>
<li class="<?=($i == 0 ? 'active' : '')?>"><a href="#<?=sprintf('%s_%s', $this->ID, $item->Key)?>" aria-controls="<?=sprintf('%s_%s', $this->ID, $item->Key)?>" role="tab" data-toggle="tab"><?=$item->Title?></a></li>
<?php
$i++;
}
?>
</ul>
<div class="tab-content">
<?php
$i = 0;
foreach ($this->Items as $item)
{
?>
<div role="tabpanel" class="<?=($i == 0 ? 'tab-pane active' : 'tab-pane')?>" id="<?=sprintf('%s_%s', $this->ID, $item->Key)?>"><?=$item->Content?></div>
<?php
$i++;
}
?>
</div>
Поскольку свойство Items
является коллекцией, т.е. может иметь вложенные элементы, то в коде HTML оно должно быть представлено в виде тега Items
с вложенными экземплярами TabItem
.
В следующем примере показан вариант размещения экземпляра TabControl
на странице.
<php:TabControl>
<php:Items>
<php:TabItem Key="Item1" Title="Вкладка #1">
Содержимое вкладки #1
</php:TabItem>
<php:TabItem Key="Item2" Title="Вкладка #2">
Содержимое вкладки #2
</php:TabItem>
<php:TabItem Key="Item3" Title="Вкладка #3">
Содержимое вкладки #3
</php:TabItem>
</php:Items>
</php:TabControl>
Класс \Nemiro\UI\Control
(далее Control
) представляет пользовательский элемент управления.
От класса Control
должны наследоваться (extends
) классы всех пользовательских элементов управления.
Если у пользовательского элемента нет класса, то использует экземпляр класса Control
по умолчанию, с ограниченными возможностями.
В следующем фрагменте кода показан вариант наследования класса пользовательского элемента Menu
от базового класса Control
.
<?php
class Menu extends \Nemiro\UI\Control
{
}
?>
Класс Control
имеет следующие публичные свойства.
Свойство | Описание | |
---|---|---|
DefaultValues | Коллекция (ключ=значение) |
Только для чтения. Коллекция значений по умолчанию. Используется для технических нужд. |
Source | Строка | Только для чтения. Путь к основному файлу элемента управления. |
TagPrefix | Строка | Только для чтения. Префикс имени элемента управления. |
TagName | Строка | Только для чтения. Имя элемента управления. |
Name | Строка | Только для чтения. Имя элемента управления с учетом префикса. |
Parent | Объект | Только для чтения. Ссылка на родителя. |
Body | Строка | Тело элемента управления. Используется для вывода на страницу. Значение может быть переопределено в обработчике события LoadComplete. |
Помимо этого, существует виртуальное свойство Content
, которое может быть определено в классах-потомках.
Свойство Content
используется в качестве значения контента элемента, когда элемент в разметке содержит закрывающий тег.
В следующем примере показан вариант явного и неявного указания значения свойству Content
.
<php:Message>Этот текст неявным образом будет помещен в свойство Content.</php:Message>
<php:Message Content="Этот текст явным образом помещен в свойство Content." />
У пользовательских элементов управления существует всего два события: Load
и LoadComplete
.
Оба события происходят после события Load и до возникновения события LoadComplete страницы.
Событие Load
происходит перед формированием данных пользовательского элемента управления для вывода.
В обработчике этого события, теоретически, можно изменить Source
, но лучше этого не делать.
Обработчик Load
может быть полезен для динамического изменения значений пользовательских свойств элемента управления.
В следующем примере показано изменение значения свойства Content
в упрощенной реализации элемента Message
.
<?php
class Message extends \Nemiro\UI\Control
{
public $Content = '';
function Load()
{
$this->Content = 'Текст сообщения: '.$this->Content;
}
}
?>
Событие LoadComplete
происходит после формирования данных.
В обработчике этого события можно изменить значение свойства Body
, которое содержит данные элемента для вывода на страницу.
В следующем примере показано переопределение значения свойства Body
в упрощенной реализации элемента Message
.
<?php
class Message extends \Nemiro\UI\Control
{
public $Content = '';
function LoadComplete()
{
$this->Body = 'Сообщение: '.$this->Content;
}
}
?>
Пользовательские элементы управления не имеют собственных файлов локализации и используют глобальные ресурсы (global.json), либо ресурсы страницы, на которой размещаются. Таким образом, один и тот же ключ ресурса локализации в пользовательских элементах управления может иметь разное значение на разных страницах сайта.