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>
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 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
When viewing this page with ?mode=test, the button doesn't work. It loads this page without ?mode=test, but h:panelGroup is rendered (because mode is set somewhere else). I use two methods of sending mode (h:inputHidden f:param) and to the server and nothing helps. View scoped bean is not available in CDI. What is the possible solution to this?
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://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<f:view>
<f:metadata>
<f:viewParam name="mode" value="#{test.mode}" />
</f:metadata>
<h:panelGroup layout="block" rendered="#{test.mode.equals('test')}">
<h:form>
<h:inputHidden value="#{test.mode}" />
<h:commandButton value="Run a method" action="#{test.method}">
<f:param name="mode" value="#{test.mode}" />
</h:commandButton>
</h:form>
</h:panelGroup>
<h:messages />
</f:view>
</html>
Java
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
#Named("test")
#RequestScoped
public class TestBean {
private String mode;
public void method() {
System.out.print(mode);
}
public String getMode() {
return mode;
}
public void setMode(String mode) {
this.mode = mode;
}
}
You've got a wide range of possibilities. The easiest one for you is not to bind the view parameter to the backing bean, just keep it bound to the view:
test.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<f:view>
<f:metadata>
<f:viewParam name="mode" value="#{mode}" />
</f:metadata>
<h:form rendered="#{mode eq 'test'}">
<h:commandButton value="Run a method" action="#{test.method(mode)}" />
</h:form>
<h:messages />
</f:view>
</html>
Test.java
#Named
#RequestScoped
public class Test {
public void method(String mode) {
System.out.print(mode);
}
}
If you however would like to switch to #ViewScoped, CDI compatible annotation is now available in JSF 2.2 version. The namespaces you're using suggest you do use that version, so go with it. For JSF prior versions, there's also the chance to do it with custom Omnifaces' annotation.
See also:
Omnifaces #ViewScoped annotation
#ViewScoped in CDI with JSF 2.2
Differences between EL 2.1 and 2.2
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>