Parameterization of facelet custom tag - jsf

I have created facelet template:
left-right.xhtml
<ui:composition>
<ui:include name="left" />
<hr />
<ui:include name="right" />
</ui:composition>
After, if I use this template with ui:decorate it works fine:
index.xhtml
<ui:decorate template="left-right.xhtml">
<ui:define name="left">FOO</ui:define>
<ui:define name="right">BAR</ui:define>
</ui:decorate>
BUT, if I use this template as custom facelet tag it does not works.
custom-taglib.xml
<facelet-taglib>
<tag>
<tag-name>leftright</tag-name>
<source>left-right.xhtml</source>
</tag>
</facelet-taglib>
index.xhtml
<custom:leftright>
<ui:define name="left">FOO</ui:define>
<ui:define name="right">BAR</ui:define>
</custom:leftright>
The content inside ui:define tags is not included into template :(
So, question is how can I parameterise facelet template if it renders as facelet custom tag?

(note that you have a syntax error in your left-right.xhtml, you should be using <ui:insert> instead of <ui:include>, but I'll assume it to be just careless oversimplification)
A tag file cannot be treated as a template client. You need to approach it differently depending on the concrete functional requirement. If you're on JSF 2.x, then a composite component would be the closest which you need. You could define the parts as <f:facet> and render them by <cc:renderFacet> in the composite implementation.
E.g.
/resources/custom/leftRight.xhtml
<cc:interface>
<cc:facet name="left" required="true" />
<cc:facet name="right" required="true" />
</cc:interface>
<cc:implementation>
<cc:renderFacet name="left" />
<hr />
<cc:renderFacet name="right" />
</cc:implementation>
Usage:
<custom:leftRight>
<f:facet name="left">FOO</f:facet>
<f:facet name="right">FOO</f:facet>
</custom:leftRight>
But if you're still on JSF 1.x, you cannot create a composite component. You'd need to stick to <ui:decorate>.
See also:
When to use <ui:include>, tag files, composite components and/or custom components?

Related

JSF 2.3 Facet in Composite Component with wrong ID

I have the following simple code in a composite component (using Mojarra 2.3.9 / Primefaces 7):
<composite:implementation>
<h:form id="form">
<composite:insertChildren />
<ui:fragment rendered="#{!empty cc.facets.actions}">
<div class="actions">
<composite:renderFacet name="actions" />
</div>
</ui:fragment>
</div>
</h:form>
</composite:implementation>
And the following part is used in a page, trying to fill the composite form with life:
<cc:compForm id="mySpecialForm">
<f:facet name="actions">
<p:commandButton
id="myBtn"
value="Submit"
process="#form"
update="#form">
</p:commandButton>
</f:facet>
</cc:compForm>
The form and all the children are rendered correctly and working quite well. But the button in the renderFacet block has - in my opinion - a wrong client ID, because instead of:
mySpecialForm:form:myBtn
the button only gets the following clientId:
mySpecialForm:myBtn
This leads to an error rendering the page:
Cannot find component for expression "#form" referenced from
"mySpecialForm:myBtn".:
org.primefaces.expression.ComponentNotFoundException: Cannot find
component for expression "#form" referenced from
"mySpecialForm:myBtn".
Am i doing something wrong or is this a bug in JSF/Primefaces? I also tried to configure the componentType to an #FacesComponent extending from UIForm, but in this case no form will be rendered at all.
Update 1:
I tried to create a "minimal, reproducible example (reprex)" like mentioned by Kukeltje. All what is needed are those 2 Parts in a web application (both files under resources):
cc/compForm.xhtml:
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:composite="http://xmlns.jcp.org/jsf/composite">
<composite:interface name="compForm" displayName="A composite form">
<composite:facet name="actions" />
</composite:interface>
<composite:implementation>
<h:form id="form">
<composite:insertChildren />
<composite:renderFacet name="actions" />
</h:form>
</composite:implementation>
</html>
compFormTest.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"
xmlns:cc="http://xmlns.jcp.org/jsf/composite/cc">
<cc:compForm id="mySpecialForm">
<h:inputText id="inputParam" value="" />
<f:facet name="actions">
<h:commandButton id="myBtn" value="Test" />
</f:facet>
</cc:compForm>
</html>
All todo is call the .xhtml page: http:localhost/YOUR_APP/compFormTest.xhtml.
After using it (at least with Mojarra JSF implementation), the input field has the following correct client ID mySpecialForm:form:inputParam. But the command button retrieves another client ID outside the form: mySpecialForm:myBtn, what is a bug from my point of view, regarding the JSF VDL: " ... will be rendered at this point in the composite component VDL view.".
But as i downstriped the example files, it is clearly not a primefaces problem, because the wrong client ID is also included, if using the standard h:commandButton component.
Perhaps someone can use the mentioned 2 files above in a MyFaces environment to check if the behaviour differs or is the same?
Or has someone a workaround in mind? Using an additional #FacesComponent and moving the button from facet to the right spot under the form leads to the following "funny" duplicate ID error:
"Cannot add the same component twice: mySpecialForm:form:myBtn" (at least the client ID was what i expected in the first place)

Reuse some .xhtml pages on a JSF primefaces application

I developing a simple application using JSF and PrimeFaces and here's a problem that I'm facing:
These are managed beans that have a Person property:
ClientBean
EmployeeBean
I have the person.xhtml that shows the data from a person. I include the person.xhtml on a client.xhtml and employee.xhtml. I need to create two person.xhtml because I'm using different beans. What I want to do is something like that:
<c:set var="person" value="clientBean.person" />
<ui:include src="person.xhtml"/>
<c:set var="person" value="employeeBean.person" />
<ui:include src="person.xhtml"/>
And in my person.xhtml I can use #{person.name} , #{person.dateOfBirth}.
I searched and use <c:set/> in JSF is wrong.
Anyone can help?
Pass it as <ui:param>.
<ui:include src="person.xhtml">
<ui:param name="person" value="#{clientBean.person}" />
</ui:include>
<ui:include src="person.xhtml">
<ui:param name="person" value="#{employeeBean.person}" />
</ui:include>
Register person.xhtml if necessary as a tag file to make it look better, see also When to use <ui:include>, tag files, composite components and/or custom components?
<my:personForm value="#{clientBean.person}" />
<my:personForm value="#{employeeBean.person}" />
Beware of duplicate component ID errors. See also Avoiding duplicate ids when reusing facelets compositions in the same naming container.

Every include should refer to other instance

I include a part of the XHTML page to my Web Application. This part can be included multiple times. And that's the problem! Because every include refers to the same java object. That means that every element has the same value. But I want for each include a new Java object. So whats the best way to solve this problem?
Main page with includes:
<ui:composition template="./templates/template.xhtml">
<ui:define name="mainContent">
<ui:include src="include/includeAbleEditor.xhtml">
<ui:param name="includeParam" value="MyClass" />
</ui:include>
<ui:include src="include/includeAbleEditor.xhtml">
<ui:param name="includeParam" value="YourClass" />
</ui:include>
</ui:define>
includAbleEditor.xhtml
<h:commandButton value="#{editorVisibility.evb.value}"
action="#{editorVisibility.evb.toggle}" />
<h:inputTextarea rendered="#{editorVisibility.evb.enabled}" />
This <h:inputTextarea> is an example for my problem. Every included inputTextarea has a toggle button. By clicking the button the textarea should be shown or hidden. But because of the same reference of the boolean value all <h:inputTextarea> will always have the same rendered value.
Do you have any suggestions?
Thanks a lot!
You'll have to hold as many instances of editorVisibility.evb as you have editors. You could for example create a List<TypeOfEvb> evbList in your EditorVisibility bean, and pass only one element to the <ui:include> as a <ui:param>:
Main page
<ui:include src="include/includeAbleEditor.xhtml">
<ui:param name="includeParam" value="MyClass" />
<ui:param name="evb" value="#{editorVisibility.evbList[0]}" />
</ui:include>
includAbleEditor.xhtml
<h:commandButton value="#{evb.value}"
action="#{evb.toggle}" />
<h:inputTextarea rendered="#{evb.enabled}" />
You could also create a composite component.
See also:
http://www.mkyong.com/jsf2/composite-components-in-jsf-2-0/

Spring web-flow and JSF templating

I want to create pages which consist of "header" and "content". I create common.xhtml
<ui:insert name="header">
<ui:include src="commonHeader.xhtml" />
</ui:insert>
<ui:insert name="content">
<ui:include src="commonContent.xhtml" />
</ui:insert>
Then I create two pages (create_user_page.xhtml and search_page.xhtml) which must have the same "header" but different "content"
<ui:composition template="common.xhtml">
<ui:define name="content">
<h1>Content for creating...</h1>
<ui:include src="create.xhtml"/>
</ui:define>
</ui:composition>
and
<ui:composition template="common.xhtml">
<ui:define name="content">
<h1>Content searching...</h1>
<ui:include src="search.xhtml"/>
</ui:define>
</ui:composition>
In my web_flow.xml I have
<view-state id="start_page" view="administrator/main_page.xhtml">
<transition on="create_user" to="create_user_page" />
<transition on="find_user" to="search_page" />
<transition on="back" to="back" />
</view-state>
<view-state id="create_user_page" view="administrator/create_user_page.xhtml">
<transition on="create_user" to="create_user_page" />
<transition on="find_user" to="search_page" />
</view-state>
<view-state id="search_page" view="administrator/search_page.xhtml">
<transition on="create_user" to="create_user_page" />
<transition on="find_user" to="search_page" />
</view-state>
At the main_page.xhtml I have two actions "create_user" and "find_user" (in "header") which lead to pages
create_user_page.xhtml and search_page.xhtml. They have similar "header" and differ in "content". All this works good but I have some questions.
1) It seems that "header" re-rendered every time I get in create_user_page.xhtml or search_page.xhtml, or I am wrong? Is it possible the the "header" will stay without re-rendering and changes will be made only for "content".
2)In my web flow xml I have to duplicate code
<transition on="create_user" to="create_user_page" />
<transition on="find_user" to="search_page" />
for "create_user_page" and "search_page". Is it possible some how to rewrite it keeping in mind that these actions take place in "header" which is the same for these two pages.
JSF templating will always re-render your header/footer. Personally I dont think its a problem unless you have something really performance-intensive in there. You can avoid re-rendering by:
Using HTML frames http://www.w3schools.com/tags/tag_frameset.asp
Partial rendering - either in your SWF flows (see tag in http://static.springsource.org/spring-webflow/docs/2.0.x/reference/htmlsingle/spring-webflow-reference.html#view-transitions), or something like PrimeFaces partial rendering
You will probably have to redesign your XHTML and flows if you use either approach - effectively your partial rendering will replace flow definition. Something like:
<h:commandLink value="go to search page" actionListener="#{myBean.goToSearchPage}" update="content"></h:commandLink>
<h:commandLink value="go to another page" actionListener="#{myBean.goToAnotherPage}" update="content"></h:commandLink>
<h:panelGroup id="content">
<h:panelGroup id="search-page" rendered="#{myBean.isThisSearchPage}">
<!-- search page contents here -->
</h:panelGroup>
<h:panelGroup id="another-page" rendered="#{myBean.isThisAnotherPage}">
<!-- another page contents here -->
</h:panelGroup>
</h:panelGroup>
The above approach is definitely much less maintainable and not-recommended. Use standard SWF instead and rerender your header/footer whenever view changes. You can still use partial rendering within SWF view to respond to user's input without rerendering the whole page.
Regarding the second question: you can use global transitions and flow inheritance. See How to import globaltransitions.xml in myflow.xml?

What is the real conceptual difference between ui:decorate and ui:include?

It occurs ago me that ui:decorate is functionally the same as ui:include except that you can also pass ui:param and ui:define to the included file.
Am I crazy?
EDIT : Although in fact you can pass ui:param to a ui:include file too, it turns out I am already doing it. Maybe you can pass a ui:define as well, I will check and edit here.
The main difference between <ui:include> and <ui:decorate> is that the <ui:decorate> is intended to allow insertion of user-defined template components, while the <ui:include> is intended to include an existing and already-predefined template.
This indeed means that the <ui:decorate> supports <ui:define> for user-defined template components in its body and can insert it at the <ui:insert> place inside the template.
Here's a -somewhat clumsy- example to show where it can be used:
/WEB-INF/templates/field.xhtml
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
>
<h:outputLabel for="#{id}" value="#{label}" />
<ui:insert name="input" />
<h:message id="#{id}_message" for="#{id}" />
</ui:composition>
/page.xhtml
<h:panelGrid columns="3">
<ui:decorate template="/WEB-INF/templates/field.xhtml">
<ui:param name="label" value="Foo" />
<ui:param name="id" value="foo" />
<ui:define name="input">
<h:inputText id="foo" value="#{bean.foo}" required="true" />
</ui:define>
</ui:decorate>
<ui:decorate template="/WEB-INF/templates/field.xhtml">
<ui:param name="label" value="Bar" />
<ui:param name="id" value="bar" />
<ui:define name="input">
<h:selectBooleanCheckbox id="bar" value="#{bean.bar}" required="true" />
</ui:define>
</ui:decorate>
...
</h:panelGrid>
Note that it renders the components nicely in each cell of the panel grid. Again, this particular example is pretty clumsy, I'd just have used a tag file instead. Only if it was a larger section, e.g. a whole form whose e.g. its header or footer should be customizable, then an <ui:decorate> would have been appropriate.
Another major advantage of <ui:decorate> is that it allows you to use a composite component with a template. See also Is it possible to use template with composite component in JSF 2?

Resources