I created a Facelet component to extend h:commandLink (to add some functionality and rounded corners).
<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">
<span class="btn-left btn-corners"> </span>
<span type="submit" class="submit">
<h:commandLink id="#{id}" value="#{label}" action="#{action}" />
</span>
<span class="btn-right btn-corners"> </span> </ui:composition>
My new component can be accessed using
<my:commandLink id="continue" label="continue" action="#{applyBacking.submit}"/>
and the Java code is
public String submit(){
...
}
However it gives me an error "ApplyBacking does not have the property submit".
I understand the reason for this error because while rendering my:commandLink, it tries to evaluate #{applyBacking.submit} to a property. Instead, I want the info about the method to the invoked (applyBacking.submit) to be passed to the template and evaluated while rendering h:commandLink.
Any suggestions?
Create a composite component instead (tutorial here), it enables you to define bean actions as attribtues.
Here's a kickoff example:
/resources/components/commandLink.xhtml
<ui:component
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:cc="http://java.sun.com/jsf/composite">
<cc:interface>
<cc:attribute name="id" required="true" />
<cc:attribute name="label" required="true" />
<cc:attribute name="action" method-signature="java.lang.String action()" required="true" />
</cc:interface>
<cc:implementation>
<span class="btn-left btn-corners"> </span>
<span type="submit" class="submit">
<h:commandLink id="#{cc.attrs.id}" value="#{cc.attrs.label}" action="#{cc.attrs.action}" />
</span>
<span class="btn-right btn-corners"> </span>
</cc:implementation>
</ui:component>
/somepage.xhtml
<!DOCTYPE html>
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:my="http://java.sun.com/jsf/composite/components">
<h:head>
<title>SO question 4608030</title>
</h:head>
<h:body>
<h:form>
<my:commandLink id="continue" label="continue" action="#{applyBacking.submit}"/>
</h:form>
</h:body>
</html>
By the way, I would personally prefer using JS/jQuery for the rounded corners part, for example the jQuery corner plugin. Just give your commandlink a specific styleClass and let JS do the magic.
Related
When using MyFaces, I'm having trouble using ui:insert to insert a converter into a template component. The JSF implementation complains the the parent of my convertdateTime is not of the right type:
Parent not composite component or an instance of ValueHolder: org.primefaces.component.fieldset.Fieldset#6559fbd6
It seemingly then checks a Fieldset (which is a parent of a parent in my case)
<my:outputTextWithLabel id="creationdate" label="#{msg.common_datecreated}" value="#{bean.date}">
<my2:convertDateTime/>
</my:outputTextWithLabel>
My outputTextWithLabel components looks like this:
<!DOCTYPE html>
<html 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:jsf="http://xmlns.jcp.org/jsf">
<ui:composition>
<h:panelGroup rendered="#{rendered or empty rendered}">
<p:outputLabel for="#{id}" value="#{label}" />
<div class="ui-inputfield ui-inputtext ui-widget ui-state-default ui-corner-all">
<h:outputText id="#{id}" value="#{value}">
<ui:insert/>
</h:outputText>
</div>
</h:panelGroup>
</ui:composition>
</html>
This code works fine with Mojarra...
So, just for clarity: isn't this supposed to work? Is Mojarra correct, or MyFaces? And what could be done here instead?
(in this example we use primefaces components, but question is not related with Primefaces)
Introduction:
In order to have ajax support for composite component elements we just use element:
composite:clientBehavior
Let's look at this situation:
<composite:interface>
<composite:clientBehavior name="dateSelect" event="dateSelect" targets="id111 id222" />
</composite:interface>
<composite:implementation>
<p:calendar id="id111" value="#{csController.selectedDate}" mode="inline"
mindate="#{csController.minDate(2013,9)}"
maxdate="#{csController.maxDate(2013,9)}"/>
<p:calendar id="id222" value="#{csController.selectedDate}" mode="inline"
mindate="#{csController.minDate(2013,10)}"
maxdate="#{csController.maxDate(2013,10)}"/>
</composite:implementation>
We we have two elements and ajax support for these two elements. This works as expected.
We could even change to this interface:
<composite:interface>
<composite:clientBehavior name="dateSelect" event="dateSelect" targets="id111" />
<composite:clientBehavior name="dateSelect" event="dateSelect" targets="id222" />
</composite:interface>
and this still works fine.
Problem:
I would like to change implementation to show custom number of calendars, not exaclty two like shown above. So I tried this:
<composite:interface>
<composite:attribute name="firstMonth" default="1"/>
<composite:attribute name="lastMonth" default="12"/>
<c:forEach var="i" begin="#{cc.attrs.firstMonth}" end="#{cc.attrs.lastMonth}">
<composite:clientBehavior name="dateSelect" event="dateSelect" targets="#{cc.clientId}#{i}"/>
</c:forEach>
</composite:interface>
<composite:implementation>
<c:forEach var="i" begin="#{cc.attrs.firstMonth}" end="#{cc.attrs.lastMonth}">
<p:calendar id="#{cc.clientId}#{i}" value="#{csController.selectedDate}" mode="inline"
mindate="#{csController.minDate(2013,i)}"
maxdate="#{csController.maxDate(2013,i)}"/>
</c:forEach>
</composite:implementation>
This of course doesn't work.
Question:
How to get ajax support for all elements generated by c:forEach loop?
Actually you don't need to follow that procedure to add ajax functionality for your components. Just use f:ajax into your calendars. As c:forEach works before it, that's not a problem:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<cc:interface>
<cc:attribute name="val" />
</cc:interface>
<cc:implementation>
<c:forEach var="date" items="#{cc.attrs.val}">
<p:calendar value="#{date}" mode="inline">
<f:ajax event="dateSelect" listener="#{bean.printCurrentValues}" />
</p:calendar>
</c:forEach>
</cc:implementation>
</html>
Being this the main page:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:composite="http://java.sun.com/jsf/composite/composites">
<h:head />
<h:body>
<h:form>
<composite:comp val="#{bean.calendars}" />
</h:form>
</h:body>
</html>
I added this listener method to the bean to check current dates are really changing:
#ManagedBean
#ViewScoped
public class Bean {
public List<Date> calendars = Arrays.asList(new Date(), new Date());
public List<Date> getCalendars() {
return calendars;
}
/**
* Just prints the updated values ;-)
*/
public void printCurrentValues() {
System.out.println("Current selected dates: " + calendars);
}
}
For your specific case you only would need to add a list of minDates and other one of maxDates in order to bind them to your calendar properties. Alternatively, you could also create your own class which wraps them to have everything packed. That's your own choice.
Solution above will work for dateSelect event and a provided listener. What if we want to have different events with different listeners?
Just define them on the interface and allow-deny them with JSTL's c:if. Here you have the composite implementation for a single input that can launch a listener method, also a dynamic number of p:calendar components, which will notify a user-defined method. By default none of them is launched.
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<cc:interface>
<cc:attribute name="dateSelect" default="false" />
<cc:attribute name="click" default="false" />
<cc:attribute name="dateSelectListener" method-signature="void f()" />
<cc:attribute name="inputClickedListener" method-signature="void f()" />
<cc:attribute name="val" />
</cc:interface>
<cc:implementation>
<h:inputText>
<c:if test="#{cc.attrs.click}">
<f:ajax event="click" listener="#{cc.attrs.inputClickedListener}" />
</c:if>
</h:inputText>
<c:forEach var="date" items="#{cc.attrs.val}">
<p:calendar value="#{date}" mode="inline">
<c:if test="#{cc.attrs.dateSelect}">
<f:ajax event="dateSelect" listener="#{cc.attrs.dateSelectListener}" />
</c:if>
</p:calendar>
</c:forEach>
</cc:implementation>
</html>
And here the main page:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:composite="http://java.sun.com/jsf/composite/composites">
<h:head />
<h:body>
<h:form>
<composite:comp val="#{bean.calendars}" dateSelect="true"
dateSelectListener="#{bean.printCurrentValues}" click="true"
inputClickedListener="#{bean.inputClicked}" />
</h:form>
</h:body>
</html>
Tested with Mojarra JSF 2.1.25 & Tomcat 7.
In the given case, I want to use a facelet with different ManagedBeans, so the regarding action-bean is given as an parameter:
<?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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html" >
<h:body>
<ui:include src="ratings.xhtml" >
<ui:param name="createAction" value="#{myOneCreateAction}" />
<ui:param name="ratings" value="#{context.ratings}" />
</ui:include>
</h:body>
</html>
I'm giving the create action as parameter value="#{myOneCreateAction}".
Within that facelet is a component also being used several times on other pages - so I try to refactor it in a composite component.
<html 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:io="http://java.sun.com/jsf/composite/inoutComponents">
<ui:composition>
<rich:dataTable id="ratingTblId"
value="#{ratings}"
var="rating">
<rich:column>
<io:removeButton
id="removeButton"
actionMethod="#{createAction.removeRating}"
immediate="true"
render=":#{rich:clientId('ratingTblId')}" />
<h:commandButton
id="removeButton2"
actionListener="#{createAction.removeRating}"
immediate="true" >
<f:ajax render="ratingTblId" />
</h:commandButton>
</rich:column>
</rich:dataTable>
</ui:composition>
</html>
See, how the method is given as actionMethod="#{createAction.removeRating}" to the component. This component itself looks like following:
<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:cc="http://java.sun.com/jsf/composite">
<!-- INTERFACE -->
<cc:interface>
<cc:attribute
name="actionMethod"
targets="remove"
method-signature="void f(javax.faces.event.ActionEvent)"/>
<cc:attribute name="render" required="false" />
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<h:commandButton
id="remove"
actionListener="#{cc.attrs.actionMethod}"
onclick="if (!confirm('Do you really?')) { return false; }">
<f:ajax execute="#this" render="#{cc.attrs.render}" />
</h:commandButton>
</cc:implementation>
</ui:composition>
and last but not least, the managed bean
Name("myOneCreateAction")
#Scope(ScopeType.CONVERSATION)
public class MyOneCreateAction {
...
public void removeRating(ActionEvent ev) {
// do something
}
...
}
Surprisingly, while the removeButton2 correctly jumps into the right function, the composite components version returns a
javax.faces.event.AbortProcessingException: Target Unreachable,
identifier 'createAction' resolved to null
instead. Am using Mojarra JSF 2.1.26 with Seam 2.3.1.CR1. There are no nested composite components. When replacing the composite component parameter to #{myOneCreateAction.removeRating}, it works like expected.
Has anybody seen this before? Am I blind? Any work-arounds known... ? Thanks in advance!
As a work-around I rewrote the component to give action bean and action method as two separate parameters resolving them as following
xhtml:
<io:removeButton
id="removeButton"
actionBean="#{createAction}"
actionMethod="removeRating"
immediate="true"
render=":#{rich:clientId('ratingTblId')}" />
composite component:
<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:cc="http://java.sun.com/jsf/composite">
<!-- INTERFACE -->
<cc:interface>
<cc:attribute name="actionBean" />
<cc:attribute name="actionMethod" />
<cc:attribute name="render" required="false" />
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<h:commandButton
id="remove"
action="#{cc.attrs.actionBean[cc.attrs.actionMethod]}"
onclick="if (!confirm('Do you really?')) { return false; }">
<f:ajax execute="#this" render="#{cc.attrs.render}" />
</h:commandButton>
</cc:implementation>
</ui:composition>
also changing the action methods signature to return String instead of void. That does not look that super sexy anymore, but works. :-/
I was having the same issue, found out that it has been fixed on version 2.2.15:
https://github.com/javaserverfaces/mojarra/issues/4271
I have this composite component that based on what is selected in a drop down(STREET, PO BOX) renders either another composite component streetAddressUpdate or postalBoxAddressUpdate. Here is the code
<?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:cc="http://java.sun.com/jsf/composite"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:tad="http://java.sun.com/jsf/composite/tmr/ad"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:tmi="http://java.sun.com/jsf/composite/tmr/mi">
<!-- INTERFACE -->
<cc:interface>
<cc:attribute
name="title"
default="Postal address" />
<cc:attribute
name="postalAddress"
type="qdtmr.comp.cbui.address.fieldmodel.PostalAddress"
default="#{addressUpdate.postalAddress}"
required="true" />
</cc:interface>
<cc:implementation>
<ol class="questions">
<!-- Postal address type -->
<li><h:outputLabel for="postalAddressType">
<span class="label">#{tcum.postalAddressType}</span>
</h:outputLabel> <p:selectOneMenu
id="postalAddressType"
value="#{cc.attrs.postalAddress.postalAddressType}"
valueChangeListener="#{addressUpdate.changePostalAddress}">
<f:selectItems value="#{addressController.postalAddressTypesList}" />
<p:ajax
update=":#{cc.clientId}:postalAddressPanel, :#{cc.clientId}:poBoxPanel" />
</p:selectOneMenu></li>
<p:outputPanel id="postalAddressPanel">
<ui:fragment rendered="#{cc.attrs.postalAddress.postalAddressType == 'STREET'}">
<tad:streetAddressUpdate
streetAddress="#{cc.attrs.postalAddress.postalStreetAddress}"
title="" />
</ui:fragment>
<p:outputPanel id="poBoxPanel">
<ui:fragment rendered="#{cc.attrs.postalAddress.postalAddressType == 'POBOX'}">
<tad:postalBoxAddressUpdate
postalBoxAddress="#{cc.attrs.postalAddress.postalBoxAddress}" />
</ui:fragment>
</p:outputPanel>
</ol>
</cc:implementation>
</html>
The thing that is confusing me is that if I take out the ui:fragment tag, my values on the page make their way back to the postalStreetAddress/postalBoxAddress model beans, but with the ui:fragment in their, my values do not end up making to the postalStreetAddress/postalBoxAddress.
The reason I have the ui:fragment is because depending on the postalAdressType either the postalBoxAddressUpdate composite component will be used or the postalStreetAddressUpdate component will be used.
Any help will be greatly appreciated. I suspect its the ui:fragment
EDIT: I checked up taking out the ui:fragment and it does seem to be the culprit. I would really like to understand whats happening
I am goin to explain a problem that I have when I use one composite component inside a template.
Imagine one view like this, that work with a generic managed bean with view scope. I pass it to the template as a parameter.
<?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:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:trkal="http://java.sun.com/jsf/composite/trkalcomponents">
<ui:composition template="/template.xhtml">
<ui:param name="maisuBean" value="#{genericBean}" />
</ui:composition>
</html>
The template is like this. Beside other components, it also use one composite component.
<?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:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:trkal="http://java.sun.com/jsf/composite/trkalcomponents">
<h:head>
<title>Titulo</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</h:head>
<h:body>
<h:form enctype="multipart/form-data">
...
<trkal:toolbarbuttonwidget id="buttonToolbar" label="Action" iconName="toolbar.png"
buttonAction="#{maisuBean.myActionListener}"
>
</trkal:toolbarbuttonwidget>
...
<h:commandLink id="otherButton" actionListener="#{maisuBean.myActionListener}">
<h:graphicImage library="images" name="toolbar.png" />
<h:outputText value="Other Action" />
</h:commandLink>
...
</h:form>
</h:body>
</html>
As you can see, this template use one composite components that allow specify the action listener that hear this event.
<composite:interface>
<composite:attribute name="id" />
<composite:attribute name="buttonAction" method-signature="void myAction(javax.faces.event.ActionEvent)" targetAttributeName="actionListener"/>
<composite:attribute name="iconName" />
<composite:attribute name="label"/>
<composite:attribute name="title"/>
<composite:attribute name="styleClass"/>
</composite:interface>
<composite:implementation>
<h:outputStylesheet target="head" library="trkalcomponents" name="toolbarbuttonwidget.css" />
<h:commandLink id="buttonAction">
<h:graphicImage library="images" name="#{cc.attrs.iconName}" />
<h:outputText value="#{cc.attrs.label}" />
</h:commandLink>
</composite:implementation>
If I click in otherButton, it work fine, but if I click in buttonToolbar it don't work.
09-nov-2012 19:16:28 javax.faces.event.MethodExpressionActionListener processAction
GRAVE: Se ha recibido 'javax.el.PropertyNotFoundException' al invocar la escucha de acción '#{maisuBean.myActionListener}' para el componente 'buttonAction'
09-nov-2012 19:16:28 javax.faces.event.MethodExpressionActionListener processAction
GRAVE: javax.el.PropertyNotFoundException: /template.xhtml #20,6 buttonAction="#{maisuBean.myActionListener}": Propiedad 'myActionListener' no hallada en el tipo com.joxeja.test.ToolBarBean
at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:111)
It seem that it can't resolve the EL expression inside the composite component.
What is wrong? How can I use one composite componente inside one template?
I am using Mojarra 2.1.7
Thanks.
Sorry I missed this on the first read:
Your implementation isn't targeting the attributes properly. I made this mistake (as I'm pretty sure all of us do). You need to reference the attributes as -> #{cc.attrs.someAttribute} not by their name. You do this for most of the elements, but not the actionListener. If that is your code it should fix it. Your signatures are correct. You're trying to use the targetAttributeName which I'm not familiar with. My guess is you need to set the id of the component to that name (so your button would be myAction not actionListener (if I'm referencing the same example you are).
That aside, how I would do it is:
<composite:interface>
<composite:attribute name="id" />
<!--
<composite:attribute name="buttonAction" method-signature="void myAction(javax.faces.event.ActionEvent)" targetAttributeName="actionListener"/>
-->
<composite:attribute name="buttonAction" method-signature="void action(javax.faces.event.ActionEvent)"/>
<composite:attribute name="iconName" />
<composite:attribute name="label"/>
<composite:attribute name="title"/>
<composite:attribute name="styleClass"/>
</composite:interface>
<composite:implementation>
<h:outputStylesheet target="head" library="trkalcomponents" name="toolbarbuttonwidget.css" />
<!-- fix below -->
<h:commandLink id="buttonAction" actionListener=#{cc.attrs.buttonAction}>
<h:graphicImage library="images" name="#{cc.attrs.iconName}" />
<h:outputText value="#{cc.attrs.label}" />
</h:commandLink>
</composite:implementation>
I like this method because it is similar to the rest of the way pages are marked up and it seems simple. Give it a shot.