how to awoid duplication ajax call in composite component - jsf

May be I make a fundamental mistake about jsf 2.2 specification, if so I would like to learn the correct solution of my problem. I use mojarra 2.2.9 immplementation, primemafaces 5.0.
I have a composite component with input, which always process on change event:
<cc:interface>
<cc:attribute name="value" type="java.lang.String" required="true"/>
<cc:clientBehavior name="change" event="change" targets="input"/>
</cc:interface>
<cc:implementation>
<span id="#{cc.clientId}">
<p:inputText value="#{cc.attrs.value}">
<p:ajax update="#this"/>
</p:inputText>
/* some ather component */
</span>
</cc:implementation>
And now in some cases I want to use additional listener and update some component outside cc, like clientBehavior solution:
<my:inp value="#{bean.value}">
<p:ajax event="change" listener="#{bean.listener}" update="someComponentOnPage" process="#this">
</my:inp>
Using that solution, I have 2 ajax call on 1 change event: first inside cc, second outside. Is there a solution to call ajax only once (if exist clientBehavior then not use ajax that inside)?
P.S.: sorry for my english...

Related

Primefaces autocomplete inside a custom composite component, behaving as without any suggested result

We use a lot of modals in a JSF project, so we made a simple custom component to embed the needed HTML code. Nothing complicated, just a wrapper without any logic:
<cc:interface>
<cc:attribute name="modalId" type="java.lang.String" required="true" />
<cc:attribute name="styleClass" type="java.lang.String" />
...
</cc:interface>
<cc:implementation>
<div id="#{cc.attrs.modalId}" class="modal fade">
<div class="modal-dialog modal-lg modal-dialog-centered #{cc.attrs.styleClass}">
...
<cc:insertChildren />
...
</div>
</div>
</div>
In one of this modal, we use a primefaces autocomplete component:
<my:modal modalId="myModal">
...
<p:autoComplete value="#{myBean.myValue}" multiple="true" completeMethod="#{myBean.myMethod}"
converter="#{myBean.myConverter}" forceSelection="true" unique="true" appendTo="#this" emptyMessage="no result"
var="example" itemLabel="#{example.name}" itemValue="#{example}" />
...
</my:modal>
In this state, the dropdown displaying the results behaves as if there were no result at all (the component emptyMessage is shown).
Of course, I checked the completeMethod, and it is returning results.
If I move the p:autocomplete outside the my:modal component, or if I replace the modal component by its pure HTML counterpart, everything works fine. So I guess our my:modal component is the cause of the fuss.
We're on a JSF 2 and Primefaces 11 project. A lot of other primefaces components (tree, selectBooleanCheckbox, fileUpload, dataTable, ...) are used without any issue in our modals.
What am I missing here?

JSF updating certain parts of a composite component

I want to develope a JSF composite component using PrimeFaces library.
Basically I want to update my composite component. I have read some SO questions about it (JSF updating a composite component or JSF Updating Composite Component (Primefaces)). But in this case I only want to update certain parts of the component.
Here is an example. My component should be a label/message/value-part of a <p:panelGrid /> to get rid of all the noise of the <p:column/>-tags.
<composite:interface>
<composite:attribute name="label" required="true" />
<composite:attribute name="value" required="true" />
</composite:interface>
<composite:implementation>
<p:column>
<!-- label -->
<p:outputLabel value="#{cc.attrs.label}" for="id_inputtext"/>
</p:column>
<p:column>
<!-- message -->
<p:message for="id_inputtext" />
</p:column>
<p:column>
<!-- inputtext -->
<p:inputText id="id_inputtext" value="#{cc.attrs.value}"/>
</p:column>
</composite:implementation>
To use this composite component I can simply put it in a panelgrid like so.
<p:panelGrid>
<p:row>
<mycomponent:columnSet id="c1" label="label" value="hello world"/>
<mycomponent:columnSet id="c2" label="label2" value="hello world2"/>
</p:row>
<p:row>
<mycomponent:columnSet id="c3" label="label3" value="hello world3"/>
<mycomponent:columnSet id="c4" label="label4" value="#{bean.someValue}"/>
</p:row>
</p:panelGrid>
In this case I can not surround the content of the component with an HTML container element like <div/> or <span/> like it is described in the above links. That would result in weird HTML because it would be within the generated table.
What I want to do in the example above is to update the <p:outputLabel/>, the <p:message/> and the <p:inputText/> from outside of the component. In a perfect world I want to update these three components independently from each other (but I guess that is even more complicated than updating all at once).
What I currently do to get this to work is kind of cheating. I create a <composite:attribute name="id" /> and give the three components fixed IDs based on a convention using the composite component id. That works but is pretty poor because using the composite component, one needs to know the inner implementation of the it.
Does anyone have an idea to solve this requirement in a nicer way?
<composite:interface>
<composite:attribute name="id" required="true" />
</composite:interface>
<composite:implementation>
<p:column>
<!-- label -->
<p:outputLabel id="#{cc.attrs.id}_label"/>
</p:column>
<p:column>
<!-- message -->
<p:message id="#{cc.attrs.id}_message" />
</p:column>
<p:column>
<!-- inputtext -->
<p:inputText id="#{cc.attrs.id}_value"/>
</p:column>
</composite:implementation>
EDIT
Thanks for the quick response in the comments.
As to the tag files: Indeed, I must admit that I avoided dealing with tag files because composite components are so much easier to handle, my bad.
Anyway, I just read some stuff, made a quick-and-dirty prototype, but came to the conclusion that (although it might be a good and proper way to use tag files in this label/message/input-situation) I have the same issue as with the composite component: To update the components inside the tag file I need to know the inner implementation of it (that is the same as described in my workaround).
I want to update the composite component/tag file from outside with a «single handle» and treat it as a black box.
If I could wish for a feature I want something to say «do the update» on the composite component/tag file. And within the composite component/tag file I can define which components should be updated if «do the update» is triggered. Something like three separate <div id="#{cc.clientId}"/> surrounding every component I want to update (which obviously is not possible like that).
Because I guess that this is nearly impossible, I would also be happy with a way to update a composite component/tag file as a whole, meaning to update every component within the black box.

Passing a converter to a composite component

After some experimenting and reading up, I found the same solution as How to reference component inside a composite component when using a converter
The problem however is that I have two IDs for the valueholder and according to the documentation I could use id1,id2 as targets but that doesn't work.
This is my composite component:
<cc:interface>
<cc:attribute name="id" required="true"/>
<cc:attribute name="value" required="true"/>
<cc:attribute name="editable" required="true"/>
<cc:editableValueHolder name="element" targets="input,output"/>
</cc:interface>
<cc:implementation>
<h:inputText value="#{cc.attrs.value}" id="input" rendered="#{cc.attrs.editable}"/>
<h:outputText value="#{cc.attrs.value}" id="output" rendered="#{not cc.attrs.editable}"/>
</cc:implementation>
And this is how I intend to use the CC:
<r:inputText editable="#{registrationBean.editable}" id="dateOfBirth"
value="#{registrationBean.dateOfBirth}">
<f:convertDateTime pattern="dd-MM-yyyy" type="date" for="element" />
</r:inputText>
I originally tried inserting the converter via insertChildren and via a facet but none worked.
target needs to be space separated not comma separated
If present, this must be a space (not tab) separated list of client
ids (relative to the top level component) of components within the
section. Space is used as the delimiter for
compatibility with the IDREFS and NMTOKENS data types from the XML
Schema.
JSF2.2 docs

'rich:component' not found: a4j:ajax with a composite component

I've built a simple composite component - a "richer" commandLink. I want it to support the clientBehavior, but when there is a <a4j:ajax> attached to the component I sometimes get the exception: ELException: Function 'rich:component' not found
It only comes when I use #{rich:component('...')} inside any attribute of <a4j:ajax>. For example oncomplete="#{rich:component('...')}.show()"
Edit: I am getting a Server error, not a JavaScript error.
The composite component (simplified):
<composite:interface>
<composite:attribute name="style" />
<composite:clientBehavior name="click" event="action" targets="commandLink" default="true"/>
</composite:interface>
<composite:implementation>
<h:commandLink id="commandLink" style="#{cc.attrs.style}">
<!-- my custom component content -->
</h:commandLink>
</composite:implementation>
The problematic use of this component looks like this:
<myLib:commandLink value="Custom CmdLink">
<a4j:ajax render="#form" execute="#form"
oncomplete="#{rich:component('myEditPopup')}"/>
</myLib:commandLink>
But the following code works like a charm:
<h:commandLink value="test">
<a4j:ajax render="#form" execute="#form"
oncomplete="#{rich:component('myEditPopup')}.show()"/>
</h:commandLink>
Edit: This one works too:
<a4j:ajax render="#form" execute="#form"
oncomplete="#{rich:component('myEditPopup')}.show()">
<myLib:commandLink value="label"/>
</a4j:ajax>
It seems to be a bug in Mojarra (we have been using the version 2.1.6), that EL lost the namespace "rich".
A working workaround was to declare the namespace in the a4j:ajax tag for each use:
<myLib:commandLink value="Show">
<a4j:ajax render="#form" execute="#form" xmlns:rich="http://richfaces.org/rich"
oncomplete="#{rich:component('myEditPopup')}.show()"/>
</myLib:commandLink>
After updating Mojarra to 2.1.26 the problem is gone and no needs for this workaround.
If you are missing { in your el, you will get the error rich:component not found.
For a4j:commandButton
<a4j:commandButton value="Show" oncomplete="#{rich:component('chargePointDetailPopup')}.show()"/>
For a4j:ajax in commandLink
<h:commandLink value="Show">
<a4j:ajax render="#form" execute="#form" oncomplete="#rich:component('chargePointDetailPopup')}.show()"/>
</h:commandLink>
Make sure to rich:popupPanel
<rich:popupPanel id="chargePointDetailPopup">
......
</rich:popupPanel>
For more Reference

How to stop recursive composite component from including itself recursively

Is it possible to have a JSF component that has ui:repeat and inside the repeat call the same component? It's because I'm building tree of question:
<cc:interface>
<cc:attribute name="questions" required="true" />
<cc:attribute name="renderQuestions" required="true" default="true" />
</cc:interface>
<cc:implementation>
<c:if test="#{cc.attrs.renderQuestions}">
<ui:repeat value="#{cc.attrs.questions}" var="q">
<p:panelGrid columns="2">
<h:outputLabel value="#{q.question}"></h:outputLabel>
<p:selectBooleanButton onLabel="#{messages['commons.yes']}"
offLabel="#{messages['commons.no']}" onIcon="ui-icon-check"
offIcon="ui-icon-close" value="#{q.ok}">
<p:ajax update="#all"></p:ajax>
</p:selectBooleanButton>
</p:panelGrid>
<cf:question renderQuestions="#{q.ok}" questions="#{q.children}" />
</ui:repeat>
</c:if>
</cc:implementation>
Currently I'm having stackoverflow?
Use a view build time tag to stop the recursion instead of a view render time tag. The component tree is built during view build time. The rendered attribute isn't evaluated during view build time, but instead during view render time. So your construct basically keeps including itself in an infinite loop. You should already know that StackOverflowError is caused by a recursion so deep that the memory stack can't handle it anymore (usually around ~1000 iterations).
Replace <h:panelGroup rendered> by a <c:if test> and it'll work as expected. The renderQuestions can by the way be simplified by omitting the check on children. The <ui:repeat> won't render anything anyway if there are no children.
If you happen to use view scoped beans in this construct and you're using Mojarra implementation, then make sure that you upgrade to at least 2.1.18, because binding a view build time tag attribute to a view scoped bean property would in older versions break the view scope.
See also:
JSTL in JSF2 Facelets... makes sense? - explains "view build time" versus "view render time"
Based on the answer of BalusC I created a solution like this.
<composite:interface>
[..]
<composite:attribute name="level" required="true" default="0"/>
</composite:interface>
<composite:implementation>
<c:if test="#{cc.attrs.level == 0}">
<a4j:repeat value="#{cc.attrs.list}" var="subList" rendered="#{cc.attrs.list != null}">
<xx:recursiveComponent list="#{subList}" id="subComponent" level="1"/>
</a4j:repeat>
</c:if>
</composite:implementation>
The level attribute is used at build time to avoid endless recursion and the renered attribute of the a4j:repeat (or ui:repeat in your case) will be evaluated at render time.

Resources