На прошлой неделе я имел удовольствие посетить (и представить) 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 для получения и сборки исходного кода.
Ресурсы