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>
Related
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?
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>
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?
Greeting,
i have created a JSF composite component that takes an object of custom made class, and views its content, everything went good but i need the component to call the set method in the bean holding the custom class object.
Sample Example :
1- the component - componentTest.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:h="http://java.sun.com/jsf/html"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
<composite:attribute name="valueHolder" required="true" type="com.max.ValueHolder" />
</composite:interface>
<composite:implementation>
<h:inputText value="#{cc.attrs.valueHolder.value}" />
</composite:implementation>
</html>
notice that the component use an attribute of a custom type :
<composite:attribute name="valueHolder" required="true" type="com.max.ValueHolder" />
2- the test page - default.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:h="http://java.sun.com/jsf/html"
xmlns:max="http://java.sun.com/jsf/composite/max" >
<h:body>
<h:form target="#self">
<max:componentTest valueHolder="#{user.valueHolder}" />
<h:commandButton value="test" action="#{myBean.action}" />
</h:form>
</h:body>
</html>
and finally the bean - MyBean.java :
package com.max;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import com.max.ValueHolder;
#ManagedBean(name="myBean")
#SessionScoped
public class MyBean{
private ValueHolder valueHolder= new ValueHolder();
public ValueHolder getValueHolder() {
return valueHolder;
}
public void setValueHolder(ValueHolder valueHolder) {
this.valueHolder = valueHolder;
}
public String action(){
return "default";
}
}
My problem is that the method Mybean.setValueHolder() is not being called as the h:inputText is holding the value within the ValueHolder not the ValueHolder itself.
and i have tried setting the value with something like
<c:set var="cachedValueHolder" value="${cc.attrs.valueHolder}"/>
<a4j:ajax event="change" >
<a4j:param value="#{cachedValueHolder}" assignTo="#{cc.attrs.valueHolder}" />
</a4j:ajax>
but it didnt work im not sure why!
Regards
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>