Professional Documents
Culture Documents
1 Koncepcja ogólna
Podstawowym założeniem przy pisaniu kontrolek JSF jest jak największe zminimalizowanie powielania kodu pisanego w
JSP z użyciem javascriptu i znaczników HTML realizującego identyczną (uwspólnioną funkcjonalność), tak by programista
używający takiej kontrolki musiał jedynie:
Zadanie to powinno zostać zrealizowane poprzez napisanie custom komponentów dekorujących istniejące kontrolki JSF w
taki sposób by pisać możliwie mało kodu po stronie komponentów (klas Javy) a maksymalnie wykorzystywać to co już
istnieje.
exclude
include
auth
W celu udostępnienia możliwości wykonywania żądań ajaxowych na opisanych powyżej komponentach niezbędne jest
dołączenie odpowiednich plików javascriptu generowanych dynamicznie przez framework dwr. W tym celu napisano tag
enableAJAX który należy umieszczać w takim miejscu w pliku JSP by renderowany przez niego output został zamieszczony
w <head> strony html. Przykładowe jego użycie:
Jego parametry to identyfikator, który musi być unikalny w zakresie komponentów na stronie (w zasadzie można powielać
ten z przykładu) oraz identyfikatory javascript'owe udostępnianych komponentów (atrybuty javascript w tagu create w pliku
dwr.xml), separatorem jest przecinek.
W celu udostępnienia prostej generycznej formy odwoływania się do metod biznesowych poprzez ajaxa napisano dwa tagi:
onClickAJAX - który generuje <a href> z odpowiednio wypełnioną metoda onClick. Przykładowe użycie
zaprezentowano poniżej:
C:\doce\kontrolki_jsf\koncepcja_jsf_ajax.xml page 1 of 10
<cst:onClickAJAX id="onClickPrev" beanName="JSFViewRetriever" operation="refreshTable"
operationParamValues="#{tableBean.beanName},#{tableBean.currentPage + 1}"
replyHandler="reloadTable" render="#{not tableBean.lastPage}">
<h:outputText value="next" />
</cst:onClickAJAX>
gdzie:
onActionAJAXDecorator - który dekoruje swoje dzieci poprzez dodanie wywołania żądania ajaxowego do atrybutu
obsługi zdarzenia, które wskazujemy w odpowiednim parametrze (w przypadku onClickAJAX było to tylko onClick, tutaj
mamy możliwość określenia):
gdzie:
skonwertowane obiekty javascript (do konwersji typów niestandardowych niezbędne jest dodanie odpowiedniej
konfiguracji w pliku dwr.xml):
C:\doce\kontrolki_jsf\koncepcja_jsf_ajax.xml page 2 of 10
<script type="text/javascript">
<!--
var displayResult = function(data)
{
for (x in transitions) {
}
//-->
</script>
XHTML'owa treść, którą możemy bezpośrednio dołączyć do odpowiedniego obiektu DOM'a. Treść ta powinna być
zwracana w obiekcie klasy pl.ufg.sif.workflow.jsfcomponents.ajax.AjaxTableContent skonwertowanym
automatycznie (poprzez zadeklarowany konwerter DWRowy) do odpowiedniego obiektu javascriptowego.
Przykładową implementację handlera prezentuje poniższy kod:
Highlights
Uwaga,
jeżeli w zwracanym kodzie XHTML jest javascript to by on był przebudowany należy wywołać metodę runScript, którą
dołączono w pliku commons-ajax.js a której przekazujemy jako parametr id obiektu, do którego podpieliśmy
otrzymane body, tak by mogła ona sparsować zawartego w nim javascripta i go zevaluować, by był on "widziany"
przez engine javascript'owy.
Obiekt AjaxViewContent zawiera divId, który jest przewidziany jako identyfikator obiektu DOM'a, do którego chcemy
dołączyć body jako innerHTML.
C:\doce\kontrolki_jsf\koncepcja_jsf_ajax.xml page 3 of 10
Schemat pobierania view przez AJAXa
Zastosowanie tego wzorca najczęściej wymagać będzie zmiany modelu (zarządzanych beanów JSF bądź komponentów
springowych o zasięgu sesyjnym), na podstawie, którego renderowane jest owo view, którego kontent ma być zwrócony.
Najlepszym rozwiązaniem będzie stworzenie dla konkretnego celu beana zarządzanego o scop'ie sesyjnym, którego
właściwości będą podmieniane a następnie będzie pobierane view generowane na podstawie tak zmienionego modelu.
Bean ten powinien posiadać pewne abstrakcyjne metody, które muszą być zaimplementowane przez programistę
używającego danej kotrolki w konkretnym już celu np. kiedy mamy kontrolke tabeli dociąganej dynamicznie taką
abstrakcyjną metodą będzie metoda zwracająca model danych dziedzinowych, które mają być wyświetlone w formie tabeli.
Na powyższym diagramie kontrolka wygenerowana przez JSF'a (AjaxEnabled UI Control) przy określonym zdarzeniu
javascript'owym wysyła żądanie ajaxowe do komponentu JSFViewRetriever W żądaniu tym przekazywany jest
identyfikator modelu (beana zarządzanego sesyjnego), który powinien zostać zaktualizowany na podstawie parametrów:
updateModelParams. Komponent JSFViewRetriever aktualizuje model a następnie pobiera kontent view ze strony JSF i
zwraca go jako rezultat żądania ajaxowego opakowanego w obiekt AjaxViewContent.
C:\doce\kontrolki_jsf\koncepcja_jsf_ajax.xml page 4 of 10
model dla danego view. Poniżej zaprezentowano przykładowe rozwiązanie taga faceletowego dla tabel sortowalnych
dociąganych ajaxem:
<div xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:cst="http://abg.com.pl/jsf/ajax"
xmlns:t="http://myfaces.apache.org/tomahawk"
xmlns:c="http://java.sun.com/jstl/core"
xmlns="http://www.w3.org/1999/xhtml" id="tableForAjax">
<ui:composition>
<div id="#{tableBean.divId}">
<f:view>
<h:form id="table#{tableBean.beanName}">
<h:outputLabel for="TheDataTable" value="Zadania" />
<cst:ajaxAwareTable id="ajaxTable#{tableBean.beanName}" tableBean="#{tableBean}">
<t:dataTable id="TheDataTable" styleClass="tableBorder"
headerClass="head" rowClasses="isNormal,isDark"
columnClasses="standardTable_Column"
rowIndexVar="rowIndex" cellspacing="0" cellpadding="4" width="90%">
</t:dataTable>
</cst:ajaxAwareTable>
<f:verbatim><br/></f:verbatim>
C:\doce\kontrolki_jsf\koncepcja_jsf_ajax.xml page 5 of 10
</div>
</h:form>
</f:view>
</div>
</ui:composition>
</div>
W powyższym kodzie przekazany obiekt modelu znajduje się pod zmienną: #{tableBean} Zastosowanie taga
faceletowego w znacznym stopniu upraszcza konstrukcję view, gdyż nie musimy wtedy wiekszości komponentów JSF i ich
ustawień wprowadzać do kodu Javy naszego custom komponentu JSF. W powyższym rozwiązaniu w celu generowania
dynamicznie kolumn JSF'owych wprowadzono tag cst:ajaxAwareTable. Tag ten nie generuje całego kontentu
tworzonego przez wyżej wylistowanego taga faceletowego a jedynie dekoruje t:dataTable przez dodanie do niego
kolumn, dzięki czemu oszczędzamy znacznie na jego rozmiarach i ilości parametrów, które są do niego przekazywane.
Gdyby zdecydować się na to, że nie mamy taga faceletowego musielibyśmy całe drzewo kontrolek JSF związane z
rozmieszczeniem przycisków next, prev i innymi elementami wyglądu tworzyć poprzez komponent JSF co wymagałoby wiele
kodu i elementów konfiguracyjnych.
Highlights
mniejsza będzie liczba zmiennych konfiguracyjnych naszego custom komponentu, których będziemy musieli się
nauczyć.
Dynamiczne (kiedy nie możemy określić konkretnego kształtu na etapie kompilacji) elementy muszą być jednak tworzone w
custom kontrolkach JSF. Wtedy również najlepiej dekorować już istniejące kontrolki, tak by kod kontrolki tworzącej
dynamiczny fragment view był jak najprostszy i jak najwięcej konfiguracji było przeniesione na stronę JSP czy tag
faceletowy
<ui:composition template="_template/maintemplate.xhtml">
<ui:define name="javascriptExtension">
<script type="text/javascript"
src="#{facesContext.externalContext.request.contextPath}/_js/overlibmws.js">
</script>
<script type="text/javascript"
C:\doce\kontrolki_jsf\koncepcja_jsf_ajax.xml page 6 of 10
src="#{facesContext.externalContext.request.contextPath}/_js/prototype.js">
</script>
<script type="text/javascript"
src="#{facesContext.externalContext.request.contextPath}/_js/ajax-commons.js">
</script>
<cst:enableAJAX id="enableAJAX" dwrBeanNames="JSFTableViewRetriever"></cst:enableAJAX>
</ui:define>
<ui:define name="workArea">
<cst:ajaxDoLoadTable tableBean="#{casesTableBacking}" />
</ui:define>
</ui:composition>
Tag ajaxDoLoadTable powoduje dodanie na stronę javascriptu, który w onLoad doładowuje ramkę tabeli
zdefiniowanej w stronie ramki opisanej poniżej Niezbędnym parametrem jest tableBean który wskazuje na bean
modelu tabeli opisany w dalszej części.
</f:view>
</jsp:root>
Tag ajaxEnabledTable służy do wygenerowania właściwej treści tabeli na podstawie beana modelu. Referencje do
beana modelu przekazujemy w parametrze tableBean. Bean ten jest beanem zarządzanym modelu tabeli o scopie
sesyjnym. Jego implementacja musi dziedziczyć z klasy abstrakcyjnej AbstractAjaxTableManagedBean (czytaj
następny rozdział).
konfigurację instancji konkretnej tabeli. Klasa tego beana powinna dziedziczyć po klasie
AbstractAjaxTableManagedBean Dziedziczenie to wymagać będzie implementacji metody: DataModel
Przykładową implementację prezentuje klasa CasesTableManagedBean. Poniżej zaprezentowano
przykładową konfigurację beana modelu wraz z opisem poszczególnych parametrów:
C:\doce\kontrolki_jsf\koncepcja_jsf_ajax.xml page 7 of 10
<managed-bean>
<description>Bean testowy z lista spraw</description>
<managed-bean-name>casesTableBacking</managed-bean-name>
<managed-bean-class>
pl.ufg.sif.workflow.jsfcomponents.ajax.CasesTableManagedBean
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>config</property-name>
<value>#{ajaxAwareTableConfig}</value>
</managed-property>
<managed-property>
<property-name>viewName</property-name>
<value>/cases_ajax_frame.jsf</value>
</managed-property>
<managed-property>
<property-name>beanName</property-name>
<value>casesTableBacking</value>
</managed-property>
<managed-property>
<property-name>columnNamesBundle</property-name>
<value>casesColumnesNames</value>
</managed-property>
<managed-property>
<property-name>columnPropertiesBundle</property-name>
<value>casesColumnesProperties</value>
</managed-property>
<managed-property>
<property-name>columnCommandsBundle</property-name>
<value>casesColumnCommands</value>
</managed-property>
<managed-property>
<property-name>rowsPerPage</property-name>
<value>3</value>
</managed-property>
<managed-property>
<property-name>defaultSortColumn</property-name>
<value>name</value>
</managed-property>
<managed-property>
<property-name>customColumnPage</property-name>
<value>../table/cases_columns.xhtml</value>
</managed-property>
</managed-bean>
C:\doce\kontrolki_jsf\koncepcja_jsf_ajax.xml page 8 of 10
2. viewName - nazwa strony ramki (tej z zawartością docelową tabeli),
3. beanName - nazwa beana modelu (bean potrzebuje wiedziec pod jaka nazwą go w JSF'ie osadzono),
możliwe jest zdefiniowanie na podstawie jednej implementacji kilku konfiguracji,
4. columnNamesBundle - nazwa klucza bundle pod którą umieszczono nazwy dla etykiet poszczególnych
kolumn
5. columnPropertiesBundle - nazwa klucza bundle pod którą umieszczono nazwy propertiesów które mają być
wyświetlane jako reprezentacja poszczególnych kolumn,
6. columnCommandsBundle - nazwa klucza bundle pod którą umieszczono action binding expressions dla
ewentualnych linków na poszczególnych kolumnach,
7. rowsPerPage - domyślna liczba wierszy na stronę,
8. defaultSortColumn - domyślna kolumna sortowania,
9. customColumnPage - (opcjonalne) jeżeli chcemy mieć jakies niestandardowe kolumny to ustawiając ten
parametr inkludujemy sobie stronę JSP z własną definicją kolumn np.:
<div xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:cst="http://abg.com.pl/jsf/ajax"
xmlns:t="http://myfaces.apache.org/tomahawk"
xmlns="http://www.w3.org/1999/xhtml" id="tableForAjax">
<ui:composition>
<t:column>
<h:commandLink action="#{casesListBean.chooseCase}">
<h:outputText value="#{item.name}"/>
</h:commandLink>
</t:column>
<t:column>
<h:commandLink action="#{casesListBean.chooseCase}" >
<h:outputText value="#{item.crDate}"/>
</h:commandLink>
</t:column>
</ui:composition>
</div
<managed-bean>
<description>
Bean z ogolna (rzadko zmieniana konfiguracja do
AbstractAjaxTableManagedBean oraz komponentu
AJAXAwareTableComponent
</description>
<managed-bean-name>ajaxAwareTableConfig</managed-bean-name>
<managed-bean-class>
C:\doce\kontrolki_jsf\koncepcja_jsf_ajax.xml page 9 of 10
pl.ufg.sif.workflow.jsfcomponents.ajax.table.AJAXAwareTableConfig
</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
<managed-property>
<property-name>ajaxViewRetrieverBeanName</property-name>
<value>JSFTableViewRetriever</value>
</managed-property>
<managed-property>
<property-name>bundleBase</property-name>
<value>jsf.ajax.table.TableResources</value>
</managed-property>
<managed-property>
<property-name>itemVar</property-name>
<value>item</value>
</managed-property>
<managed-property>
<property-name>rowsPerPageList</property-name>
<list-entries>
<value-class>java.lang.Integer</value-class>
<value>3</value>
<value>5</value>
<value>7</value>
<value>10</value>
<value>25</value>
<value>35</value>
</list-entries>
</managed-property>
</managed-bean>
C:\doce\kontrolki_jsf\koncepcja_jsf_ajax.xml page 10 of 10