Spring Web Flow — работа с JavaScript (Spring JavaScript Quick Reference)
12. Spring Web Flow — работа с JavaScript (Spring JavaScript Quick Reference)
12.1. Введение
Spring Javascript (spring-js) — легковесная абстракция над распространенными инструментами JavaScript, такими как Dojo. Она предоставляет общую модель программирования на стороне клиента, позволяющую постепенно улучшить веб страницу при помощи виджетов и Ajax.
Использование Spring JS API продемонстрировано в версии проекта Spring MVC + Web Flow — Spring Travel.
12.2. Обслуживание ресурсов Javascript
Spring JS предоставляет дженерик ResourceServlet для работы с веб ресурсами, такими как JavaScript и CSS файлы из jar файлов, а так же из корневой директории webapp. Этот сервлет предоставляет удобный способ для работы с файлами Spring.js на ваших страницах. Для развертывания этого сервлета необходимо объявить следующие строчки в web.xml:
1 2 3 4 5 6 7 8 9 10 11 |
<!-- Serves static resource content from .jar files such as spring-js.jar --> <servlet> <servlet-name>Resource Servlet</servlet-name> <servlet-class>org.springframework.js.resource.ResourceServlet</servlet-class> </servlet> <!-- Map all /resources requests to the Resource Servlet for handling --> <servlet-mapping> <servlet-name>Resource Servlet</servlet-name> <url-pattern>/resources/*</url-pattern> </servlet-mapping> |
Обратите внимание, что начиная с версии 3.0.4, Spring Framework содержит замену для ResourceServlet (подробнее в Spring Framework documentation). С новым элементом <mvc:resources> запросы ресурсов (.js, .css) обрабатываются в DispatcherSevlet без необходимости отдельного ResourceServlet. Ниже представлена конфигурация Spring MVC для примера mvc-booking:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <mvc:annotation-driven/> <mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/web-resources/" /> ... </beans> |
Эти настройки маппят запросы для /resources к ресурсам, найденным под путем/META-INF/web-resources в classpath. Вот здесь и находятся ресурсы Spring JavaScript. Однако, вы можете изменить местонахождение атрибутов из конфигурации выше, для обработки ресурсов из любого другого classpath или относительного пути веб приложения.
Обратите внимание, что полный URL к ресурсам зависит от настроек вашего DispatcherServlet. В примере mvc-booking был выбран маппинг как у сервлета по умолчанию ‘/‘:
1 2 3 4 5 6 7 8 9 |
<servlet> <servlet-name>Spring MVC Dispatcher Servlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Spring MVC Dispatcher Servlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> |
Это означает, что полный путь URL для загрузки Spring.js будет /myapp/resources/spring/Spring.js. Если бы DispatcherServlet маппился по /main/*, то полный путь выглядел бы так — /myapp/main/resources/spring/Spring.js.
При использовании маппинга сервлета по умолчанию так же рекомендуется добавить настройки ниже в конфигурацию Spring MVC, которые позволят гарантировать, что любой запрос к ресурсам, который не был обработан вашим Spring MVC, будет делегирован обратно в Servlet контейнер.
1 2 3 4 5 6 7 8 9 10 11 12 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> ... <mvc:default-servlet-handler /> </beans> |
12.3. Включение Spring Javascript на странице
Spring JS спроектирован так, что реализация его API может быть построена для любого популярного инструмента Javascript. Исходная реализация Spring.js построена на инструменте Dojo.
Использование Spring Javascript на странице требует включения основного инструментария — обычно это файл основного интерфейса Spring.js, а так же файл реализации библиотеки — Spring-(library implementation).js. В качестве примера, ниже показано реализация Dojo из Spring.js, используемая ResourceServlet:
1 2 3 |
<script type="text/javascript" src="<c:url value="/resources/dojo/dojo.js" />"> </script> <script type="text/javascript" src="<c:url value="/resources/spring/Spring.js" />"> </script> <script type="text/javascript" src="<c:url value="/resources/spring/Spring-Dojo.js" />"> </script> |
Когда используется система виджетов базовой библиотеки, как правило, необходимо так же включить некоторые ресурсы CSS, для получения желаемого внешнего вида. Для примера booking-mvc используется встроенная таблица стилей Dojo tundra.css:
1 |
<link type="text/css" rel="stylesheet" href="<c:url value="/resources/dijit/themes/tundra/tundra.css" />" /> |
12.4. Декораторы Spring Javascript
Центральной концепцией в Spring Javascript является идея применения декораторов к существующему дереву DOM. Эта техника используется для постепенного улучшения веб страницы таким образом, что страница останется функциональной даже в менее прогрессивных браузерах. Для предоставления декораторов используется метод addDecoration.
Следующий пример демонстрирует улучшение тега Spring MVC <form:input>, предоставляя более богатое поведение:
1 2 3 4 5 6 7 |
<form:input id="searchString" path="searchString"/> <script type="text/javascript"> Spring.addDecoration(new Spring.ElementDecoration({ elementId: "searchString", widgetType: "dijit.form.ValidationTextBox", widgetAttrs: { promptMessage : "Search hotels by name, address, city, or zip." }})); </script> |
ElementDecoration используется для предоставления богатого поведения виджета уже существующего дерева DOM. Этот тип декорирования не стремится скрыть основной инструментарий, поэтому нативные инструменты используются напрямую. Этот подход позволяет вам использовать общую модель декоратора для внедрения в любой виджет основного инструмента в необходимом порядке. Чтобы увидеть больше примеров использования декораторов, предоставляющих валидацию на стороне клиента, смотрите пример приложения booking-mvc.
При использовании элемента ElementDecoration для применения с виджетами, предоставляющими богатые возможности валидации, необходимо, в общем случае, предотвратить подтверждение формы для сервера, до прохождения валидации. Это можно сделать с использованием ValidateAllDecoration:
1 2 3 4 |
<input type="submit" id="proceed" name="_eventId_proceed" value="Proceed" /> <script type="text/javascript"> Spring.addDecoration(new Spring.ValidateAllDecoration({ elementId:'proceed', event:'onclick' })); </script> |
Это декорирует кнопку «Proceed» с помощью специального события onclick, которое запускает валидацию на стороне клиента и не позволяет засабмитить форму до ее успешного прохождения.
AjaxEventDecoration является слушателем событий клиентской стороны, который запускает удаленный Ajax запрос к серверу. Также автоматически регистрирует функцию обратного вызова на ссылке при ответе:
1 2 3 4 5 6 7 8 |
<a id="prevLink" href="search?searchString=${criteria.searchString}&page=${criteria.page - 1}">Previous</a> <script type="text/javascript"> Spring.addDecoration(new Spring.AjaxEventDecoration({ elementId: "prevLink", event: "onclick", params: { fragments: "body" } })); </script> |
Здесь декорировано событие onclick ссылки «Previous Results» с Ajax вызовом, также передающее специальные параметры, уточняющие как фрагмент будет перерисован в ответе. Обращаем внимание, что эта ссылка останется полностью функциональной, даже если Javascript недоступен на клиенте (чтобы узнать как как этот запрос обрабатывается на сервере читайте в Section 12.5, “Handling Ajax Requests”)
Так же возможно предоставить более чем один декоратор для элемента. Следующий пример показывает декорирование кнопки с Ajax и полной валидацией с подавлением подтверждения:
1 2 3 4 5 |
<input type="submit" id="proceed" name="_eventId_proceed" value="Proceed" /> <script type="text/javascript"> Spring.addDecoration(new Spring.ValidateAllDecoration({elementId:'proceed', event:'onclick'})); Spring.addDecoration(new Spring.AjaxEventDecoration({elementId:'proceed', event:'onclick',formId:'booking', params:{fragments:'messages'}})); </script> |
Так же возможно задекорировать несколько элементов одним выражением, используя Dojo’s query API. Следующий пример показывает декорирование набора чекбоксов виджетами Dojo Checkbox:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<div id="amenities"> <form:checkbox path="amenities" value="OCEAN_VIEW" label="Ocean View" /></li> <form:checkbox path="amenities" value="LATE_CHECKOUT" label="Late Checkout" /></li> <form:checkbox path="amenities" value="MINIBAR" label="Minibar" /></li> <script type="text/javascript"> dojo.query("#amenities input[type='checkbox']").forEach(function(element) { Spring.addDecoration(new Spring.ElementDecoration({ elementId: element.id, widgetType : "dijit.form.CheckBox", widgetAttrs : { checked : element.checked } })); }); </script> </div> |
12.5. Обработка запросов Ajax
Обработка Ajax ответа на стороне клиента с помощью Spring Javascript построена на понятии получения «фрагментов» от сервера. Эти фрагменты являются стандартным HTML, что позволяет заменить часть существующей страницы. Основной задачей на сервере является необходимость определить какие части от полного ответа необходимо выделить для частичного рендера.
Для того, чтобы существовала возможность рендера части фрагментов из полного ответа, необходимо, чтобы полный ответ был построен с использованием технологии шаблонов, которая позволяет использовать композицию при построении ответа. Таким образом позволяя ссылаться и перерисовывать конкретные части индивидуально. Spring Javascript предоставляет несколько простых расширений Spring MVC, которые используют Tiles для достижения этой функциональности. Такая же техника теоретически может быть использована с любой системой шаблонов, поддерживающую композицию.
Функциональность удаленного доступа Ajax Spring Javascript построена на понятии, что обработка в ядре кода Ajax запроса не должна отличаться от стандартного запроса браузера. Таким образом нет необходимости прямого присутствия специальной информации об запросе Ajax в коде, а значит возможно использовать один обработчик для двух стилей запросов.
12.5.1. Предоставление специфичной библиотеки AjaxHandler
Главным интерфейсом для интеграции различных Ajax библиотек с предоставлением поведения Ajax в стиле Web Flow (например отсутствие перенаправлением для обновления части страницы) является org.springframework.js.AjaxHandler. SpringJavascriptAjaxHandler с настройками по умолчанию способен распознать Ajax запрос, предоставленный Spring JS client-side API, и может реагировать подходящим образом в случае, когда требуется редирект. Для того, чтобы интегрировать другую библиотеку Ajax (например чистую JavaScript библиотеку или высокоуровневую абстракцию, вроде Ajax-совместимая библиотека компонентов JSF), может быть внедрен собственный AjaxHandler в FlowHandlerAdapter или FlowController.
12.5.2. Обработка Ajax запросов с помощью контроллеров Spring MVC
Для обработки Ajax запросов с помощью контроллеров Spring MVC требуется добавить конфигурацию, предоставленную расширениями Spring MVC в ваш Spring application context, для возможности рендера частичного ответа(важно, что расширения требуют использование шаблонов Tiles):
1 2 3 |
<bean id="tilesViewResolver" class="org.springframework.js.ajax.AjaxUrlBasedViewResolver"> <property name="viewClass" value="org.springframework.webflow.mvc.view.FlowAjaxTilesView"/> </bean> |
Это настраивает AjaxUrlBasedViewResolver, который в свою очередь интерпретирует Ajax запросы и создает объекты FlowAjaxTilesView для обработки соответствующих фрагментов. Обратите внимание, что FlowAjaxTilesView способен обрабатывать рендер обоих запросов — Web Flow и чистого Spring MVC. Фрагменты соответствуют отдельным атрибутам в описании представления Tiles. Например, возьмем следующее описание представления Tiles:
1 2 3 4 5 6 7 8 |
<definition name="hotels/index" extends="standardLayout"> <put-attribute name="body" value="index.body" /> </definition> <definition name="index.body" template="/WEB-INF/hotels/index.jsp"> <put-attribute name="hotelSearchForm" value="/WEB-INF/hotels/hotelSearchForm.jsp" /> <put-attribute name="bookingsTable" value="/WEB-INF/hotels/bookingsTable.jsp" /> </definition> |
Ajax запрос способен указать «body», «hotelSearchForm» или «bookingsTable» для рендера в качестве фрагмента в запросе.
12.5.3. Обработка Ajax запросов в Spring MVC + Spring Web Flow
Spring Web Flow обрабатывает дополнительную визуализацию фрагментов непосредственно в языке описания потока, используя элемент render. Преимущество этого подхода в том, что выбор фрагментов полностью отделен от кода на стороне клиента, таким образом не нужно передавать в запросе никаких специальных параметров, и в настоящее время они выглядят как в чистом подходе для контроллеров Spring MVC. К примеру, если вы хотите обновить фрагмент «hotelSearchForm» из предыдущего примера в Javascript popup окне, то используйте этот подход:
1 2 3 4 5 6 7 8 |
<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true"> <on-entry> <render fragments="hotelSearchForm" /> </on-entry> <transition on="search" to="reviewHotels"> <evaluate expression="searchCriteria.resetPage()"/> </transition> </view-state> |