Введение

Пользовательские элементы могут играть роль простых вставок (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>

Класс Control

Класс \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

Событие Load происходит перед формированием данных пользовательского элемента управления для вывода.

В обработчике этого события, теоретически, можно изменить Source, но лучше этого не делать.

Обработчик Load может быть полезен для динамического изменения значений пользовательских свойств элемента управления.

В следующем примере показано изменение значения свойства Content в упрощенной реализации элемента Message.

<?php
class Message extends \Nemiro\UI\Control
{

  public $Content = '';

  function Load()
  {
    $this->Content = 'Текст сообщения: '.$this->Content;
  }

}
?>

LoadComplete

Событие LoadComplete происходит после формирования данных.

В обработчике этого события можно изменить значение свойства Body, которое содержит данные элемента для вывода на страницу.

В следующем примере показано переопределение значения свойства Body в упрощенной реализации элемента Message.

<?php
class Message extends \Nemiro\UI\Control
{

  public $Content = '';

  function LoadComplete()
  {
    $this->Body = 'Сообщение: '.$this->Content;
  }

}
?>

Локализация

Пользовательские элементы управления не имеют собственных файлов локализации и используют глобальные ресурсы (global.json), либо ресурсы страницы, на которой размещаются. Таким образом, один и тот же ключ ресурса локализации в пользовательских элементах управления может иметь разное значение на разных страницах сайта.

Подробнее о механизмах локализации.