Вступление
В этой статье я покажу, как работать со свойством clientId UIComponent , которое было улучшено в JavaServer Faces 2.0, о поиске конкретного идентификатора со стороны клиента и о том, как использовать эти знания с библиотекой jQuery. Мы рассмотрим составные компоненты , составление граней , шаблоны граней и UIComponents с NamingContainer (например, UIData).
Предварительно требования
В этой статье я использовал IDE NetBeans 6.9 , Mojarra 2.0.2 (FCS b10) в качестве реализации JSF 2.0 и GlassFish 3.0.1 .
Здравствуйте, ClientId
На этом шаге мы создадим первое простое веб-приложение с одной страницей.
1. В мастере NetBeans выберите «Java Web» -> «Веб-приложение» -> «ClientIdExampleApp» -> «Выбрать EE 6 Web и Glassfish 3» -> «Выбрать среду JavaServer Faces Framework (с представлением Facelets») и нажмите «Готово».
Получение ClientId из нового выражения EL в JSF 2.0 $ { component }
<h:form id="form1" prependId="true">
${component.clientId}
<h:commandButton id="btn1" value="${component.clientId}" />
${component.clientId}
<h:panelGroup id="panel1" layout="block">
${component.clientId}
</h:panelGroup>
</h:form>
Этот код должен производить что-то вроде этого:
<form enctype="application/x-www-form-urlencoded" action="/WebApplication1/faces/index.xhtml" method="post" name="form1" id="form1">
(1)form1
<input type="submit" (2)value="form1:btn1" name="form1:btn1" id="form1:btn1">
(3)form1
<div id="form1:panel1">
(4)form1:panel1
</div>
</form>
Хотя этот пример действительно легко кодировать, мы можем видеть, что:
$ {component.clientId} возвращает «
ближайший » родительский
UIComponent . Точки (2) и (4) разрешили clientId правильно, в то время как (1) и (3) разрешили clientId родителя:
UIForm . Если нам нужно создать функцию javascript, это решение действительно быстрое и достаточно хорошее. С другой стороны, когда нам нужна перекрестная ссылка на javascript id, лучше использовать
привязывающие атрибуты.
Получение ClientId от атрибутов привязки UIComponents
Второй способ более сложный, но более эффективен в самых разных случаях. Два шага необходимы, чтобы получить clientId путем привязки. Сначала нам нужно добавить атрибут связывания и связать этот атрибут со свойством Managed Bean, соответствующим конкретным подклассам UIComponent.
Простая форма:
<h:form id="form1" prependId="true" binding="(1)${index.form1}" style="background-color: red">
ClientId: (2)${index.form1.clientId}<br />
Simple attribute of form1: (3)${index.form1.attributes["style"]}<br />
<h:commandButton id="btn1" value="(4)#{index.form1.findComponent('btn1').clientId} :-)" />
<h:panelGroup id="panel1" layout="block">
(5)${component.clientId} <br />
</h:panelGroup><br />
panel1 clientId: (6)#{index.form1.findComponent("panel1").clientId} <br />
panel1 layout attribute (7)#{index.form1.findComponent("panel1").attributes["layout"]}
</h:form>
Java управляемый код компонента:
@ManagedBean
@RequestScoped
public class index {
private UIForm form1;
public UIForm getForm1() {
return form1;
}
public void setForm1(UIForm form1) {
this.form1 = form1;
}
public index() {
}
}
Отображается HTML:
<form style="background-color: red;" enctype="application/x-www-form-urlencoded" action="/WebApplication1/faces/index.xhtml;jsessionid=64e609789a8d90f6993151f964d4" method="post" name="form1" id="form1">
ClientId: form1<br>
Simple attribute of form1: background-color: red<br /><input type="submit" value="form1:btn1 :-)" name="form1:btn1" id="form1:btn1"><div id="form1:panel1">
form1:panel1 <br /></div><br />
panel1 clientId: form1: panel1 <br />
panel1 layout attribute: block
</form>
Краткое разъяснение
В пунктах: (1), (2), (3) и (5) мы использовали доларный знак $ для доступа к свойствам (форма 1 и компонент) управляемого компонента. В (4), (7), (6) мы использовали хеш- знак # для ссылки на методы UIComponent, такие как findComponent, для поиска дочернего компонента и использования свойств panel1.
ClientId с помощью NamingContainer
Сейчас я приведу несколько примеров моего веб-приложения и объясню, как все это работает. Давайте начнем с h: dataTable с несколькими строками и двумя столбцами. Второй столбец будет содержать ah: commandButton, и мы хотим использовать эту кнопку, чтобы выполнить некоторое клиентское действие javascript.
Вот простые данные:
<h:dataTable id="dt1" binding="${clientIdExample.dt1}" value="#{clientIdExample.localeList}" var="item"
dir="LTR" frame="hsides" rules="all" summary="This is a JSF code to create dataTable." >
<f:facet name="header">
<h:outputText value="This is 'dataTable' demo" />
</f:facet>
<h:column id="col1">
${component.clientId}
<f:facet name="header">
<h:outputText value="name"/>
</f:facet>
<h:outputText value="#{item.displayName}" style="margin:5px; padding:5px"></h:outputText>
</h:column>
<h:column id="col2">
<f:facet name="header">
<h:outputText value="click"/>
</f:facet>
<h:commandButton id="editRow" value="click" onclick="toggleRow('${component.clientId}');return false;"
style="margin:5px; padding:5px"/>
</h:column>
<f:facet name="footer">
<h:outputText value="The End" />
</f:facet>
</h:dataTable>
И, конечно же, наш ManagedBean:
@Named(value = "clientIdExample")
@RequestScoped
public class ClientIdExample {
private Locale[] localeList;
private HtmlDataTable dt1;
public Locale[] getLocaleList() {
Locale[] tmp = Locale.getAvailableLocales();
localeList = new Locale[5];
for (int i=0;i< localeList.length;i++){
localeList[i] = tmp[i];
}
return localeList;
}
public void setLocaleList(Locale[] localeList) {
this.localeList = localeList;
}
public HtmlDataTable getDt1() {
return dt1;
}
public void setDt1(HtmlDataTable dt1) {
this.dt1 = dt1;
}
public ClientIdExample() {
}
}
И сгенерированный HTML должен выглядеть так:
<table rules="all" frame="hsides" summary="This is a JSF code to create dataTable." dir="LTR" id="form1-dt1">
<thead>
<tr><th scope="colgroup" colspan="2">This is 'dataTable' demo</th></tr>
<tr>
<th scope="col">name</th>
<th scope="col">click</th>
</tr>
</thead>
<tfoot>
<tr><td colspan="2">The End</td></tr>
</tfoot>
<tbody>
<tr class="">
<td>
form1-dt1-0
<span style="margin: 5px; padding: 5px;">japoński (Japonia)</span></td>
<td><input type="submit" style="margin: 5px; padding: 5px;" value="click" name="form1-dt1-0-editRow" id="form1-dt1-0-editRow"></td>
</tr>
<tr class="">
<td>
form1-dt1-1
<span style="margin: 5px; padding: 5px;">hiszpański (Peru)</span></td>
<td><input type="submit" style="margin: 5px; padding: 5px;" value="click" name="form1-dt1-1-editRow" id="form1-dt1-1-editRow"></td>
</tr>
<tr class="">
<td>
form1-dt1-2
<span style="margin: 5px; padding: 5px;">angielski</span></td>
<td><input type="submit" style="margin: 5px; padding: 5px;" value="click" name="form1-dt1-2-editRow" id="form1-dt1-2-editRow"></td>
</tr>
<tr class="">
<td>
form1-dt1-3
<span style="margin: 5px; padding: 5px;">japoński (Japonia,JP)</span></td>
<td><input type="submit" style="margin: 5px; padding: 5px;" value="click" name="form1-dt1-3-editRow" id="form1-dt1-3-editRow"></td>
</tr>
<tr>
<td>
form1-dt1-4
<span style="margin: 5px; padding: 5px;">hiszpański (Panama)</span></td>
<td><input type="submit" style="margin: 5px; padding: 5px;" value="click" name="form1-dt1-4-editRow" id="form1-dt1-4-editRow"></td>
</tr>
</tbody>
</table>
Теперь я представлю 3 способа выделения всей строки с помощью события click. Эти функции просты, поэтому я не буду объяснять.
1)
function toggleRow(clientId)
{
$('#'+clientId).parent().parent().toggleClass("selectedRow");
}
2)
$(document).ready(function(){
$('input[id$=editRow]').click(function(){
$(this).parent().parent().toggleClass("selectedRow");
return false;
});
});
3)
$(document).ready(function(){
$('#${clientIdExample.dt1.clientId} tr td > input').click(function(){
$(this).parent().parent().toggleClass("selectedRow");
return false;
});
});
Working with jQuery and JSF 2.0(javax.faces.SEPARATOR_CHAR)
Generally when you use jsf framework and look inside html output you can find that clientId is separated by colon sign «:». Unfortunatelly this sign is not allowed when you use jQuery library because is reserved for jQuery selectors. JavaServer Faces 2.0 comes with parameter: javax.faces.SEPARATOR_CHAR. In this case you should change default separator to another one which is accepted by jQuery. For example (web.xml):
<context-param>
<param-name>javax.faces.SEPARATOR_CHAR</param-name>
<param-value>-</param-value>
</context-param>
Call ajax across Facelets Template and Clients
In this example I will show how to call ajax to update UIComponent in Template.xhtml from AjaxEngine.xhtml(Facelets Client). We will use javascript jsf.ajax.request function instead of f:ajax and we will use clientId property of UIControl.
If we want to use javascript jsf.ajax.request in pure form we need to load appropriate client script
<h:outputScript name="jsf.js" library="javax.faces" target="head" />
This will generate something like this:
<script src="/PrimeFacesLearning/javax.faces.resource/jsf.js.jsf?ln=javax.faces&stage=Development" type="text/javascript"></script>
Template.xhtml:
<h:body>
<f:view contentType="text/html" locale="pl_PL">
<h:form id="form1" prependId="true" binding="#{template.form1}">
<ui:debug id="debug1" rendered="true" hotkey="l">
</ui:debug>
<div id="container">
<div id="header"></div>
<div id="center-column">
<div id="left-column">
<h:outputText id="ajaxText" binding="#{template.ajaxText}" value="${ajaxEngine.firstname}" />
</div>
<br />
<p:panel id="right-column" styleClass="right-column" toggleable="true" closable="true">
<ui:insert name="right-column" >
</ui:insert>
</p:panel>
</div>
</div>
</h:form>
</f:view>
</h:body>
DummyAjax.xhtml
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"
template="./resources/layout/Template.xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.prime.com.tr/ui"
xmlns:sc="http://java.sun.com/jsf/composite/Controls">
<ui:define name="right-column">
<h1> f:ajax example using Facelets Templating</h1>
<h:outputScript name="jsf.js" library="javax.faces" target="head" />
<script src="/PrimeFacesLearning/javax.faces.resource/jsf.js.jsf?ln=javax.faces&stage=Development" type="text/javascript"></script>
<h:panelGrid id="panelGrid1">
<h:inputText value="#{ajaxEngine.firstname}" onkeyup="jsf.ajax.request(this, event,{render:'form1-text ${template.ajaxText.clientId} form1-count'}); return false">
</h:inputText>
<h:outputText id="text" value="#{ajaxEngine.firstname}" escape="false"/>
<br />
Firstname char count: <h:outputText id="count" value="#{ajaxEngine.count}" />
</h:panelGrid>
</ui:define>
</ui:composition>
/**
* This code will be automatically called when jsf.ajax.request will
* be populated from client side.
* Here we also set another property of DummyAjax - Integer count
* to manipulate other variables in managed bean.
* @param firstname
*/
public void setFirstname(String firstname) {
this.firstname = firstname;
setCount(getFirstname().length());
}
In code above we have two pages. Template.xhml and DummyAjax.xhtml as facelets template client. In template we have <h:outputText id=»ajaxText» binding=»#{template.ajaxText}» value=»${ajaxEngine.firstname}» /> and in DummyAjax.xhtml we have:
<h: inputText id = «input1» value = «# {ajaxEngine.firstname}» />
<h: outputText id = «text» value = «# {ajaxEngine.firstname}» escape = «false» />
Все эти три элемента управления ссылаются на одно свойство управляемого компонента: # {ajaxEngine.firstname}. Следующая интересная вещь — вызов ajax для каждого ключа.
onkeyup = «jsf.ajax.request (this, event, {render: ‘form1-text $ {template.ajaxText.clientId} form1-count’}); вернуть false»
выражение метода для получения clientId каждого UIComponent:
form1-текст | # {Template.form1.findComponent ( «panelGrid1»). FindComponent ( «текст»). ClientId} |
form1-input1 | # {Template.form1.findComponent ( «panelGrid1»). FindComponent ( «вход1»). ClientID} |
Form1 подсчета | #{template.form1.findComponent(«panelGrid1»).findComponent(«form1-count»).clientId} |
And in the end last thing is really usefull which is body of method setFirstName(String firstname) where we have a chance to update as many properties as we want.
JavaServer Faces 2.0 Control Composition
In the almost of the end of article I would’n dwell on ${cc.attr.id} just because this works just the same as all content in article.
Short example:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.prime.com.tr/ui"
xmlns:cc="http://java.sun.com/jsf/composite">
<!-- NO INTERFACE -->
<cc:interface>
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
${cc.attrs.id},${cc.clientId} <br />
</cc:implementation>
</html>
<h:form id="form1" prependId="true">
<sc:Dummy id="dummy1" /><br />
<sc:Dummy id="dummy2" />
</hform>
<!--browser output-->
dummy1, form1-dummy1 <br />
dummy2,form1-dummy2
Article Summary
This article should give you understanding of how to get clientId with diffrent scenarious. You should also know how to use jsf 2.0 with jQuery and you have learned how to call ajax without f:ajax tag. You can download source code from table below.
Useful links
Expression Language | Official reference to Java 6 EL. |
Composite Components example. | Simple but usefull example of how to use CC. |
jsf.ajax.* javadoc | Full referene to jsf.js library. |
jsf ajax example | Example with f:actionListener and jsf.ajax.request(…) |
FireBug | Oh God. This is one of the best tool for browse client html, debug javascript and watch real ajax requests. |
PrimeFaces | My favourite jsf tag library. Uses jQuery as javascript engine. Many great controls and of course Open Source. |
First, second gool article | Those article show details about jsf clientId properties. |
jQuery | This library simplify developer life. |
Subversion repository of code. | Simple NetBeans project where I learn JSF 2.0 |