I have an issue with using p:outputLabel when used with composite component. I have composite component with p:inputText field (I removed irrelevant parts from component):
<cc:interface>
<cc:editableValueHolder name="myInput" targets="myInput"/>
<cc:attribute name="required" required="true" type="java.lang.Boolean" default="false"/>
</cc:interface>
<cc:implementation>
<p:inputText id="myInput" required="#{cc.attrs.required}"/>
</cc:implementation>
Now, I wont to use this component with p:outputLabel:
<p:outputLabel for="myComponent:myInput" value="#{resources['myLabel']}:"/>
<my:myComponent id="myComponent" required="#{myBean.required}"/>
Everything works fine, required validation, message is displayed as well, but there is no * sign on label, as there is when I connect label directly to p:inputText component. If I, on the other hand, hardcode required="true" on p:inputText everything works fine.
I debugged through org.primefaces.component.outputlabel.OutputLabelRenderer and discovered that component is recognized as UIInput, but input.isRequired() returns false. Farther debugging discovered that required attribute isn't yet defined on component, so it returns false as default value i UIInput:
(Boolean) getStateHelper().eval(PropertyKeys.required, false);
Also, if I just move p:outputLabel inside composite component everything works fine. Like EL is evaluated later inside composite component?
I'm using Primefaces 3.5 with Mojarra 2.1.14
This is, unfortunately, "by design". The evaluation of the #{} expressions is deferred to the exact moment of the access-time. They're unlike "standard" EL ${} in JSP not evaluated at the exact moment they're been parsed by the tag handler and "cached" for future access during the same request/view. At the moment the <p:outputLabel> is rendered, and thus the #{cc.attrs.required} as referenced by UIInput#isRequired() needs to be evaluated, there's no means of any #{cc} in the EL context. So any of its attributes would not evaluate to anything. Only when you're sitting inside the <cc:implementation>, the #{cc} is available in the EL context and all of its attribues would thus successfully evaluate.
Technically, this is an unfortunate corner case oversight in the design of <p:outputLabel>. Standard JSF and EL are namely behaving as specified. Basically, the presentation of the label's asterisk depending on the input's required attribute should be evaluated the other way round: at the moment the <p:inputText> inside the composite is to be rendered or perhaps even already when it's to be built. Thus, the label component should not ask the input component if it's required, but the input component should somehow notify the label component that it's required. This is in turn hard and clumsy (and thus inefficient) to implement.
If moving the label to inside the composite is not an option, then your best bet is to create a tag file instead of a composite component around the input component. It only requires some additional XML boilerplate.
/WEB-INF/tags/input.xhtml:
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:p="http://primefaces.org/ui"
>
<c:set var="id" value="#{not empty id ? id : 'myInput'}" />
<c:set var="required" value="#{not empty required and required}" />
<p:inputText id="#{id}" required="#{required}"/>
</ui:composition>
/WEB-INF/my.taglib.xml:
<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
version="2.0"
>
<namespace>http://example.com/my</namespace>
<tag>
<tag-name>input</tag-name>
<source>tags/input.xhtml</source>
</tag>
</facelet-taglib>
/WEB-INF/web.xml:
<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/my.taglib.xml</param-value>
</context-param>
Usage:
<html ... xmlns:my="http://example.com/my">
...
<p:outputLabel for="myInput" value="#{resources['myLabel']}:" />
<my:input id="myInput" required="#{myBean.required}" />
I just did a quick test and it works fine for me.
See also:
When to use <ui:include>, tag files, composite components and/or custom components?
Related
I've been trying to create a custom ui component for jsf to replace <f:selectItems />, which is wrapped in a selectOneMenu. So my facesComponent needs to generate all the options while the selectOneMenu provides the <select></select>.
Problem is that the options are not rendered inside the selectOneMenu, but rather just outside of it.
My facesComponent looks like this:
#FacesComponent(value = "be.mokuril.jsf.SelectItemsForEnum")
public class SelectItemsForEnum extends UISelectItems {
#Override
public void encodeAll(FacesContext facesContext) throws IOException {
ResponseWriter responseWriter = ResponsefacesContext.getResponseWriter();
responseWriter.startElement("option", null);
responseWriter.writeAttribute("value", 1, null);
responseWriter.write("option1");
responseWriter.endElement("option");
responseWriter.startElement("option", null);
responseWriter.writeAttribute("value", 2, null);
responseWriter.write("option2");
responseWriter.endElement("option");
}
And this is my taglib:
<facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="2.0" id="mw"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
>
<namespace>http://www.mokuril.be/jsf/mw</namespace>
<composite-library-name>mw</composite-library-name>
<tag>
<tag-name>selectItemsForEnum</tag-name>
<component>
<component-type>be.mokuril.jsf.SelectItemsForEnum</component-type>
</component>
</tag>
And the xhtml to reproduce the problem:
<h:form>
<h:selectOneMenu>
<mw:selectItemsForEnum />
</h:selectOneMenu>
</h:form>
I've also been looking at the component tree:
<HtmlSelectOneMenu disabled="false" id="j_idt7" immediate="false" inView="true" localValueSet="false" readonly="false" rendered="true" required="false" transient="false" valid="true">
<SelectItemsForEnum id="j_idt8" inView="true" rendered="true" transient="false"/>
</HtmlSelectOneMenu>
And if I use <f:selectItems /> instead of my component I get this:
<UISelectItems id="j_idt9" inView="true" rendered="true" transient="false"/>
Which is actually what I expected it to look like, but I clearly must be overlooking something important.
Your concrete problem is caused because UISelectOne/UISelectMany components will scan their direct children for UISelectItem(s) instences. When you use a composite component, its content is basically wrapped in an UIPanel component, which is not an instance of UISelectItem(s), so the selection components will ignore it. Technically, you should be using a custom component instead of a composite component. See also When to use <ui:include>, tag files, composite components and/or custom components?
However, you'll stumble upon the next problem: the UISelectOne/UISelectMany will continue to render the options all by themselves and ignore the output from your renderer. Basically, no one <f:xxx> component renders HTML by itselves. This responsibility is up to its <h:xxx> parent. Technically, you should be overriding the renderer of <h:selectOneMenu> instead if you want to manipulate the output of <f:selectItem(s)>.
It's unclear which problem you're trying to solve this way, but if I were to do educated guesses, those questions should most probably answer and solve your real problem the correct way: How to use enum values in f:selectItem(s) and/or How to add tooltip to f:selectItems.
For partial updating my composite components, I have some problems finding the correct parent IDs. e.g. if my component is inside of a tabView
<p:tabView id="foo">
<p:tab>
<stg:mycomponent id="bar" />
</p:tab>
</p:tabView>
Now I need the String :foo:bar to find the correct parts, that I want to update. Via #{cc.id} I just get bar, so this does not work for me.
However, I tried to achieve this by some kind of dirty hack by adding a attribute to my component like this
<composite:attribute
name="parentId"
default=":#{component.namingContainer.parent.namingContainer.clientId}"
/>
If I hand over the String :foo:bar to parentId everything works fine, but of course that's not what I really want to do. I do not want to force the user of my component to hand over this ID.
But now the problem: If I do not hand over a parentId, I only can use my attribute in the "first level" of my component. If there are some kind of "nested IDs" then #{cc.attrs.parentId} is evaluated e.g. to foo:bar (which is nice) but also foo:bar:fooBar or somethin like that, depending on where #{cc.attrs.parentId} is located in my code.
I hope it's comprehensible what my problem is and what I am exactly asking for. If not, please leave a comment.
I am using primefaces 3.5 and JSF 2.1 (Mojarra)
If I understand, you need to update parts of a composite component within the composite component itself. Normally you don't need to know the parents IDs to achieve this.
As you are setting id on a primefaces component, you can update it with primefaces using the same id (without any :container:etc prefix) as long as your update is in the same scope.
Here is an example (see source below).
Observe the generated id's in the HTML page produced by JSF in the first tab:
First span : id="static" (raw html id, not PF, not unique in the document)
Second span : id="myform:tv1:comp1:static2" (PF-generated to ensure id is unique in the document)
Third span : id="myform:tv1:comp1:dynamic" (PF-generated, unique)
poll component use the id "myform:tv1:comp1:dynamic", even if we only provide "dynamic" in the source.
The parents does not need to know the ID of the component part to be updated
The component does not need to know the IDs of its parents/containers
Please note generated IDs on the second tab. PrimeFaces does its job by naming them unique.
Sample composite component:
<composite:interface>
<composite:attribute name="label" />
</composite:interface>
<composite:implementation>
<h1><h:outputText value="#{cc.attrs.label}"/></h1>
<span id="static1">
Static : #{testBean.increment}
</span>
<p:outputPanel id="static2">
Static : #{testBean.increment}
</p:outputPanel>
<p:outputPanel id="dynamic">
Dynamic : #{testBean.increment}
</p:outputPanel>
<p:poll interval="3" update="dynamic" />
</composite:implementation>
Use the component in a XHTML page:
<h:form id="myform">
<p:tabView id="tv1" >
<p:tab id="tab1" title="First Tab">
<comps:myComponent id="comp1" label="Abc" />
</p:tab>
<p:tab id="tab2" title="Second Tab">
<comps:myComponent id="comp2" label="Def" />
</p:tab>
</p:tabView>
</h:form>
And this is the code for the test bean:
#ManagedBean(name="testBean")
public class TestBean implements Serializable
{
static int _counter = 0;
public String getIncrement()
{
_counter++;
return Integer.toString(_counter);
}
}
I hope this example will be clear enough and close to what you want to get.
Please let me know.
I want to set a ui:param depending on a bean value and I thought using c:if was a good idea. So I put in my page the following code:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
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:c="http://java.sun.com/jsp/jstl/core"
xmlns:wai="http://www.id.ethz.ch/wai/jsf"
template="/view/listView.xhtml">
<c:if test="#{subscriptionListController.model.listViewName eq 'mySubscriptions'}">
<ui:param name="title" value="#{msg.subscriptionTitleMySubscriptions}"/>
</c:if>
<c:if test="#{subscriptionListController.model.listViewName eq 'paidSubscriptions'}">
<ui:param name="title" value="#{msg.subscriptionTitlePaidSubscriptions}"/>
</c:if>
<c:if test="#{subscriptionListController.model.listViewName eq 'allSubscriptions'}">
<ui:param name="title" value="#{msg.subscriptionTitleAllSubscriptions}"/>
</c:if>
....
but the parameter is not set...
If I let print out the value of #{subscriptionListController.model.listViewName eq 'mySubscriptions'} I get true in the corresponding case and false in the other two cases.
At the beginning I had only 2 possibilities and solved it with the ternary operator:
<ui:param name="title" value="#{subscriptionListController.model.listViewName eq 'mySubscriptions' ? msg.subscriptionTitleMySubscriptions : msg.subscriptionTitlePaidSubscriptions}"/>
and it worked. But now I have more possibilities...
What am I doing wrong?
As indicated by <ui:composition template>, this page represents a template client.
Any <ui:param> outside <ui:define> applies to the master template (the file which you declared in template attribute) and is ignored inside the template client itself. If you intend to prepare variables for inside the template client, you should put <ui:param> inside <ui:define>.
But there's another thing: the original purpose of <ui:param> is to pass variables to the file referenced by <ui:composition template>, <ui:decorate template> or <ui:include src>, not to prepare/set variables inside the current facelet context. For the sole functional requirement of preparing/setting variables in the current EL context, you'd better be using JSTL <c:set> for the job. You can use <ui:param> for this, but this isn't its original intent and didn't work that way in older MyFaces versions.
Thus, so:
<ui:define>
<c:if test="#{subscriptionListController.model.listViewName eq 'mySubscriptions'}">
<c:set var="title" value="#{msg.subscriptionTitleMySubscriptions}"/>
</c:if>
<c:if test="#{subscriptionListController.model.listViewName eq 'paidSubscriptions'}">
<c:set var="title" value="#{msg.subscriptionTitlePaidSubscriptions}"/>
</c:if>
<c:if test="#{subscriptionListController.model.listViewName eq 'allSubscriptions'}">
<c:set var="title" value="#{msg.subscriptionTitleAllSubscriptions}"/>
</c:if>
...
</ui:define>
Unrelated to the concrete problem, you can optimize this as follows without the need for an unmaintainable <c:if> group which would only grow with every subscription type:
<ui:define>
<c:set var="subscriptionTitleKey" value="subscriptionTitle.#{subscriptionListController.model.listViewName}">
<c:set var="title" value="#{msg[subscriptionTitleKey]}"/>
...
</ui:define>
with those keys
subscriptionTitle.mySubscriptions = Title for my subscriptions
subscriptionTitle.paidSubscriptions = Title for paid subscriptions
subscriptionTitle.allSubscriptions = Title for all subscriptions
You are using JSTL with Facelets. JSTL are executed during view build time, and not in render phase. Additionally there some issues with processing them in JSF2 libraries - like in older Mojarra versions, where they didn't work on view scoped beans with partial state saving - see https://stackoverflow.com/a/3343681). This is why your EL expression has worked.
The solution is to avoid JSTL - use ui:repeat instead c:forEach and EL expression and conditional rendering instead of c:if.
I want to invoke one method through a link from Facelets:
My Facelets code is like:
<h:commandButton value="A" actionListener="#{customerData.searchedCustomerListA}" />
<h:commandLink value="A" actionListener="#{customerData.searchedCustomerListA}"/>
Backing bean code is like:
public void searchedCustomerListA(ActionEvent ae){
customerName = "A";
leftCustomerListAvailable.clear();
if(customerDataBean.getSearchedCustomerList(customerName)!= null)
leftCustomerListAvailable =customerDataBean.getSearchedCustomerList("A");
}
The same code is working for <h:commandButton> but not working for <h:commandLink>. How is this caused and how can I solve it?
The technical difference between <h:commandLink> and <h:commandButton> is that the link uses JavaScript to submit the parent form. So if it doesn't work while a syntactically equivalent button works fine, then that can only mean that either JavaScript is disabled in browser, or that the jsf.js file containing the mandatory helper functions isn't included in the page (which you should easily have noticed by seeing JS errors in the JS console of browser's builtin developer toolset).
So, to fix this problem, you need to verify if JS is enabled in browser and that you've a <h:head> component instead of plain HTML <head> in the template, so that JSF will be able to auto-include the jsf.js file.
Or, if your application's business requirements requires that the application functions as designed with JS disabled, then you should stick to <h:commandButton> and throw in some CSS to make it to look like a link (e.g. remove background, padding, border, inset, etc).
Try this, This sould work.
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:form>
<h:commandLink type="button" action="#{testBean.tsetLink}">
<h:outputText value="A" />
</h:commandLink>
</h:form>
</html>
ManagedBean
#ManagedBean
#RequestScoped
public class TestBean {
public void tsetLink(){
System.out.println("Link clicked!!!!!!!!!!!!");
}
}
I my case the cause of this issue was a poorly configured url rewriting filter. One of the filters patterns unintentionally matched http://localhost:8080/mysite/javax.faces.resource/jsf.js.xhtml?ln=javax.faces which prevented jsf.js from being loaded. Check this answer: Clicking h:commandLink causes Uncaught ReferenceError: mojarra is not defined.
I use JSF 2.0 (Apache myFaces) on WebSphere Application Server 8.
I have a bean which contains a list of charts (data for jquery HighCharts).
For each chart I need some JSF components + one Highchart Wrapper written as CompositeCompoent (look here)
So I use the ui:repeat function of jsf 2 like this:
<?xml version="1.0" encoding="UTF-8" ?>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:hc="http://java.sun.com/jsf/composite/chartComponents"
template="/template/mytemplate.xhtml">
<ui:define name="content">
<ui:repeat value="#{chartCtrl.charts }" var="chart" id="chartrepeat" varStatus="chartStatus">
#{chartStatus.index }
<h:form id="chartform_#{chartStatus.index }">
<!-- some jsf select stuff-->
</h:form>
#{chartStatus.index }
<hc:Chart title="Statistics" id="hcchart_#{chartStatus.index }"
<!-- here's the problem-->
<ui:repeat value="#{chart.series }" var="serie">
<hc:ChartSeries series="#{serie.data }" />
</ui:repeat>
</hc:Chart>
#{chartStatus.index }
</p:panel>
</ui:repeat>
<h:outputScript library="js" name="highcharts.js" />
<h:outputScript library="js/modules" name="exporting.js" />
<h:outputScript library="js" name="jquery-1.9.1.min.js" target="head" />
</ui:define>
</ui:composition>
The #{chartStatus.index } works every where but not in hc:Chart id="".
The generated js and div by this CC contains the id 'hcchart_chartdiv'. The index of the current repeat left.
How can I pass the correct number to the id attribute?
EDIT: Composite Component
Here is a part of the hc:Chart where the ID should be used
<cc:implementation>
<div id="#{cc.id}_chartDiv" />
<!-- Highcharts -_>
<script type="text/javascript">
$(function() {
// Data must be defined WITHIN the function. This prevents
// different charts using the same data variables.
var options = {
credits : {
enabled : false
},
chart : {
renderTo : '#{cc.id}_chartDiv',
....
</script>
When I leave the attribute id in hc:Chart empty, then the generated ID is something like "j_id568185923_1_5f9521d0_chartDiv". But still without :row:.
EDIT 2: IndexOf Approach
I tested another approach to set the ID of my chart.
id="hc_#{chartCtrl.charts.indexOf(chart) }"
I tried to use the IndexOf method of my ArrayList. I implemented HashCode und Equals method in all Classes. When I test it, it works fine.
But when I use it with EL I get -1 returned.
Solution
Just as BalusC said I cant use the ID tag for EL. So I simple created a new attribute in my CC. That works fine (just so easy).
Thanks BalusC.
Does someone has another idea?
The id attribute is evaluated during view build time. The <ui:repeat varStatus> is set during view render time which is after view build time. Essentially, you've the same problem as explained in detail here: JSTL in JSF2 Facelets... makes sense?
Any EL expression in the id attribute must be available during view build time. If you replace <ui:repeat> by <c:forEach> which runs during view build time, then the id must be properly set. An alternative is to just get rid of EL in those id attributes based on <ui:repeat varStatus>. JSF will already automatically suffix the IDs of <ui:repeat> child components with the row index.
Note that the <c:forEach> may have unforeseen side effects when used in combination with view scoped beans and partial state saving enabled. See the aforelinked answer for details.