Опубликован: 10.12.2007 | Уровень: специалист | Доступ: платный
Лекция 14:

Шаблоны

14.5. Жизненный цикл шаблона

Рассмотрим те шаги, которые последовательно проходит запрос шаблона. Первая часть процесса - начальное порождение контента XUL.

  1. Mozilla загружает документ XUL, содержащий шаблон. XUL контент, окружающий шаблон, рендерится как обычно, а для контента, порождаемого шаблоном, выделяется место.
  2. Все теги, включая теги шаблона, формируют дерево DOM 1.
  3. Когда обнаруживается шаблон, браузер начинает грузить в память факты из указанных источников. Это делается асинхронно, подобно тому, как загружается изображение в HTML web-страницу.
  4. Среди тегов шаблона определяются теги правил запроса, и правила преобразуются во внутреннюю форму.
  5. После того, как все правила определены, и источники данных полостью загружены, система начинает поиск решений. Если программист добавил в код метки-наблюдатели ( observers ), эти наблюдатели отслеживаются в процессе поиска.
  6. Если требуется построение с задержкой ("ленивое"), процесс поиска может помечать некоторые части меткой "не делать этого сейчас, может быть, вернуться к этому позже").
  7. Когда все решения найдены, порождается контент. Этот контент добавляется к документу XUL. Это делается асинхронно, подобно тому, как загружается изображение в web-страницу.
  8. Когда все "неленивые" решения найдены и обработаны, начальное порождение контента завершено.

Вторая часть процесса касается взаимодействия пользователя и шаблона после завершения первоначального порождения контента.

  1. Порожденный XUL контент может реагировать на действия пользователя точно так же, как обычный контент. Он может использовать обработчики событий, реагировать на изменения в верстке и отзываться на запросы скриптов.
  2. Если шаблон строится лениво, клик пользователя на стрелочке (треугольничке в дереве) вызывает дальнейшую обработку RDF графа данных. В этом случае воспроизводятся шаги 5-8 первой части.
  3. Если пользователь выполняет операцию перетаскивания контента мышью или кликает на пиктограмме сортировки, шаблоны, основанные на тегах <listbox> или <tree>, должны откликнуться на эти действия, возможно даже обновляя содержание источника фактов. Перетаскивание мышкой контента шаблона требует от прикладного программиста написания дополнительных скриптов.
  4. Если шаблон должен быть перестроен, воспроизводятся шаги 2-8 из первой части, за исключением того, что новый контент замещает старый, а не порождается с самого начала, как в исходном случае. Если шаблон не может изменять данные и предназначен только для их чтения, закрытие окна Mozilla или закрытие поддеревьев DOM никак не сказывается на источнике RDF фактов. Эти шаблоны не могут повредить документы RDF. Если шаблон включает код, способный воздействовать на источники данных, изменения будут внесены в данные, только если (а) факты были получены из RDF файла или файла закладок и (б) данный файл перезаписывается явным образом кодом, использующим интерфейс nsIRDFRemoteDataSource.

14.6. Скриптинг

Рассмотрим, как улучшать шаблоны из скриптов и управлять ими, и как использовать продвинутые свойства деревьев, обсуждавшиеся в "Списки и Деревья" , "Списки и деревья". "Объекты XPCOM" , "Объекты XPCOM", содержит подробное описание RDF и объектов источников данных, дополняющих систему шаблонов.

14.6.1. Советы по созданию динамических шаблонов

Шаблоны на чистом XUL относительно просты, поскольку неизменны и порождают контент лишь однажды. Шаблоны с использованием XUL, JavaScript, AOM и XPCOM - это достойная задачка, потому что полная функциональность в этом случае скорее исключение, чем правило. Вот некоторая трансцендентная мудрость о динамических шаблонах:

  • Флаг dont-build-content, и другие флаги, сказывающиеся на производительности, не работают, когда шаблон должен быть перестроен как целое. Они воздействуют лишь на производительность обработки любой рекурсивной части запроса шаблона, и на способ слияния множественных источников данных.
  • Атрибут или свойство ref шаблона может быть изменено в любое время, и порождаемый контент будет автоматически перестроен.
  • Изменения в любой части шаблона, кроме ref, требуют вызова метода rebuild() вручную.
  • Интерфейс nsIRDFCompositeDataSource, содержащий все источники данных шаблона, не следует использовать для добавления новых фактов (ни методом Assert() ни любым другим). Всегда работайте с конкретным источником данных.
  • Наиболее надежные источники данных для динамической обработки - in-memory-datasource (изначально пустые источники данных, в дальнейшем заполняющие значение rdf:null атрибута datasources ) и xml-datasource (RDF файлы и URLs, указанные в атрибуте datasources). Добавлять факты в xml-datasource можно лишь с помощью схемы file:. С использованием схем chrome: или resource:, или любой иной, этого делать не следует.
  • Если вы изменяете данные в значительном количестве, сначала удалите их из шаблона, измените, а затем добавьте обратно. Это предохранит систему от множества изменений во внешнем виде порождаемого контента. См. также методы beginBatchUpdate() для деревьев в Таблице 13.3.
  • Метод Flush() поддерживается лишь для источников данных, основанных на URL типа "file:".
  • Метод Refresh() поддерживается для URL типа "file:" и "http:". Используя этот метод для получения файла с web-сервера, проследите, чтобы получаемые данные нигде не кешировались, это может привести к некорректному поведению системы.
14.6.2. AOM и XPCOM объекты шаблонов

Система шаблонов добавляет свойства JavaScript к объектам, представляющим теги шаблона. Свойства добавляются и к тегам, определяющим шаблон, и к порождаемым тегам. Такие свойства добавляются к стандартным свойствам тегов шаблона. Эти свойства указаны в Таблице 14.1.

Таблица 14.1. Свойства JavaScript, добавляемые системой шаблонов
Свойство Полезность Тег <template> Теги правил Порожденные теги
database + - - -
builder Иногда + - - -
resource + + (содержит id тега) - + (содержит значение URI или null )
datasources + Пустая строка Пустая строка Пустая строка
ref + Пустая строка Пустая строка Пустая строка

В этой таблице, "+" означает, что свойство приобретает некоторое полезное значение; "-" значит, что свойство приобретает значение null; "Пустая строка" означает, что свойство содержит строку длины нуль.

Свойство database - это объект, содержащий ссылки на источники данных, используемые шаблоном. Он реализует интерфейс nsICompositeDataSource XPCOM. Он существует даже если единственный источник данных rdf:null. Для шаблонов, получающих данные из chrome, он содержит источник данных rdf:local-store.

Свойство builder - это объект, управляющий запросом и процессом порождения контента шаблона. Его можно рассматривать как высокоспециализированный (и очень ограниченный) инструмент верстки и рендеринга шаблона. Он реализует интерфейс nsIXULTemplateBuilder. Он используется лишь для шаблонов на основе тегов <listbox> и <tree>.

Свойство resource - это объект, содержащий URI скомпилированного из всех источников RDF. Этот URI может быть подлежащим, предикатом или дополнением. Если тег - <template>, он содержит id шаблона, как не-URI ресурс. Если порожденный тег шаблона содержит атрибут uri, он содержит значение указанной расширенной переменной шаблона. Объект реализует интерфейс nsIRDFResource.

Свойство datasources содержит значение XUL атрибута datasources.

Свойство ref содержит значение XUL атрибута ref.

Объект database имеет методы AddDataSource(), RemoveDataSource(), и GetDataSources(), используемые для управления источниками данных шаблона.

Объект builder имеет метод rebuild(), используемый для полного пересчета и обновления содержания шаблона.

Объект resource и атрибуты datasources и ref существуют лишь для удобства. Ни один из этих объектов не может быть замещен прикладным программистом.

В дополнение к данным объектам AOM, существуют объекты-наблюдатели, объекты-снимки и объекты-делегаты конструкторов шаблонов. Они рассматриваются ниже.

14.6.3. Конструкторы шаблонов

Конструкторы шаблонов - это конструкторы, то есть код внутри платформы Mozilla, порождающий контент дерева. Конструкторы не могут быть реализованы прикладным программистом, но могут модифицироваться.

Простейшее использование конструктора - пересчет и обновление дерева в шаблоне. Требуется лишь одна строка кода:

treeElement.builder.rebuild()

Здесь нет опций. Эта техника работает только для шаблонов, построенных на тегах <tree> или <listbox>. rebuild() не работает на шаблонах, построенных на иных тегах.

Во время перестройки дерева состояния open и closed всех поддеревьев сохраняются, если указано ленивое построение дерева. Чтобы этого не случилось, используйте атрибут statedatasource тега <tree>, и затем удалите источник данных перед перестройкой. Чтобы его удалить, удалите источник данных из свойства databases, затем удалите атрибут tree, используя операцию DOM, после чего вызовите метод rebuild().

Вспомним, что в Mozilla используется два конструктора; конструктор контента XUL и конструктор шаблонов. Мы здесь обсуждаем последний. Конструктор шаблона основан на XPCOM-компоненте и интерфейсе:

@mozilla.org/xul/xul-template-builder;1 nsIXULTemplateBuilder

Данный интерфейс содержит метод rebuild(). Данный конструктор не может быть модифицирован или замещен прикладным программистом.

XUL конструктор имеет частный случай для деревьев:

@mozilla.org/xul/xul-tree-builder;1 nsIXULTreeBuilder

Этот конструктор также не может быть замещен прикладным программистом. Но он может быть улучшен. С помощью интерфейса nsITreeBuilderObserver можно зарегистрировать новый объект. Хотя этот интерфейс и напоминает объект-наблюдатель, он также очень похож и на объект-делегат. Чем его считать - дело вкуса.

Интерфейс nsITreeBuilderObserver - подмножество интерфейса nsITreeView, применяемого для создания пользовательских снимков дерева. Его можно использовать для улучшения способов взаимодействия пользователя и дерева, такого как кликанье мышкой по колонкам и перетаскивание мышкой контента. В приложениях Mozilla можно найти два примера применения этого интерфейса: одно в менеджере закладок и одно в почтовом клиенте. Оба примера реализуют drag-and-drop на соответствующих деревьях и их код JavaScript можно рассматривать как хороший пример реализации.

Эти интерфейсы применяются для реализации drag-and-drop из-за того способа, которым деревья хранят свой контент, и который отличается от способа, используемого остальными тегами XUL (см. "Списки и Деревья" , "Списки и Деревья") Стандартная техника drag-and-drop описывается в "События" , "События", она не может быть использована для деревьев. Фактически вообще не существует (простого) способа реализовать drag-and-drop для деревьев не на основе шаблонов.

Если используется объект с интерфейсом nsITreeBuilderObserver, его нужно реализовать как наблюдатель DOM-объекта дерева с помощью метода addObserver() интерфейса nsITreeView.

Все конструкторы Mozilla также поддерживают интерфейс nsIRDFObserver, а это означает, что все дерево целиком может действовать как наблюдатель. Этот интерфейс может быть добавлен объекту источника данных (интерфейс nsIRDFDataSource ), и он будет опрашиваться каждый раз, когда появляются новые факты, значимые для запроса шаблона.

14.6.4. Снимки

Если шаблон основан на дереве, то также доступны свойства снимков. В "Списки и Деревья" , "Списки и Деревья", объяснялось, что снимки - это автоматически создаваемые объекты, используемые конструктором дерева или списка для генерации актуального в каждый момент контента.

Созданный программистом объект-снимок полностью заменяет контент, который будет показан шаблоном как часть данных из файла RDF. Если источник данных для шаблона <tree> - rdf:null, то созданный программистом снимок делает всю работу по наполнению дерева данными. Простейший способ это реализовать - использовать тег <tree> с нормальной секцией <treecols>, плюс одна из трех следующих опций для остающегося контента:

<children/> 
<template>
  <treechildren/>
</template>
<template> ... normal set of template rules ... 
</template>

Подобное дерево сначала построит свой обычный контент, затем, когда снимок изменится и дерево будет перестроено, начинает действовать снимок, который определяет дальнейший контент. Если использован атрибут-флаг "dont-build-content", обычный контент вообще не будет построен и показан.

14.6.5. Объекты-делегаты

Объекты-делегаты - это очень обобщенный термин в объектно-ориентированном программировании, и множество структур имеют делегато-подобные свойства.

В платформе Mozilla объекты-делегаты - это объекты, связывающие URIs фактов RDF с внешними системами, так что каждый URI имеет ассоциированную с ним внешнюю нагрузку. Это могут быть жизненно важные данные разного сорта. Объекты-делегаты, например, могут связывать факты RDF об электронной почте с внешними источниками, хранящимися на удаленных серверах, например, MTP, IMAP или LDAP.

В этой лекции нет места и времени для подробного обсуждения объектов-делегатов. Как стартовую точку изучения можно рекомендовать интерфейсы nsIRDFResource и nsIRDFDelegateFactory, а также следующие компоненты XPCOM:

@mozilla.org/rdf/delegate-factory;1?key=
14.6.6. Часто встречающиеся задачи программирования

Ниже приведен краткий список стратегий решения часто встречающихся задач. Программируемые RDF объекты подробно рассматриваются в "Объекты XPCOM" , "Объекты XPCOM". В частности, там рассматриваются приемы работы с источниками данных rdf:null и другими внутренними источниками данных.

Чтобы сменить источник данных, используйте databases.getDataSources(), шаг за шагом по доступному списку источников, чтобы найти нужный, задействуйте databases.removeDataSource() с этим источником в качестве аргумента, и затем перестройте шаблон. Либо удалите этот источник и перестройте шаблон.

Чтобы сменить корневой URI запроса шаблона, используйте setAttribute("ref",newURI) для базового тега. Шаблон будет перестроен автоматически. Установка свойства ref даст тот же эффект.

Чтобы сменить правила запроса, используйте операции DOM или innerHTML чтобы изменить теги прямо в XUL, и затем перестройте шаблон с помощью rebuild(). Лучшее решение - создать все возможные правила и затем запретить ненужные. Это можно сделать, поместив перед правилами, которые нужно запретить, правило "catch-all". Правило "catch-all" - это такое правило, которое находит решения всех доступных ему данных, так что до оставшихся правил дело не доходит.

Чтобы изменить факты в источнике данных, выберите нужный источник с помощью databases.GetDataSources(). Используйте методы Assert() или Change() интерфейса nsIRDFDataSource. В некоторых случаях шаблон будет перестроен автоматически. Чтобы перестроить его наверняка, используйте rebuild().

Чтобы изменить результаты запроса, следует немного отступить назад. Вы не можете изменить результаты, найденные запросом, потому что они порождаются им. Необходимо изменить запрос или исходные RDF данные так, чтобы получаемые решения изменились. Чтобы изменить факт в источнике фактов, используется компонент @mozilla.org/rdf/rdf-service;1 и другие RDF компоненты. С их помощью можно конструировать факты и части фактов, и затем использовать метод Assert() чтобы установить значение факта true. Затем перестройте шаблон.

Чтобы изменить порожденный контент, применяются обычные DOM операции. (Эти изменения будут потеряны, если шаблон перестроить). Чтобы сделать изменения постоянными, измените часть правила <content>, а не порожденный контент, и перестройте шаблон.

Дмитрий Гуменюк
Дмитрий Гуменюк
Россия, Звенигород
Konstantin Grishko
Konstantin Grishko
Россия, Москва, Московский финансово-промышленный университет "Синергия", Москва