Composite Component with f:convertNumber won't work - jsf

I made a JSF composite component which uses f:convertNumber. However, it cannot convert value. How is this caused and how can I solve it?
currency.xhtml
<?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:composite="http://java.sun.com/jsf/composite"
xmlns:f="http://java.sun.com/jsf/core">
<composite:interface>
</composite:interface>
<composite:implementation>
<f:convertNumber pattern="#,###" currencyCode="\\"/>
</composite:implementation>
</html>
index.xhtml
...
<h:outputText value="10000000">
<mycomp:currency />
</h:outputText>
...
result
10000000

This will indeed not work.
Composite components are interpreted as UI components. The <f:convertNumber>, however, is a taghandler, not an UI component. Basically, it will get applied on the composite itself (and render as useless), not on the target component as you intented.
You've at least two options:
Move <h:outputText> into the composite too,
<composite:interface>
<composite:attribute name="value" />
</composite:interface>
<composite:implementation>
<h:outputText value="#{cc.attrs.value}">
<f:convertNumber pattern="#,###" currencyCode="\\" />
</h:outputText>
</composite:implementation>
so you can ultimately use it as below.
<mycomp:currency value="10000000" />
Subclass NumberConverter with default values set in constructor and use it instead.
#FacesConverter("defaultCurrencyConverter")
public class DefaultCurrencyConverter extends NumberConverter {
public DefaultCurrencyConverter() {
setPattern("#,###");
setCurrencyCode("\\");
}
}
<h:outputText value="10000000" converter="defaultCurrencyConverter" />
When you register this converter in a tagfile as described here Creating custom tag for Converter with attributes,
<tag>
<tag-name>currency</tag-name>
<converter>
<converter-id>defaultCurrencyConverter</converter-id>
</converter>
</tag>
then you can ultimately use it as intented.
<h:outputText value="10000000">
<mycomp:currency />
</h:outputText>
See also:
How to avoid repetitions / use constants in Facelets page?
When to use <ui:include>, tag files, composite components and/or custom components?

Related

Simple for loop in composite component

I want to create a simple for loop in a composite component.
I have a component that is supposed to display a character n times. This seems to be like the simplest scenario. I am not sure what I am doing wrong.
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:composite="http://xmlns.jcp.org/jsf/composite"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<composite:interface>
<composite:attribute name="length" required="true" shortDescription="The length of the anonymized text"/>
</composite:interface>
<composite:implementation>
<c:forEach begin="0" end="#{cc.attrs.length}">•</c:forEach>
</composite:implementation>
</ui:composition>
This errors out:
javax.faces.FacesException: Error Instantiating: com.sun.faces.facelets.tag.composite.ImplementationHandler
I managed to achieve this by creating an an util class in java that creates an empty array to iterate over with <ui:repeat />
view:
<ui:repeat value="#{util:createArray(cc.attrs.length)}" var="index">•</ui:repeat>
util class:
public final class UIHelper {
public static Object[] createArray(int size) {
return new Object[size];
}
}
taglib:
<?xml version="1.0" encoding="UTF-8"?> <facelet-taglib
...
<function>
<function-name>createArray</function-name>
<function-class>package.UIHelper</function-class>
<function-signature>Object[] createArray(int)</function-signature>
</function> </facelet-taglib>

JSF Composite pass all events

I want to create my custom composite component in JSF (with primefaces) that shows a label in front of an input an adds a message at the end.
In order to do that here is my source code:
The composite:
<?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.org/ui"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface componentType="customInput">
<composite:attribute name="label" />
<composite:attribute name="value" />
</composite:interface>
<composite:implementation>
<h:panelGrid columns="3">
<h:outputText value="#{cc.attrs.label}:" />
<p:inputText id="abcde" value="#{cc.attrs.value}" />
<p:message for="abcde" />
</h:panelGrid>
</composite:implementation>
</html>
The backing bean:
#FacesComponent(value = "customInput")
public class CustomInput extends InputText implements NamingContainer {
#Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
}
So far, so good. Now I want to use the events inherited by the p:inputText component. Like for example:
<pch2:PchInputText2 label="Name" id="test2" value="#{testBean.test}">
<p:ajax event="blur" listener="#{chantierFormBean.updateMap()}" />
<p:ajax event="change" listener="#{chantierFormBean.updateMap()}" />
</pch2:PchInputText2>
I know that I could pass these events by adding
<composite:clientBehavior name="change" event="change" targets="abcde" />
to the composite:interface part, but then I have to add one client behavior for every event (in the future). Isn't there a method to pass all events inherited by the primefaces inputtext?
Thanks in advance
That's not possible.
A composite is in first place not the right tool for this job. It's not primarily intented to DRY out and refactor repeated XHTML code. It's intented to create a whole new (input) component tied to a single model value. For example, a <p:fileUpload> and <p:imageCropper> togeher which is tied to a single com.example.Image property. Or three <p:selectOneMenu> together which is tied to a single java.time.LocalDate property.
Use a tagfile instead.
<ui:composition ...>
<h:outputLabel for="#{id}" value="#{label}:" />
<p:inputText id="#{id}" value="#{value}">
<ui:insert />
</p:inputText>
<p:message for="#{id}" />
</ui:composition>
See also:
How to make a grid of JSF composite component?
When to use <ui:include>, tag files, composite components and/or custom components?

How to access EntityManager in the Backing Component of a composite component?

I made a composite component with a backing component:
Xhtml of the 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://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:composite="http://java.sun.com/jsf/composite" xmlns:c="http://java.sun.com/jsp/jstl/core">
<composite:interface componentType="editorCompositeComponent">
<!-- ...bunch of attributes... -->
<composite:attribute name="content" type="java.lang.String" default="" />
</composite:interface>
<composite:implementation>
<!-- ... other components ... -->
<p:editor widgetVar="editorWidget" value="#{cc.attrs.content}" width="600" maxlength="8000" />
<p:commandButton action="#{cc.save(cc.attrs.caseId)}" value="Save" />
</composite:implementation>
</html>
Backing component:
#FacesComponent("editorCompositeComponent")
public class EditorCompositeComponent extends UINamingContainer {
private String content;
// bunch of other variables
public void save(String caseId) {
MemoFile memoFile = new MemoFile();
memoFile.setContent(content);
memoFileService = new MemoFileService();
// Normally this service would be Injected but Injection
// isn't possible in #FacesComponent
memoFileService.save(memoFile);
// the save-method just calls EntityManager's merge etc.
// It works well in all the ManagedBeans
}
// all the getters and setters
}
So, can't inject stuff and can't thereby find EntityManager, so how would one go about persisting the contents of the editor in composite component?
Dependency injection isn't supported in UI components. That's a bit too much of tight coupling of responsibilities. UI component instances aren't supposed to be managed by the container.
Your best bet is to create a separate request scoped managed bean for the task.
#Named
#RequestScoped
public class EditorCompositeBean {
// ...
}
You could pass the composite component instance to its action method:
<p:commandButton ... action="#{editorCompositeBean.save(cc)}" />
Or use that bean as the model instead:
<composite:interface componentType="editorCompositeComponent">
<composite:attribute name="value" type="com.example.EditorCompositeBean" />
</composite:interface>
<composite:implementation>
<p:editor ... value="#{cc.attrs.value.content}" />
<p:commandButton ... action="#{cc.attrs.value.save}" />
</composite:implementation>

How to target a commandLink nested inside an ui:repeat, in a composite component?

I'm trying to create a paginator composite component. The component should render a commandLink for each available page. It looks something like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<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:ui="http://java.sun.com/jsf/facelets">
<cc:interface>
<cc:attribute name="action" targets="jumpButton" required="true"/>
<cc:attribute name="bean" type="java.lang.Object" required="true"/>
</cc:interface>
<cc:implementation>
<ui:repeat value="#{cc.attrs.bean.pages}" var="page">
<h:commandLink id="jumpButton"
actionListener="#{cc.attrs.bean.jumpToPage(page)}">
<h:outputText value="#{page}"/>
</h:commandLink>
</ui:repeat>
</cc:implementation>
</html>
The component is used in various pages like this:
<ccc:paginator bean="#{myBean}"
action="/index?faces-redirect=true&includeViewParams=true"/>
Or:
<ccc:paginator bean="#{myOtherBean}"
action="/dir/index?faces-redirect=true&includeViewParams=true"/>
Notice the use of faces-redirect=true and includeViewParams=true, which as far as I know cannot be used directly on the commandLinks in the composite component.
The problem is that jumpButton cannot be target because it is inside an ui:repeat. I get the message:
javax.servlet.ServletException: /index?faces-redirect=true&includeViewParams=true : Unable to re-target MethodExpression as inner component referenced by target id 'jumpButton' cannot be found.
If I create a command link with id="jumpButton" outside the ui:repeat, the composite component and button works fine. How can I make my composite component work with the command link inside the ui:repeat?
Solution
The managed bean's jumpToPage action:
public String jumpToPage(String path, Integer page) {
...
setCurrentPage(page);
return path;
}
The composite component:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<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:ui="http://java.sun.com/jsf/facelets">
<cc:interface>
<cc:attribute name="bean" type="java.lang.Object" required="true"/>
<cc:attribute name="path" type="java.lang.String" required="true"/>
</cc:interface>
<cc:implementation>
<ui:repeat value="#{cc.attrs.bean.pages}" var="page">
<h:commandLink id="jumpButton"
action="#{cc.attrs.bean.jumpToPage(cc.attrs.path, page)}">
<h:outputText value="#{page}"/>
</h:commandLink>
</ui:repeat>
</cc:implementation>
</html>
Component usage examples:
<ccc:paginator bean="#{myBean}"
path="/index?faces-redirect=true&includeViewParams=true"/>
<ccc:paginator bean="#{myOtherBean}"
path="/dir/index?faces-redirect=true&includeViewParams=true"/>
You should remove the action attribute within <cc:interface>. It is not needed since you invoke the action via the bean attribute.
Update 1
And the outcome of the action you can also define as return value of your bean. Like this:
public class MyBean {
public String jumpToPage(int page){
// ...
return "/index?faces-redirect=true&includeViewParams=true";
}
}
And then use action instead of actionListener:
<h:commandLink id="jumpButton" action="#{cc.attrs.bean.jumpToPage(page)}">
<h:outputText value="#{page}"/>
</h:commandLink>

Using one component (dataTable) id inside composite component

How can I use id of DataTable Component (Primefaces 2.2.1) inside Composite Component in Java Server Faces 2.1 ?
Now, I have a view:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.prime.com.tr/ui"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:haiq="http://java.sun.com/jsf/composite/haiqcomponents"
template="/WEB-INF/templates/login/main.xhtml">
<ui:define name="content">
<h:form prependId="false"> <!-- prependId="true" ??? -->
<p:dataTable id="leakTable" var="leak" value="#{dataExplorer.data}">
<p:column filterBy="#{leak.source}" headerText="source" footerText="source" filterMatchMode="contains" >
<f:facet name="header">
<h:outputText value="source" />
</f:facet>
<h:outputText value="#{leak.source}" />
</p:column>
<!-- Few more columns here -->
</p:dataTable>
<!-- Add : prefix before ID? -->
<haiq:exporter target=":leakTable" fileName="#{msgs.fileName}" imageLibrary="images" pageOnly="false" />
</h:form>
</ui:define>
</ui:composition>
My 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:cc="http://java.sun.com/jsf/composite"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.prime.com.tr/ui">
<!-- INTERFACE -->
<cc:interface>
<cc:attribute name="fileName" default="data" />
<cc:attribute name="target" required="true" type="java.lang.String" />
<cc:attribute name="pageOnly" default="true" type="java.lang.Boolean" />
<cc:attribute name="imageLibrary" default="images" />
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<h:commandLink>
<h:graphicImage library="#{cc.attrs.imageLibrary}" name="excel.png" />
<p:dataExporter type="xls"
target="#{cc.attrs.target}"
fileName="#{cc.attrs.filename}"
pageOnly="#{cc.attrs.pageOnly}" />
</h:commandLink>
</cc:implementation>
</html>
After view rendering, following error occurred:
javax.faces.FacesException: Cannot find component ":leakTable" in view.
at org.primefaces.component.export.DataExporter.processAction(DataExporter.java:89)
at javax.faces.event.ActionEvent.processListener(ActionEvent.java:88)
at javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:769)
at javax.faces.component.UICommand.broadcast(UICommand.java:300)
Removing : before leakTable (in target attribute) or changing preperndId (in form) to true does not solve the problem.
How can I use datatable inside cc?
Similar problem is described here
Apparently you've another NamingContainer parent in the view. To be sure, open page in browser, rightclick and View Source and determine the generated ID of <p:dataTable id="leakTable">. Then, you should grab exactly that ID and prefix with :.
Alternatively, you can also bind the table component to the view and use UIComponent#getClientId() instead to dynamically refer the client ID.
<p:dataTable binding="#{leakTable}" ...>
with
<haiq:exporter target=":#{leakTable.clientId}" ...>
Unrelated to the concrete problem, I suggest to replace <!DOCTYPE><html> of your composite by <ui:component>, which is more natural and it also saves JSF from doing it impliticly everytime.
<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"
xmlns:p="http://primefaces.prime.com.tr/ui">
See also https://stackoverflow.com/tags/composite-component/info.

Resources