Статьи

JSF 2 ПОЛУЧАЕТ закладки URL

JSR-314: JavaServer Faces (JSF) 2.0 демонстрирует сильную эволюцию инфраструктуры JSF, основанную на фактических стандартах, появившихся в сообществе JSF и продуктах участвующих вендоров. В этой статье, второй статье из серии, посвященной функциям JSF 2.0, предоставленной Red Hat, или в которой Red Hat активно участвовал, рассматриваются новые функции, обеспечивающие формализованную поддержку GET в среде, традиционно основанной на запросах POST. Основными строительными блоками этой поддержки являются параметры представления и пара компонентов пользовательского интерфейса, которые создают закладки с закладками. Обе функции инкубируются в Seam и, следовательно, должны быть знакомы любому разработчику Seam. Они также являются особенностями, за которые страстно отстаивает сообщество JSF.

Примечание автора: большое спасибо Питу Мьюру , который сыграл ключевую роль в качестве технического редактора этой серии.

Прочитайте другие части этой серии статей:

Часть 1 —
JSF 2: Другой
путь к стандартизации в Seam
Часть 2 —
JSF 2 GETs Закладочные URL-адреса 

Часть 3 —
Свободная навигация в JSF 2

Часть 4 —
Ajax и JSF, присоединились наконец

Часть 5 —
Введение Поведение клиента JSF 2

 

Каждый сеанс пользователя должен где-то начинаться. JSF был разработан с расчетом на то, что пользователь всегда начинает с представления запуска. Это представление фиксирует начальное состояние и позволяет пользователю указать, какое действие вызывать, вызывая событие пользовательского интерфейса, например нажатие кнопки. Например, для просмотра ипотечного кредита пользователь может ввести свой идентификатор в текстовое поле и затем нажать кнопку «Поиск».

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

Хотя гиперссылки чаще всего используются на веб-сайтах, они также применяются и к веб-приложениям (см. Эту запись в блоге).для обсуждения разницы между веб-сайтом и веб-приложением). Гиперссылки поддерживают повторное использование, выступая в качестве языка обмена в составных приложениях. Одно приложение может ссылаться на ресурс в другом приложении вместо необходимости дублировать его функциональность. Фактически, запрос может исходить от устаревшего приложения, которое даже не имеет веб-интерфейса. В этом случае вы, скорее всего, будете помещать пользователя в веб-приложение где-то посередине. Как оказалось, такая ситуация довольно распространена. Когда вы посещаете блог, вы начинаете на экране поиска, чтобы найти запись для чтения? Скорее всего, не. Больше раз, чем нет, вы нажимаете на ссылку, чтобы просмотреть конкретную запись в блоге.

Смысл этого обсуждения в том, что первоначальные запросы (называемые в JSF запросами не-лиц) могут быть столь же важными, как и отправка форм (запросов-граней), будь то веб-сайт или веб-приложение. В прошлом JSF изо всех сил пытался поддержать вышеупомянутый сценарий, уделяя гораздо больше внимания запросам лиц. JSF 2 исправляет этот дисбаланс, вводя параметры представления и компоненты пользовательского интерфейса, создающие гиперссылки. Параметры просмотра позволяют приложению реагировать на запрос ресурса путем включения формальной обработки параметров запроса в жизненный цикл JSF для запросов GET и POST. Параметры просмотра не ограничиваются потреблением данных. Они двунаправлены. JSF 2 может распространять данные, полученные параметрами просмотра, при создании закладок URL,с дополнительным поведением для URL-адресов перенаправления, создаваемых случаями навигации по перенаправлению.

Мы начнем с изучения параметров представления, как они определены и как они работают в жизненном цикле JSF. Затем вы узнаете, как они работают в тандеме с новыми компонентами, создающими гиперссылки, и обработчиком навигации, обеспечивающим «закладочную» поддержку JSF.

Представляем параметры просмотра

Документация по API описывает параметр представления, представленный классом компонента javax.faces.component.UIViewParameter , как декларативную привязку между параметром запроса и свойством модели. Привязка к свойству модели выражается с использованием выражения значения EL (например, # {blog.entryId} ). Если выражение опущено, параметр запроса связывается с переменной области запроса с тем же именем.

Вот простой пример параметра представления, который отображает значение параметра запроса с именем id на свойство стиля JavaBean с именем entryId в управляемом компоненте с именем blog .

<f:viewParam name="id" value="#{blog.entryId}"/>

Предполагая, что
свойство
entryId в управляемом компоненте блога имеет тип long, для свойства будет присвоено значение 9 при запросе следующего URL-адреса:

http: //domain/blog/entry.jsf? Id = 9 

Но подождите, это еще не все! Значение параметра запроса сначала преобразуется и проверяется, прежде чем присваивается свойству модели. Такое поведение должно звучать знакомо. Это потому, что он отражает обработку привязок ввода формы по запросу лица. В некотором смысле, параметры просмотра превращают строку запроса в альтернативную отправку формы. И, как и при вводе формы, параметры просмотра также обрабатываются во время запросов лиц. Полный жизненный цикл параметров представления будет рассмотрен позже, когда мы рассмотрим распространение параметров представления.

Прежде чем идти дальше, важно отметить, что параметры представления доступны только при использовании нового языка декларации представления (VDL), стандартизированной версии Facelets. Основная причина в том, что EG JSR-314 согласился с тем, что не следует создавать новые функции для поддержки JSP, поскольку он не рекомендуется использовать как обработчик представления в JSF 2.

Возможно, вы думаете …

Разве это уже не возможно?

Если вы опытный разработчик JSF, вы, возможно, знаете, что уже можно сопоставить значение параметра запроса со свойством модели. Назначение объявляется путем ссылки на элемент карты # {param} в элементе <managed-property> объявления управляемого компонента. Например, вы могли бы альтернативно сопоставить параметр запроса id с управляемым bean-компонентом блога в face-config.xml следующим образом:

 <managed-bean>
<managed-bean-name>blog</managed-bean-name>
<managed-bean-class>com.acme.Blog</managed-bean-class>
<managed-property>
<property-name>entryId</property-name>
<value>#{param['id']}</value>
</managed-property>
</managed-bean>

На этом сходство заканчивается. Параметры просмотра выходят за рамки этого простого назначения, предоставляя:

  • Детализация, ориентированная на представление (отображение свойств в определении управляемого компонента является глобальным для приложения)
  • Пользовательские конвертеры и / или валидаторы (вместе с сообщениями об ошибках)
  • Би-Направленность

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

Достаточно сказать, что хотя отображение свойств в определении управляемого компонента работает, оно довольно анемично. Параметры просмотра намного более адекватны и надежны в сравнении. Что касается темы в этой статье, параметры представления являются ключом к обеспечению поддержки закладок в JSF. А поскольку закладки связаны с конкретными представлениями, то и параметры просмотра должны.

Это все в поле зрения

Как вы уже догадались, параметры вида ориентированы на вид. Это означает, что они каким-то образом должны быть связаны с одним или несколькими представлениями (например, в отличие от связи с управляемым компонентом). Однако до этого момента не было возможности связать расширяемые метаданные без рендеринга с представлением JSF. Поэтому EG сначала должен был найти место в дереве компонентов пользовательского интерфейса, чтобы прикрепить метаданные, такие как параметры представления. Это привело к введению аспекта метаданных
UIViewRoot . В следующем разделе будет представлен этот новый аспект и то, как он используется для размещения параметров представления для определенного представления или даже набора представлений. Затем мы рассмотрим, как обрабатываются параметры представления в жизненном цикле JSF.

Фасет метаданных представления

Параметры представления предоставляют информацию о том, как должны обрабатываться параметры запроса, когда представление запрашивается или связывается с ним. Параметры вида не отображаются сами. Поэтому мы говорим, что они являются частью метамодели представления и описаны с использованием метаданных. Итак, вопрос в том, «Где должны жить эти метаданные?»

Оказывается, представление JSF, представленное в корне классом компонента javax.faces.component.UIViewRoot , уже содержит некоторые метаданные. В настоящее время эти метаданные состоят из строковых значений для определения параметров, таких как языковой стандарт, набор визуализации и тип содержимого представления, и выражений методов, которые обозначают наблюдатели фазы для конкретного вида. Например:

<f:view locale="en_US" afterPhase="#{myBean.afterPhaseListener}">
...
</f:view>

Хотя значения могут быть назначены этим свойствам метаданных явно в коде Java, чаще они назначаются декларативно с использованием соответствующих атрибутов
тега компонента
<f: view> . Но ни
UIViewRoot, ни его компонентный тег
<f: view> не могут вместить сложные метаданные, то есть метаданные, которые не могут быть описаны одним атрибутом. Это были фаска вида метаданных приходит.
Фаска вида метаданных является зарезервированным аспектом
UIViewRoot , названный
javax_faces_metadata

, который может содержать произвольно сложную ветвь компонентов пользовательского интерфейса, которые предоставляют дополнительные метаданные для представления. Аспекты являются особыми, потому что они игнорируются обходом дерева компонентов пользовательского интерфейса, что требует обязательного запроса для перехода к одному из них. Этот аспект делает аспект идеальным кандидатом для скрытия некоторых метаданных для представления, к которому можно получить доступ по требованию.

Фасет метаданных представления выглядит как любой другой фасет в дереве компонентов пользовательского интерфейса. Он должен быть объявлен как прямой потомок
<f: view> в шаблоне представления следующим образом:

<f:view>
<f:facet name="javax_faces_metadata">
...
</f:facet>
...
</f:view>

Примечание. Если вы используете Facelets, возможно, вы не знакомы с тегом <f: view>, поскольку он не является обязательным в Facelets. Когда вы добавляете его в свой шаблон, это должен быть самый внешний компонентный тег, но он не обязательно должен быть корнем документа.

Поскольку фасет метаданных представления является встроенным фасетом и, как ожидается, будет активно использоваться, тег псевдонима
<f: metadata> был введен в качестве сокращения для формального определения фасета, показанного выше:

<f:view>
<f:metadata>
...
</f:metadata>
...
</f:view>

Теперь у нас есть место в дереве компонентов пользовательского интерфейса для хранения метаданных, относящихся к представлению. Но зачем определять метаданные в шаблоне представления? Это все о повторном использовании и последовательности.

Описание метаданных представления с помощью компонентов пользовательского интерфейса

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

Поскольку мы используем компоненты пользовательского интерфейса для описания метаданных представления, то имеет смысл рассматривать
UIViewParameter как любой другой компонент ввода. Фактически это расширяет
UIInput . Это позволяет нам регистрировать пользовательские преобразователи и валидаторы на
UIViewParameter без каких-либо специальных оговорок. Вот пример:

<f:viewParam name="id" value="#{blog.entryId}">
<f:validateLongRange minimum="1"/>
</f:viewParam>

Примечание. Позже в этой серии вы узнаете, что как и входные компоненты, параметры представления могут применять ограничения, определенные аннотациями Bean Validation (или XML), что делает ненужными явные теги проверки, такие как это.

Но есть одно предупреждение для встраивания метаданных представления в шаблон. Без специальных положений извлечение метаданных потребовало бы построения всего представления (т. Е. Дерева компонентов пользовательского интерфейса). Мало того, что это будет дорого и не нужно, если целью не является визуализация представления, это также может иметь побочные эффекты. Когда построено дерево компонентов, выражения значений в обработчиках тегов Facelets оцениваются, что может изменить состояние системы.

Чтобы предотвратить эти противодействия, фасет метаданных представления получает специальную обработку в спецификации. В частности, должна быть возможность извлечь и построить его отдельно от остального дерева компонентов. Ранее я упоминал, что параметры представления доступны только в Facelets, но не в JSP, из-за решения руководства. Существует также техническая причина, по которой параметры просмотра зависят от Facelets. Только Facelets могут обеспечить необходимое разделение между синтаксическим анализом шаблона и построением дерева компонентов, что позволяет обрабатывать фрагмент шаблона изолированно. Результатом этой операции является подлинное дерево компонентов пользовательского интерфейса, представленное
UIViewRoot, который содержит только фасет метаданных представления и его дочерние элементы. Для всех намерений и целей, как будто шаблон представления содержал только этот один дочерний элемент.

Используя следующую логику, можно получить метаданные для произвольного представления в любой момент времени. Этот интеллектуальный анализ данных появится позже, когда мы поговорим о распространении параметров представления.

String viewId = "/your_view_id.xhtml"
FacesContext ctx = FacesContext.getCurrentInstance();
ViewDeclarationLanguage vdl = ctx.getApplication().getViewHandler()
.getViewDeclarationLanguage(ctx, viewId);
ViewMetadata viewMetadata = vdl.getViewMetadata(ctx, viewId);
UIViewRoot viewRoot = viewMetadata.createMetadataView(ctx);
UIComponent metadataFacet = viewRoot.getFacet(UIViewRoot.METADATA_FACET_NAME);

На этом этапе вы можете получить
компоненты
UIViewParameter , которые являются дочерними элементами фасета, чтобы, возможно, получить доступ к сопоставлениям параметров представления. Скорее, однако, вы будете искать свои собственные пользовательские компоненты, чтобы вы могли выполнить пользовательское поведение до отображения представления (например, действия представления).

Извлечение метаданных представления очень умно, потому что, хотя оно строит только частичное представление, оно все же учитывает композиции Facelets. Это означает, что вы можете поместить свои метаданные в общий шаблон и включить его. Используя некоторую творческую аранжировку, вы можете применять общие метаданные к шаблону представлений. Вот пример:

<f:view>
<f:metadata>
<ui:include src="WEB-INF/fragments/common_view_metadata.xhtml"/>
...
</f:metadata>
...
</f:view>

Вы узнали, что определение фасета метаданных представления предоставляет следующие сервисы для JSF:

  • Произвольно сложные метаданные, которые могут повторно использовать существующую компонентную инфраструктуру
  • Метаданные хранятся в представлении или в общем шаблоне, а не во внешнем XML-файле.
  • Может быть извлечен и обработан без каких-либо побочных эффектов (идемпотент)
  • Общие объявления метаданных могут совместно использоваться несколькими представлениями

Теперь, когда вы хорошо разбираетесь в фасете метаданных представления, пришло время разработать конкретный пример параметров представления на практике. Мы рассмотрим, как обеспечить выполнение предварительных условий и загрузить данные по первоначальному запросу, используя информацию из строки запроса. Затем вы узнаете, как эта информация распространяется при переходе пользователя к другим представлениям.

Параметры ткачества в жизненном цикле

В этой статье несколько раз упоминался случай использования загрузки записи блога с URL-адреса путем передачи значения параметра id нашему управляемому компоненту при первоначальном запросе. Давайте позволим этому сценарию разыграться. Вот URL пользователь может запросить приходить на сайт:

Http:? //Domain/blog/entry.jsf ид = 9

Начнем с вопроса , что мы делаем со значением , когда он будет назначен на
EntryID имущества
блога управляемый боб. Один из подходов состоит в том, чтобы загружать запись лениво, как только на нее есть ссылка в пользовательском интерфейсе.

<h2>#{blog.blogEntry.title}</h2>
<p>#{blog.blogEntry.content}</p>

Вот как должен выглядеть управляемый компонент для поддержки этого подхода:

@ManagedBean(name = "blog")
public class Blog {
private Long entryId;
private BlogEntry blogEntry;

public Long getEntryId() { return entryId; }
public void setEntryId(Long entryId) { this.entryId = entryId; }

public BlogEntry getBlogEntry() {
if (blogEntry == null) {
blogEntry = blogRepository.findEntry(entryId);
}

return blogEntry;
}
}

Конечно, не имеет смысла отображать запись без идентификатора (и даже может привести к
исключению NullPointerException ). Таким образом, мы действительно должны сделать обязательным параметр запроса id. Мы также добавим сообщение, если оно отсутствует.

<f:metadata>
<f:viewParam name="id" value="#{blog.entryId}" required="true" requiredMessage="No entry specified.">
<f:validateLongRange minimum="1"/>
</f:viewParam>
...
</f:metadata>

Если требуемый параметр запроса отсутствует, вы можете отобразить сообщение об ошибке с помощью
тега <h: messages> . Ошибки преобразования и проверки регистрируются как глобальные
сообщения, так как нет элемента представления, с которым можно было бы связаться.

<h:messages globalOnly="true"/>

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

Просмотр инициализации

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

Системные события уведомляют зарегистрированных слушателей об интересных точках перехода в жизненном цикле JSF на гораздо более детальном уровне, чем фазовые слушатели. В частности, мы заинтересованы в
PreRenderViewEvent , который запускается сразу после построения дерева компонентов (но еще не отображается). Если слово «зарегистрировано» вызывает ужасные воспоминания о дескрипторах XML, не бойтесь. Наблюдение интересующего нас события — это просто добавление одного или нескольких
элементов
<f: event> к фасету метаданных представления.
: <Е события> тег два обязательный атрибут,
тип и
слушатель .
Типа атрибут является именем события , чтобы наблюдать , полученным путем удаления
событий суффикса с конца имени класса событий и decaptializing результата. Нас интересует только одно событие —
preRenderView .
слушатель

Атрибут — это выражение привязки метода, указывающее либо на метод без аргументов, либо на метод, который принимает
SystemEvent .

<f:metadata>
...
<f:event type="preRenderView" listener="#{blog.loadEntry}"/>
</f:metadata>

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

public void loadEntry() {
blogEntry = blogRepository.findEntry(entryId);
}

Если запись не может быть найдена, вы можете добавить условную логику в представление для отображения сообщения об ошибке:

<c:if test="#{blog.blogEntry eq null}">
The blog entry you requested does not exist.
</c:if>

В идеале было бы лучше вообще не отображать вид. Вы можете заставить навигацию происходить, используя NavigationHandler API.

public void loadEntry() {
try {
blogEntry = blogRepository.findEntry(entryId);
} catch (NoSuchEntryException e) {
FacesContext ctx = FacesContext.getCurrentInstance();
ctx.getApplication().getNavigationHandler()
.handleNavigation(ctx, "#{blog.loadEntry}", "invalid");
}
}

Единственная проблема заключается в том, что метод слушателя будет вызываться, даже если параметр представления не может быть успешно преобразован, проверен и назначен свойству модели. Еще раз, есть функция JSF 2 для спасения. Вы можете использовать новый
метод isValidationFailed () в
FacesContext, чтобы проверить, произошла ли ошибка преобразования или проверки при обработке параметров представления.

public void loadEntry() {
FacesContext ctx = FacesContext.getCurrentInstance();
if (ctx.isValidationFailed()) {
ctx.getApplication().getNavigationHandler()
.handleNavigation(ctx, "#{blog.loadEntry}", "invalid");
return;
}

// load entry
}

До сих пор мы имели дело с тривиальной строкой в ​​длинное преобразование. Но параметры просмотра позволяют вам представлять более сложные данные, если у вас есть конвертер, который может маршировать значение из (и в) строки. Давайте предположим, что мы хотим позволить пользователю просматривать записи в блоге, которые попадают в диапазон дат. Даты до и после могут быть закодированы в URL следующим образом:


/entries.jsf?after=2007-12-31&before=2009-01-01 Затем

эти значения можно преобразовать в объекты Date с помощью
тега конвертера <f: convertDateTime> и присваивается свойствам Date для управляемого компонента следующим образом:

<f:metadata>
<f:viewParam name="after" value="#{blog.afterDateFiler}">
<f:convertDateTime type="date" pattern="yyyy-MM-dd"/>
</f:viewParam>
<f:viewParam name="before" value="#{blog.beforeDateFiler}">
<f:convertDateTime type="date" pattern="yyyy-MM-dd"/>
</f:viewParam>
<f:event type="preRenderView" listener="#{blog.loadEntries}"/>
</f:metadata>

Мы снова используем
прослушиватель
PreRenderViewEvent для загрузки данных до отображения страницы, в этом случае выполняется фильтрация коллекции записей блога, которые будут отображаться.

Эмуляция поведения ориентированной на действие инфраструктуры, которую продемонстрировали предыдущие примеры, является одним из применений
PreRenderViewEvent . Другой способ — выполнять функцию обратного вызова жизненного цикла для программного создания или настройки дерева компонентов пользовательского интерфейса после того, как оно «надувается» из шаблона представления. Возможно, вы хотите построить часть дерева динамически из структуры данных. Выполнение этого в JSF потребует «привязки» свойства bean-компонента к существующему компоненту пользовательского интерфейса, объявленного с использованием выражения значения EL в
привязке.атрибут тега. Но этот подход действительно уродлив, потому что вы должны поместить логику добавления дерева в метод получения или установки свойства JavaBean, в зависимости от того, создается или восстанавливается представление.
PreRenderViewEvent предлагает гораздо более точный и самостоятельно задокументировать крюк.

Как вы уже видели, наконец-то можно ответить на закладку URL в JSF (без боли и хрупкого кода). Но до этого момента все, что мы сделали, это взяли, возьми, возьми. Чтобы поддержка закладок была завершена, нам нужно иметь возможность создавать закладки для URL. Это подводит нас к теме распространения параметров.

Нажмите параметры на

Если бы параметры представления могли принимать только данные, отправленные через строку запроса URL-адреса, даже с учетом встроенного преобразования и проверки, которые они предоставляют, они действительно не были бы такими уж полезными. Что делает их такими привлекательными, так это то, что они являются двунаправленными, то есть они также распространяются на последующие запросы и довольно прозрачно. Последующим запросом может быть запрос лиц, который нацеливается на текущий вид, или запрос не лиц к виду, который имеет параметры вида, который преобразуется в URL-адрес для закладок. Запрос на доступный для закладки URL может исходить либо от ссылки на странице, либо от события перенаправления навигации. Мы рассмотрим, как параметры представления распространяются во всех этих случаях в этом разделе.

Сохраняется в дереве компонентов

Давайте вернемся к представлению записи в блоге и рассмотрим, что произойдет, если у нас появится форма комментария под сообщением. Форма комментария может быть определена следующим образом:

<h:form>
<h:inputText value="#{comment.name}" required="true"/>
<h:inputTextarea value="#{comment.text}" required="true"/>
<h:commandButton action="#{commentHandler.post}" value="Post"/>
</h:form>

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

Когда кодирование представления (т. Е. Рендеринг) завершено, значения параметров представления скрываются в сохраненном состоянии дерева компонентов пользовательского интерфейса. Когда дерево компонента восстанавливается при обратной передаче, например, когда отправляется форма комментария, сохраненные значения параметров вида применяются к модели. Это позволяет параметрам просмотра хорошо сочетаться с существующим дизайном JSF. Начальное состояние, предоставляемое URL-адресам для параметров представления, может поддерживаться до тех пор, пока пользователь взаимодействует с представлением (например, инициирует запросы на запросы через события пользовательского интерфейса). В этом случае вы можете рассматривать параметры представления как элегантную замену скрытых полей формы.

Однако если пользователь добавит в закладки URL-адрес после публикации комментария, ссылка на запись в блоге будет потеряна. Это связано с тем, что после запроса POST местоположение браузера не содержит строку запроса. Вот что увидит пользователь:


http: //domain/blog/entry.jsf

Если мы будем следовать рекомендациям, мы все равно захотим реализовать
шаблон
Post / Redirect / Get . Это дает нам возможность заново заполнить строку запроса URL. В прошлом для этого требовался явный вызов
метода
redirect () ExternalContext внутри метода действия.

FacesContext.getCurrentInstance().getExternalContext().redirect("/entry.jsf?id=" + blog.getEntryId());

Этот явный (и навязчивый) вызов был необходим, поскольку в случае навигации не было никакого способа добавить параметры в строку запроса. Теперь параметры просмотра могут позаботиться об этом за нас. Мы можем указать JSF кодировать параметры представления идентификатора целевого представления в URL-адрес перенаправления, включив
атрибут
include-redirect-params в
элементе
<redirect> .

<navigation-rule>
<from-view-id>/entry.xhtml</from-view-id>
<navigation-case>
<from-action>#{commentHandler.post}</from-action>
<to-view-id>#{view.viewId}</to-view-id>
<redirect include-view-params="true"/>
</navigation-case>
</navigation-rule>

Мы перейдем к навигации больше в следующей статье этой серии. Давайте поговорим об этих обычных старых гиперссылках на странице. Мы хотим, чтобы они тоже были в закладках. Это означает, что состояние должно быть закодировано в URL, на который они указывают. Еще раз, параметры просмотра вступают в игру.

Закладки ссылки

Давайте теперь предположим, что мы хотим создать закладку (постоянную ссылку) на текущую запись в блоге. Вы можете связать непосредственно с другой точкой зрения JSF с помощью
итогового атрибута новых тегов компонентов гиперссылка продуцирующих,
<ч: ссылка> и
<ч: кнопку> . Эти теги компонента представлены классом компонента
javax.faces.component.UIOutcomeTarget . (Причина, по которой атрибут назван
result, а не
viewId, будет объяснена в следующей статье. Пока просто знайте, что значением
атрибута
result может быть идентификатор представления). Оба эти тега компонента поддерживают кодирование параметров представления в строку запроса URL, как указано
атрибут includeViewParams . Вот как определяется постоянная ссылка:

<h:link outcome="/entry.xhtml" includeViewParams="true" value="Permalink"/>

Значением по умолчанию
includeViewParams является false. Поскольку для него установлено значение true, параметры представления читаются в обратном порядке и добавляются в строку запроса ссылки. Вот HTML-код, сгенерированный этим компонентным тегом, при условии, что идентификатор записи равен 9:

<a href="/blog/entry.jsf?id=9">Permalink</a>

Ссылка создается с использованием нового
метода getBookmarkableURL () в
API
ViewHandler . Этот метод вызывает
функцию encodeBookmarkableURL () в
API
ExternalContext, чтобы при необходимости прикрепить маркер идентификатора сеанса. Эти методы дополняют методы
getRedirectURL () и
encodeRedirectURL () для
ViewHandler и
ExternalContext соответственно. В среде сервлетов реализации оказываются одинаковыми, но дополнительные методы служат как заявлением о намерениях, так и точкой расширения для сред, в которых URL-адрес ссылки и URL-адрес перенаправления обрабатываются по-разному, например портлет.

Обратите внимание, что к пути добавляется контекстный путь приложения (/ blog), расширение изменяется с суффикса представления (.xhtml) на отображение сервлета JSF (.jsf), а строка запроса содержит имя и значение посмотреть параметр, прочитанный из модели. Если бы вы использовали
тег <h: outputLink> , вам пришлось бы делать все эти вещи вручную. Именно поэтому ЭГ сочла необходимым ввести этот компонент.

Мы можем сделать один лучше. Если
атрибут
результата отсутствует, предполагается текущий идентификатор представления. Таким образом, мы можем сократить тег до этого:

<h:link includeViewParams="true" value="Permalink"/>

Если вы хотите, чтобы ссылка отображалась в виде кнопки, вместо нее можно использовать
тег компонента
<h: button> .

<h:button includeViewParams="true" value="Permalink"/>

Однако обратите внимание, что в этом случае требуется JavaScript для обновления местоположения браузера при нажатии кнопки, как вы можете видеть из сгенерированного HTML:

<button onclick="window.location.href="/blog/entry.jsf?id=9";">Permalink</button>

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

<f:metadata>
<f:viewParam name="query" value="#{blog.searchString}"/>
<f:viewParam name="category" value="#{blog.category}"/>
<f:viewParam name="page" value="#{blog.page}"/>
</f:metadata>

Тем не менее, ссылка на эти результаты поиска по-прежнему так же проста, как и постоянная ссылка на запись:

<h:link includeViewParams="true" value="Refresh query"/>

Этот компонентный тег будет производить HTML, похожий на этот:

<a href="/blog/entries.jsf?category=JSF&query=features&page=2">Refresh</a>

Что если мы захотим вернуться на предыдущую страницу? В этом случае мы не можем допустить, чтобы параметр представления с именем
page автоматически записывался в строку запроса, поскольку это просто свяжет нас с текущей страницей. Нам нужно переопределить. К счастью, легко закодировать параметр закодированного представления. Вы просто используете стандартный
тег <f: param> , как если бы вы определяли новый параметр строки запроса:

<h:link includeViewParams="true" value="Previous">
<f:param name="page" value="#{blog.page - 1}"/>
</h:link>

Параметры представления, которые закодированы в ссылки на текущий идентификатор представления, довольно интуитивно понятны. Ситуация усложняется, когда мы используем параметры представления для ссылки на другой идентификатор представления. Для этого нужно надеть кепку и подумать.

Просмотр параметров передачи

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

Давайте рассмотрим вариант использования. Предположим, что мы хотим создать ссылку из результатов поиска на одну запись. Мы бы определили ссылку следующим образом:

<ui:repeat var="_entry" value="#{blog.entries}">
<h2>#{_entry.title}</h2>
<p>#{_entry.excerpt}</p>
<p>
<h:link outcome="/entry.xhtml" includeViewParams="true" value="View entry">
<f:param name="id" value="#{_entry.id}"/>
</h:link>
</p>
</ui:repeat>

Вопрос, который нужно задать себе, заключается в следующем. Msgstr «Строка поиска, категория и смещение страницы включены в URL для записи?» Я надеюсь, что вы сказали «Нет». Причина в том, что когда создается URL для ссылки на запись, тег компонента считывает сопоставления параметров представления, определенные в шаблоне /entry.xhtml. Единственным параметром, отображаемым в этом шаблоне, является идентификатор записи. Чтобы сохранить вектор фильтра, параметры представления, определенные в представлении /entries.xhtml, также должны быть в шаблоне /entry.xhtml. Ага! Поскольку это общие параметры представления, мы должны определить их в общем шаблоне:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core">
<f:viewParam name="page" value="#{blog.page}"/>
<f:viewParam name="query" value="#{blog.searchString}"/>
<f:viewParam name="category" value="#{blog.category}"/>
</ui:composition>

Затем мы можем включить этот шаблон в каждое представление, которое должно сохранить эти параметры представления:

<f:view>
<f:metadata>
<ui:include src="/WEB-INF/fragments/common_view_params.xhtml"/>
<f:viewParam name="id" value="#{blog.entryId}"/>
...
</f:metadata>
</f:view>

Имейте в виду, что если пользователь перейдет к записи после выполнения поиска, URL-адрес записи, отображаемой в строке адреса браузера, теперь будет содержать вектор фильтра. Но если вы хотите, чтобы пользователь мог вернуться к поисковому фильтру (без использования кнопки «Назад»), это то, что вам нужно. Вы всегда можете предоставить простую постоянную ссылку для закладки только записи.

Даже если вы сейчас определяете параметры представления в каждом из представлений, это не означает, что URL будет завален пустыми параметрами строки запроса, когда они не используются. Параметры представления кодируются (то есть добавляются в строку запроса), только если значение не равно нулю. В противном случае, нет никакого следа параметра просмотра.

Теперь вы узнали, как параметры представления распространяются во время обратной передачи, перенаправления и в URL-адрес для закладок. Основным преимуществом этого процесса является то, что он прозрачен. Вам не нужно беспокоиться о каждом параметре запроса, который содержит состояние в строке запроса URL-адреса. Вместо этого JSF интерпретирует метаданные параметра представления, определенные в шаблоне целевого представления, и автоматически добавляет эти пары имя / значение в URL при активации этой функции.

Добавить в закладки

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

Эта статья началась с знакомства с аспектом метаданных представления, который представляет собой общее средство для определения метамодели представления, которая повторно использует существующую инфраструктуру компонентов пользовательского интерфейса. Вы узнали, что параметры представления и прослушиватели PreRenderViewEvent являются первыми стандартными реализациями метаданных представления. Вы видели, как комбинация этих двух функций позволяет вам захватывать начальное состояние из строки запроса URL-адреса, проверять предварительные условия и загружать данные для представления все перед его отображением. Наконец, вы узнали, как значения параметров представления распространяются на последующие запросы.

В этой серии статей мы подробнее рассмотрим усовершенствования навигации, сделанные в JSF 2, и объясним, как эти изменения связаны с возможностью создания закладок, о которой вы узнали в этой статье. Так добавь в закладки
эту серию и зайдите снова скоро!