Статьи

Jsf 2.0 ClientId с jQuery

 В этой статье я покажу, как работать со свойством 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) мы использовали знак доллара $ для доступа к свойствам (form1 и component) управляемого компонента. В (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).closest('tr').toggleClass("selectedRow");
}
2)
$(document).ready(function(){
$('input[id$=editRow]').click(function(){
$(this).closest('tr').toggleClass("selectedRow");
return false;
});
});
3)
$(document).ready(function(){
$('#${clientIdExample.dt1.clientId} tr td > input').click(function(){
$(this).closest('tr').toggleClass("selectedRow");
return false;
});
});

 Работа с jQuery и JSF 2.0 (javax.faces.SEPARATOR_CHAR)

Обычно, когда вы используете инфраструктуру JSF и просматриваете вывод HTML, вы обнаруживаете, что clientId  отделен знаком двоеточия «:». К сожалению, этот знак недопустим при использовании библиотеки jQuery, поскольку он зарезервирован для селекторов jQuery. JavaServer Faces 2.0 поставляется с параметром: javax.faces.SEPARATOR_CHAR. В этом случае вы должны изменить разделитель по умолчанию на другой, который принят jQuery. Например (web.xml):

    <context-param>
<param-name>javax.faces.SEPARATOR_CHAR</param-name>
<param-value>-</param-value>
</context-param>

Звоните Ajax через шаблон Facelets и клиентов 

В этом примере я покажу, как вызвать Ajax для обновления UIComponent в Template.xhtml из AjaxEngine.xhtml (Facelets Client). Мы будем использовать функцию javascript jsf.ajax.request вместо f: ajax, и мы будем использовать свойство clientId UIControl. 

Если мы хотим использовать Javascript jsf.ajax.request в чистом виде, нам нужно загрузить соответствующий клиентский скрипт

<h:outputScript name="jsf.js" library="javax.faces" target="head" />

Это сгенерирует что-то вроде этого:

<script src="/PrimeFacesLearning/javax.faces.resource/jsf.js.jsf?ln=javax.faces&amp;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&amp;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());
}

В приведенном выше коде у нас есть две страницы. Template.xhml и DummyAjax.xhtml в качестве клиента шаблона Facelets. В шаблоне у нас есть  <h: outputText id = «ajaxText» binding = «# {template.ajaxText}» value = «$ {ajaxEngine.firstname}» />,  а в DummyAjax.xhtml мы имеем

 <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'}); return 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-счетчик»). ClientID}

И, наконец, последняя вещь действительно полезна — это тело метода setFirstName (String firstname), где у нас есть шанс обновить столько свойств, сколько мы хотим.

Композиция элемента управления JavaServer Faces 2.0

Я бы не стал останавливаться на $ {cc.attr.id} только потому, что это работает так же, как и все содержимое статьи.
Краткий пример:

<?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

Краткое содержание статьи

Эта статья должна дать вам понимание того, как получить clientId в различных сценариях. Вы также должны знать, как использовать JSF 2.0 с jQuery, и вы узнали, как вызывать Ajax без тега f: ajax. Вы можете скачать исходный код из таблицы ниже.

 Полезные ссылки

Язык выражения Официальная ссылка на Java 6 EL.
Пример составных компонентов. Простой, но полезный пример использования CC.
jsf.ajax. * javadoc Полная ссылка на библиотеку jsf.js.
JSF AJAX пример Пример с f: actionListener и jsf.ajax.request (…)
FireBug Это один из лучших инструментов для просмотра html клиента, отладки Javascript и просмотра реальных запросов Ajax.
PrimeFaces Моя любимая библиотека тегов jsf. Использует jQuery в качестве движка Javascript. Много отличных элементов управления и, конечно, с открытым исходным кодом.
Первая, вторая статья В этой статье приводятся подробности о свойствах JSF clientId.
JQuery Эта библиотека упрощает жизнь разработчиков.
Subversion хранилище кода. Простой проект NetBeans, где я изучаю JSF 2.0