На прошлой неделе я имел удовольствие посетить (и представить) Ajax Experience в Сан-Франциско. Коллекция бесед была хороша, и беседы «Reverse Ajax» (Comet) были особенно интересны. Я немного рассказал о JSF и Ajax, и я хотел бы поделиться техническими деталями небольшого «мэшапа», которое я собрал для своего выступления.
Эта демонстрация использует библиотеку Dynamic Faces (с JSF), чтобы проиллюстрировать стандартную службу поиска. Чтобы упростить вещи для демонстрации, в демонстрации используются только три артефакта:
- Одна страница JSP
- Один маленький файл JavaScript
- Один управляемый боб
Во-первых, давайте посмотрим на интерфейс:
Пользовательский интерфейс довольно простой. Вы вводите один или несколько разделенных пробелами символов в текстовое поле «Символы» и нажимаете кнопку «Поиск». Вы можете ввести информацию прокси, если вы находитесь за брандмауэром. Наиболее интересной особенностью демо-версии является опция «Потоковая передача». Если для параметра «Потоковая передача» установлено значение «Вкл.», Клиент будет опрашивать сервер, вызывая транзакции Ajax каждые 10 секунд (или через определенный интервал времени). Опция «Удаленный / Локальный» позволяет переключать демонстрационную версию для использования локальных данных, если сетевое соединение недоступно. Если используется опция «Удаленный», для получения биржевых данных используется служба котировки акций Yahoo . Таблица данных о запасах будет динамически менять размер в зависимости от количества символов, используемых в запросе. Позволять’Взгляните на артефакты, использованные в этой демонстрации.
JSP
Вот фрагмент страницы JSP для демонстрации, показывая соответствующие части:
<f:view> <html> <head> ... ... <jsfExt:scripts/> <script type="text/javascript"> ... ... include_js('javascripts/stock-faces.js'); </script> </head> <body> <h:form id="form" prependId="false"> <h:panelGrid styleClass="title-panel"> <h:outputText value="Stock Query" styleClass="title-panel-text"/> <h:outputText value="Powered By Dynamic Faces" styleClass="title-panel-subtext"/> </h:panelGrid> <h:panelGrid border="1" columns="1" styleClass="panel-input-border"> <h:panelGrid border="1" columns="7"> <h:outputText value="Symbol:"/> <h:inputText id="symbol"/> <h:commandButton id="search" value="Search" onclick="DynaFaces.fireAjaxTransaction(this, {});return false;" actionListener="#{bean.getStockInfo}" /> <h:outputText value="Proxy Host:"/> <h:inputText id="proxyHost"/> <h:outputText value="Proxy Port:"/> <h:inputText id="proxyPort"/> <h:outputText value="Streaming:"/> <h:selectOneMenu id="streaming" value="Off" onchange="toggleStreaming()"> <f:selectItem itemValue="Off" itemLabel="Off"/> <f:selectItem itemValue="On" itemLabel="On"/> </h:selectOneMenu> <h:selectOneMenu id="connection" value="Local"> <f:selectItem itemValue="Local" itemLabel="Local"/> <f:selectItem itemValue="Remote" itemLabel="Remote"/> </h:selectOneMenu> </h:panelGrid> </h:panelGrid> <h:panelGrid id="stockdata" border="1" columns="8" styleClass="panel-data-border" rendered="false"> <h:panelGrid> <f:facet name="header"> <h:outputText value="Symbol"/> </f:facet> </h:panelGrid> <h:panelGrid> <f:facet name="header"> <h:outputText value="Name"/> </f:facet> </h:panelGrid> <h:panelGrid> <f:facet name="header"> <h:outputText value="Open"/> </f:facet> </h:panelGrid> <h:panelGrid> <f:facet name="header"> <h:outputText value="Last"/> </f:facet> </h:panelGrid> <h:panelGrid> <f:facet name="header"> <h:outputText value=""/> </f:facet> </h:panelGrid> <h:panelGrid> <f:facet name="header"> <h:outputText value="Change"/> </f:facet> </h:panelGrid> <h:panelGrid> <f:facet name="header"> <h:outputText value="Change %"/> </f:facet> </h:panelGrid> <h:panelGrid> <f:facet name="header"> <h:outputText value="Volume"/> </f:facet> </h:panelGrid> </h:panelGrid> </h:form> </body> </html> </f:view>
Вот объяснение «синих» разделов во фрагменте кода:
- <jsfExt: scripts /> — это стандартный тег для приложений Dynamic Faces, включающий библиотеку JavaScript Dynamic Faces.
- В include_js ( ‘JavaScripts / СТОК-faces.js’); line — это просто служебная функция, которая загружает JavaScript-файл приложения stock-faces.js (о котором мы поговорим далее).
- К тегу h: commandButton прикреплен обработчик события JavaScript onclick — DynaFaces.fireAjaxTransaction , который отправляет Ajax-запрос на сервер при нажатии кнопки. ActionListener указано # {} bean.getStockInfo будут выполнены на сервере , как обычно. Но ключ в том, что любые представления или манипуляции компонентов JSF, выполняемые на сервере, будут происходить через Ajax.
- Опция streaming — это просто компонент h: selectOneMenu, который имеет обработчик события JavaScript onchange .
- Далее следует «заполнитель» для нашей динамической таблицы данных о запасах. Для отображаемого атрибута установлено значение false , но оно будет установлено равным true из кода приложения, когда есть данные о запасах для возврата.
Получить полный источник этого файла здесь .
Приложение JavaScript
stock-faces.js
var pollId; /** Delay between requests to the server when polling. */ var pollDelay = 10000; /** Start polling the server */ function start() { pollId = setInterval(poll, pollDelay); } /** Stop polling the server */ function stop() { clearInterval(pollId); } function poll() { queueEvent(); DynaFaces.fireAjaxTransaction(null, {}); } function queueEvent() { var actionEvent = new DynaFaces.ActionEvent("search", DynaFaces.PhaseId.INVOKE_APPLICATION); DynaFaces.queueFacesEvent(actionEvent); return false; } function toggleStreaming() { var menu = document.getElementById("streaming"); var idx = menu.selectedIndex; var streaming = menu[idx].value; if (streaming == "Off") { stop(); } else if (streaming == "On") { start(); } }
- Задержка опроса (или интервал времени между вызовами на сервер составляет 10 секунд.
- Функция start () запускает опрос сервера.
- Функция stop () останавливает опрос сервера.
- Функция poll () выполняет две функции:
- Он ставит в очередь событие действия JSF на стороне сервера .
- Запускает Ajax-запрос к серверу, используя библиотеку Dynamic Faces.
- Функция queueEvent () ставит в очередь событие действия JSF на стороне сервера, используя библиотеку Dynamic Faces. Это событие действия будет обработано во время стандартной обработки жизненного цикла JSF при прохождении запроса Ajax.
- Функция toggleStreaming () просто переключает значение элемента управления «потоковым» меню.
Получить полный источник этого файла здесь .
JSF Managed Bean
Bean.java (насколько оригинально …)
/** * This bean has methods to retrieve stock information from * the Yahoo quote service. */ public class Bean { private static final String SERVICE_URL = "http://quote.yahoo.com/d/quotes.csv"; /** * Action method that is used to retrieve stock information. * This method uses two helper methods - one to get the * stock information, and the other to dynamically build * the "data" components for the UI. */ public void getStockInfo(ActionEvent ae) { ... ... stockData = getStockData(symbols); buildUI(stockData); ... } /** * Helper method to get the stock data (remotely). */ private String[] getStockData(String[] symbols) throws IOException, MalformedURLException { String[] data = new String[symbols.length]; for (int i=0; i<symbols.length; i++) { StringBuffer sb = new StringBuffer(SERVICE_URL); sb.append("?s="); sb.append(symbols[i]); sb.append("&f=snol1cp2v=.csv"); String url = sb.toString(); URLConnection urlConn = null; InputStreamReader inputReader = null; BufferedReader buff = null; try { urlConn = new URL(url).openConnection(); inputReader = new InputStreamReader( urlConn.getInputStream()); buff = new BufferedReader(inputReader); data[i] = buff.readLine(); data[i] = data[i].replace( "\"", "" ); ... ... } return data; } /** * Helper method to dynamically add JSF components to display * the data. */ private void buildUI(String[] stockData) { FacesContext context = FacesContext.getCurrentInstance(); UIForm form = (UIForm)context.getViewRoot().findComponent("form"); UIPanel dataPanel = (UIPanel)form.findComponent("stockdata"); String buffer = null; dataPanel.getChildren().clear(); for (int i=0; i<stockData.length; i++) { String[] data = stockData[i].split("\\,"); UIOutput outputComponent = null; UIGraphic imageComponent = null; ... ... // Create and add components wth data values // Symbol outputComponent = new UIOutput(); outputComponent.setValue(data[0]); dataPanel.getChildren().add(outputComponent); // Name outputComponent = new UIOutput(); outputComponent.setValue(data[1]); dataPanel.getChildren().add(outputComponent); // Open Price (if any) outputComponent = new UIOutput(); try { openPrice = new Double(data[2]).doubleValue(); } catch (NumberFormatException nfe) { openPriceAvailable = false; } outputComponent.setValue(data[2]); dataPanel.getChildren().add(outputComponent); ... ... } dataPanel.setRendered(true); }
Этот управляемый компонент JSF имеет метод действия getStockInfo, который выполняет две функции:
- Он использует вспомогательный метод getStockData для связи со службой котировок акций Yahoo (как определено в SERVICE_URL) для получения биржевых данных для всех символов.
- Он использует вспомогательный метод buildUI для создания компонентов JSF (из исходных данных) и добавляет компоненты JSF в представление компонентов JSF. После того, как все компоненты созданы и добавлены, он устанавливает для атрибута рендеринга значение true в компоненте JSF «stockdata».
Метод действия getStockInfo вызывается в двух отдельных случаях:
- Вызывается при нажатии кнопки «Поиск».
- Он также вызывается в результате запроса Ajax «опрос». Это связано с тем, что каждый опрос клиента ставит в очередь событие, связанное с этим обработчиком событий. Обратитесь к методу queueEvent в файле JavaScript stock-faces.js .
Получить полный источник этого файла здесь .
Резюме
Мы увидели, как мы можем объединить возможности сборки компонентов JSF с Ajax для создания динамических приложений. В этом приложении показано использование двух функций Dynamic Faces:
- fireAjaxTransaction
- Удаленная организация событий JSF из JavaScript
Это приложение включено в качестве одного из примеров приложений в проекте jsf-extensions . Пожалуйста, обратитесь к FAQ по jsf-extensions для получения и сборки исходного кода.
Ресурсы