I just dont get it:
If I want my composite component to insert children, I use <composite:insertChildren/> but #{cc.childCount} always returns 0 in that case. On the other hand, If I do not use <composite:insertChildren/> I always get correct childCount without children being rendered. Why is that happening?
All I want to do in my component is to render some "default" panel if there are no children and do not render it in other case - behavior similar to <ui:insert name="param_name">default value</ui:insert>. So I need both insertChildren and childCount which do not seem to work together.
Here is the code:
<my:test>
<h:outputText value="child1" rendered="#{some.trueValue}"/>
<h:outputText value="child2" rendered="#{some.trueValue}"/>
<my:test>
If I use implementation below, I get 2 rendered as expected
<composite:implementation>
<h:outputText value="#{cc.childCount}"/>
</composite:implementation>
When insertChildren is used I get both children rendered and 0 at the end:
<composite:implementation>
<composite:insertChildren/>
<h:outputText value="#{cc.childCount}"/>
</composite:implementation>
Whereas my goal is:
<composite:implementation>
<composite:insertChildren/>
<h:panelGroup rendered="#{cc.childCount == 0}">
some default data
</h:panelGroup>
</composite:implementation>
Any ideas/workarounds?
Put the children in a panelGroup with an id (eg children).
Then
#{component.findComponent('children').childCount}
will give you the correct value. Good luck!
From the cc:insertChildren documentation:
Any child components or template text within the composite component tag in the using page will be re-parented into the composite component at the point indicated by this tag's placement within the section.
So by using <cc:insertChildren> you actually move the children from the #{cc} component to the component in which you specified <cc:insertChildren>, effectively making them (great)* grandchildren of #{cc}. To get easy access to these relocated children, I use a <ui:fragment> as a parent:
<ui:fragment binding="#{childContainer}">
<cc:insertChildren/>
</ui:fragment>
Now you can use #{childContainer.childCount} to get to count the children you specified for your composite component. This solution is a bit fragile though: you can only use your composite component once per view, because of the binding. This problem can of course be solved by binding a FacesComponent bean to your composite component. First the bean:
package com.stackoverflow.jsfinsertchildrenandcountexample;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIComponent;
import javax.faces.component.UINamingContainer;
#FacesComponent(value = "myCompositeComponent")
public class MyCompositeComponent extends UINamingContainer {
private UIComponent childContainer;
public UIComponent getChildContainer() {
return childContainer;
}
public void setChildContainer(UIComponent childContainer) {
this.childContainer = childContainer;
}
}
And now you can bind this class to your composite component:
<?xml version='1.0' encoding='UTF-8' ?>
<ui:component
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
>
<cc:interface componentType="myCompositeComponent">
</cc:interface>
<cc:implementation>
I have #{cc.childContainer.childCount} children.
<ui:fragment binding="#{cc.childContainer}">
<cc:insertChildren/>
</ui:fragment>
</cc:implementation>
</ui:component>
Now you have a composite component with <cc:insertChildren> and direct access to these children.
EDIT: incorporated BalusC comment.
I had a similar problem. I switched to c:if and it worked, so in your case it would be
<composite:implementation>
<composite:insertChildren/>
<c:if test="#{cc.childCount == 0}">
some default data
</c:if>
</composite:implementation>
Related
I have a big form that I need to reuse in multiple pages. So, I decided to create a
<ui:composition> that contains the form and include it in some pages (page1.xhtml and page2.xhtml).
form.xhtml:
<ui:composition ...>
<!-- The form goes here -->
</ui:composition>
This form has a controller called FormController.
In page1.xhtml and page2.xhtml I just include the form using a <ui:include> tag:
<ui:include src="/WEB-INF/.../form.xhtml"/>
I need to initialize a property in the FormController bean, so, in page1.xhtml I decided to set an attribute with the Id that I need (for example 5):
<c:set var="id" scope="request" value ="5"/>
And in the controller I just get the value of this attribute:
#PostConstruct
public init() {
Long id = ((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getAttribute("id");
//Do some queries to the database
}
Until know, everything works fine. But in page2.xhtml the "initialization" of the bean property has to be done after an ajax request, so I used the following code:
<h:selectOneMenu ...>
<f:selectItems ...>
<f:ajax listener="#{otherBean.doSomething}" render="panel"/>
</h:selectOneMenu>
<h:panelGroup id="panel">
<c:set var="id" scope="request" value ="#{otherBean.id}"/>
<ui:include src="/WEB-INF/.../form.xhtml"/>
</h:panelGroup>
What is weird is that this works just the first time I select an element in the <h:selectOneMenu>. The second time, the doSomething() method is called but the panel is not rendered (I don't know why, you know why?), so I decided to explore the following alternative that works well in both pages, but I feel that it isn't a good solution:
#{bean.init(otherBean.id)}
<ui:include src="/WEB-INF/modules/company/company.xhtml"/>
As you see, I am just calling an init method (before the <ui:include>) with the argument I need. In the controller I just set the property and do the corresponding queries:
public init(Long id) {
this.id = id;
//Do some queries
}
What do you thing about this solution?
If the form has to be initialized at start, you can use
<f:metadata>
<f:viewAction action="#{otherBean.initSomething('MYID2')}"/>
</f:metadata>
If the form has to be initialized by an action
<h:commandButton action='#{otherBean.doSomething('MYID1')}'...>
or
<f:ajax listener="#{otherBean.doSomething('MYID')}" .../>
on my jsf page I use a composite component which is basically an input field. My backing bean holds a list that contains all allowed languages which the user is allowed to enter. On submit I want to validate the user's input based on that list.
So I need to pass the list to the validator because obtaining that list takes to long, so I do it when the backing bean is initialized. But I struggle here.
My composite component looks like this:
<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="value" required="true" />
<composite:editableValueHolder name="nameValidator" targets="value" />
</composite:interface>
<composite:implementation>
<h:panelGroup>
<h:inputText id="value" value="#{cc.attrs.value}" />
</h:panelGroup>
</composite:implementation>
</html>
My validator look like this:
#FacesValidator("OcrLanguageValidator")
public class OcrLanguageValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
Object param1 = context.getExternalContext().getRequestParameterMap().get("param1");
if (param1 != null) {
}
}
}
And I am using the component like this in my jsf page:
<h:form id="newParameter" styleClass="form-horizontal">
<uiComps:testCC value="test" >
<f:validator validatorId="OcrLanguageValidator" for="nameValidator" />
<f:param name="param1" value="blub" />
</uiComps:testCC>
</h:form>
When the validator method is called the param1 is always null. How can I achieve my goal? I use Mojarra 2.3.
Edit because of possible duplication:
I don't think, that this is a duplication. I am using a composite component here. Without it is a way to do it. But I don't like the idea of adding a special attribute to the component just because I need it in one single case but not the others. Hope there might be a better solution to this problem.
The premise is quite straightforward: I have a page layout that relies on nested composite components (CC) to do the rendering, i.e. one JSF page references a CC, which itself references another CC, which contains a call to a third CC. - Source below.
Now, when that third CC wants to execute a bean method using ajax which would result in a fully self-contained partial update... nothing happens.*
I have searched SO and other places extensively, and have investigated all points of BalusC's insightful post here, but I've come up empty. Trace logging finally turned up the following message during the apply, validate and render stages, resulting in an "empty" response: FINER [javax.enterprise.resource.webcontainer.jsf.context] ... JSF1098: The following clientIds were not visited after a partial traversal: fooForm:j_idt14:j_idt15:j_idt18 . This is a waste of processor time and could be due to an error in the VDL page.
*) This only happens under very particular circumstances (which, though, are the exact definition of my use case):
Deeply nested CCs, at least two parent levels.
The nesting must be implicit, i.e. different CCs calling another, not just nested tags directly inside the calling page.
"Higher" CCs pass down children which are inserted in the "lower" CC using <cc:insertChildren />.
The CC that performs the ajax call and partial update contains actions dynamically created from the CC's attributes or clientId. (But not even necessarily in the same call, just inside the same component.)
All have to be met at the same time: If I use the innermost CC higher up in the hierarchy (or the nesting including the call to the final CC is all inside the calling page), everything works. If I use facets, no problem. If I remove or hard-code the action parameter, all good.
(Currently testing on EE6, EAP 6.4, but is the same in an EE7 project running on EAP 7.0)
Calling page:
<!DOCTYPE html>
<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:my="http://java.sun.com/jsf/composite/components/nestedcomponents">
<h:head>
<title>Nested composite component test</title>
</h:head>
<h:body>
<h:form id="fooForm">
<h2>Works</h2>
<my:randomString saveBean="#{util}" saveAction="doSomething" />
<h2>Doesn't</h2>
<my:containerInsertingAnotherUsingInsertChildren>
<my:randomString saveBean="#{util}" saveAction="doSomething" />
</my:containerInsertingAnotherUsingInsertChildren>
</h:form>
</h:body>
</html>
Innermost CC: (<my:randomString>, black frame; the #{util} is a request-scoped bean with one-liner dummy methods)
<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:f="http://java.sun.com/jsf/core">
<cc:interface>
<cc:attribute name="someValue" />
<!--cc:attribute name="someAction" method-signature="void action()" /-->
<!--cc:attribute name="someAction" targets="btn" targetAttributeName="action" /-->
<cc:attribute name="saveBean" />
<cc:attribute name="saveAction" />
</cc:interface>
<cc:implementation>
<h:panelGroup layout="block" id="box" style="border: 1px solid black; margin: 3px; padding: 3px;">
<h:outputText value="#{cc.attrs.id} / #{cc.clientId} / #{util.getRandomString()} " />
<h:commandLink id="btn" value="save!" action="#{cc.attrs.saveBean[cc.attrs.saveAction]}" >
<f:ajax render="box" immediate="true" />
</h:commandLink>
</h:panelGroup>
</cc:implementation>
</html>
Outer, wrapping CC: (<my:containerInsertingAnotherUsingInsertChildren>, red frame)
<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:my="http://java.sun.com/jsf/composite/components/nestedcomponents">
<cc:interface>
</cc:interface>
<cc:implementation>
<h:panelGroup layout="block" style="border: 1px solid red; margin: 3px; padding: 3px;">
<my:containerUsingInsertChildren>
<cc:insertChildren />
</my:containerUsingInsertChildren>
</h:panelGroup>
</cc:implementation>
</html>
Intermediate CC: (<my:containerUsingInsertChildren>, blue frame)
<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">
<cc:interface>
</cc:interface>
<cc:implementation>
<h:panelGroup layout="block" style="border: 1px solid blue; margin: 3px; padding: 3px;">
<cc:insertChildren />
</h:panelGroup>
</cc:implementation>
</html>
As I wrote, hard-coded calls work as expected and update the little attached box. As soon as the bean method involves parameters (attributes) of the CC, and the CC sits deep enough in the hierarchy, they just get skipped.
I'm at a loss, and solutions or workarounds are most welcome.
This is caused by a bug in Mojarra related to generating the client ID of a composite component nested via <cc:insertChildren>. If you assign the composite components a fixed ID as in:
<h:form id="form">
<my:level1 id="level1">
<my:level3 id="level3" beanInstance="#{bean}" methodName="action" />
</my:level1>
</h:form>
Whereby level1.xhtml is implemented as:
<cc:implementation>
<my:level2 id="level2">
<cc:insertChildren />
</my:level2>
</cc:implementation>
And level2.xhtml as:
<cc:implementation>
<cc:insertChildren />
</cc:implementation>
And level3.xhtml as:
<cc:implementation>
<h:commandButton id="button" value="Submit #{component.clientId}"
action="#{cc.attrs.beanInstance[cc.attrs.methodName]}">
<f:ajax />
</h:commandButton>
</cc:implementation>
Then you would notice that the #{component.clientId} in the submit button says form:level1:level3:button instead of form:level1:level2:level3:button as per the expectation (see also the answer to this related question How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar").
This was the clue to find the mistake in tree traversal. The log message which you got is actually misleading.
JSF1098: The following clientIds were not visited after a partial traversal: form:level1:level3:button. This is a waste of processor time and could be due to an error in the VDL page.
The first part is correct, that's indeed the technical problem, but the assumption about the potential cause as implied by "This is a waste of processor time and could be due to an error in the VDL page" is incorrect. It would theoretically only happen if the visit resulted in a stack overflow error, which happens only around 1000 levels deep. This is far from the case here.
Coming back to the root cause of the problem of the wrong client ID, this is unfortunately not trivial to work around without fixing the core JSF implementation itself (I've reported it as issue 4339). However, you can work around it by supplying a custom visit context which provides the correct sub tree IDs to visit. Here it is:
public class PartialVisitContextPatch extends VisitContextWrapper {
private final VisitContext wrapped;
private final Pattern separatorCharPattern;
public PartialVisitContextPatch(VisitContext wrapped) {
this.wrapped = wrapped;
char separatorChar = UINamingContainer.getSeparatorChar(FacesContext.getCurrentInstance());
separatorCharPattern = Pattern.compile(Pattern.quote(Character.toString(separatorChar)));
}
#Override
public VisitContext getWrapped() {
return wrapped;
}
#Override
public Collection<String> getSubtreeIdsToVisit(UIComponent component) {
Collection<String> subtreeIdsToVisit = super.getSubtreeIdsToVisit(component);
if (subtreeIdsToVisit != VisitContext.ALL_IDS) {
FacesContext context = getFacesContext();
Map<String, Set<String>> cachedSubtreeIdsToVisit = (Map<String, Set<String>>) context.getAttributes()
.computeIfAbsent(PartialVisitContextPatch.class.getName(), k -> new HashMap<String, Set<String>>());
return cachedSubtreeIdsToVisit.computeIfAbsent(component.getClientId(context), k ->
getIdsToVisit().stream()
.flatMap(id -> Arrays.stream(separatorCharPattern.split(id)))
.map(childId -> component.findComponent(childId))
.filter(Objects::nonNull)
.map(child -> child.getClientId(context))
.collect(Collectors.toSet())
);
}
return subtreeIdsToVisit;
}
public static class Factory extends VisitContextFactory {
private final VisitContextFactory wrapped;
public Factory(VisitContextFactory wrapped) {
this.wrapped = wrapped;
}
#Override
public VisitContextFactory getWrapped() {
return wrapped;
}
#Override
public VisitContext getVisitContext(FacesContext context, Collection<String> ids, Set<VisitHint> hints) {
return new PartialVisitContextPatch(getWrapped().getVisitContext(context, ids, hints));
}
}
}
By default, when Mojarra comes to <my:level2> during tree traversal and invokes getSubtreeIdsToVisit(), it would get an empty set, because the component ID string level2 is not present in the client ID string form:level1:level3:button. We need to override and manipulate getSubtreeIdsToVisit() in such way that it "correctly" returns form:level1:level3:button when <my:level2> is being passed in. This can be done by breaking down the client ID into parts form, level1, level3 and button and trying to find it as direct child of given component.
In order to get it to run, register it as below in faces-config.xml:
<factory>
<visit-context-factory>com.example.PartialVisitContextPatch$Factory</visit-context-factory>
</factory>
Said that, make sure that you aren't abusing composite components for sake of templating. Better use tag files instead. See also When to use <ui:include>, tag files, composite components and/or custom components?
I have the following XHTML:
<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">
<head>
<title>TODO supply a title</title>
</head>
<body>
<f:metadata>
<f:viewParam id="productCV" name="productName" value="#{productBean.product}"
converter="#{productConverter}" required="true"/>
</f:metadata>
<ui:composition template="/templates/mastertemplate.xhtml">
<!-- Define the page title for this page-->
<ui:define name="pageTitle">
<h:outputFormat value="#{msgs.productPageTitle}">
<f:param value="#{productBean.product.description}"/>
</h:outputFormat>
</ui:define>
<!-- Pass the categoryName parameter to the sidebar so the category of this product is highlighted-->
<ui:param name="categoryName" value="#{productBean.product.categoryName}"/>
<ui:define name="content">
<!-- If productconversion failed, show this error-->
<h:message id="error" for="productCV" style="color: #0081c2;" rendered="#{productBean.product == null}" />
<!-- If productconversion succeeded show the product page-->
<h:panelGroup rendered="#{productBean.product != null}">
<p>#{productBean.product.description} #{productBean.product.categoryName}</p>
<h:form>
<h:commandLink action="#{cartBean.addItemToCart(productBean.product)}">
<f:ajax event="action" render=":cart :cartPrice" />
<h:graphicImage value="resources/img/addToCart.gif"/>
</h:commandLink>
</h:form>
</h:panelGroup>
</ui:define>
</ui:composition>
</body>
</html>
At the top I accept a String as GET param which I run through a converter and then get a Product object, I place this in the productBean.product, that bean has a setter and getter for the Product attribute, that's all.
I then use this object to show info etc. this works fine. I also add commandLink to add it to my cart using AJAX. This refuses to work if my ProductBean is in RequestScope, when I put it in SessionScope it works, but will only add the product 1 time.
As best I know this should be a straight forward RequestScope, I don't understand why it does work with SessionScope.
I have read through this post but I don't think I'm violating any of those rules.
For completeness, this is my ProductBean:
import be.kdg.shop.model.stock.Product;
import java.util.logging.Logger;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
#Named
#RequestScoped
public class ProductBean {
private static final Logger logger = Logger.getLogger(ProductBean.class.getName());
private Product product;
public ProductBean() {}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
Your bean is request scoped. So the bean instance lives as long as a single HTTP request-response cycle.
When the page with the form is requested for the first time, a new bean instance is created which receives a concrete product property as view parameter. After generating and sending the associated response, the bean instance is garbaged, because it's the end of the request.
When the form is submitted, effectively a new HTTP request is fired and thus a new bean instance is created with all properties set to default, including the product property. This way #{productBean.product} is null for the entire request. The rendered attribute of a parent component of the command link will evaluate false. The command link action is therefore never decoded. This matches point 5 of commandButton/commandLink/ajax action/listener method not invoked or input value not updated which you already found, but apparently didn't really understood.
The solution is to put the bean in the view scope. A view scoped bean lives as long as you're interacting (submitting/postbacking) with the same JSF view. Standard JSF offers #ViewScoped for this. As you're using CDI instead of JSF to manage beans, your best bet is the CDI #ConversationScoped. This is relatively clumsy (you've to start and end the scope yourself), so some CDI extension such as MyFaces CODI which offers a #ViewAccessScoped may be more useful.
See also:
How to choose the right bean scope?
I'm writing a composite component, you have a special tag named:
<composite:insertChildren />
Which inserts all the component's children there. Is there any way to know whether the component has children? Like a boolean value that could go on a "rendered" attribute.
The basic expression you're after is the following:
#{cc.childCount} or more elaborately:
#{component.getCompositeComponentParent(component).childCount}
E.g. the following composite component:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:cc="http://java.sun.com/jsf/composite"
>
<cc:interface/>
<cc:implementation>
<h:outputText value="Children: #{cc.childCount}" />
</cc:implementation>
</html>
used on the following Facelet:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:test="http://java.sun.com/jsf/composite/test"
>
<h:body>
<test:myCom>
<h:outputText value="first child" />
<h:outputText value="second child" />
</test:myCom>
</h:body>
</html>
will print Children: 2.
Thus #{cc.childCount != 0} will tell you whether a composite component has children or not.
I've encountered the same problem and managed to find children of a composite component within it's facet 'javax.faces.component.COMPOSITE_FACET_NAME'.
In Java it's like this:
// we are within some method of UIComponent
UIComponent childrenHolderFacet = getFacets().get("javax.faces.component.COMPOSITE_FACET_NAME");
Iterator<UIComponent> childrenIt = childrenHolderFacet.getChildren().iterator();
...
In JSF it's something like:
#{component.getFacets().get("javax.faces.component.COMPOSITE_FACET_NAME").children}
Hope it helps.
At least on Mojarra this does not work. A composite component's children get inserted just fine but accessing #{cc.parent} or #{cc.children} does not work on 2.0.2, and #{cc.childCount} always returns 0 on 2.0.4, regardless of the number of children.