f:ajax listener in composite component stopped working in WildFly 10 - jsf-2.2

We have in our JSF 2.2 project a composite component used like this (the code has been simplified for clarity):
<!-- the page with the composite component inside -->
<my:component methodListener="#{myBean.myMethod}" />
<!-- the composite component -->
<cc:interface>
<cc:attribute name="methodListener" required="true" method-signature="void listener(javax.faces.event.AjaxBehaviorEvent)" />
...
<cc:implementation>
<h:commandLink>
<f:ajax listener="#{cc.attrs.methodListener}" />
<f:attribute name="myAttribute" value="myValue" />
</h:commandLink>
// the called method
public void myMethod(AjaxBehaviorEvent event)
{
System.out.println("> " + event);
}
In Wildfly 9.0.2, when clicking on the commandLink inside the composite component, myMethod is called and the event is defined (allowing us to find back myAttribute value).
In Wildfly 10.1.0, with the exact same code, myMethod is called but the event parameter is always null.
Are we doing something wrong here?
A tested workaround could be to replace the methodListener CC attribute with a bean instance, something like the following code. Still, this would require a lot of replacements in our project, therefore we'd like to avoid this solution.
<!-- the workaround composite component -->
<cc:interface>
<cc:attribute name="methodBean" required="true" type="com.example.myBean" />
...
<cc:implementation>
<h:commandLink>
<f:ajax listener="#{cc.attrs.methodBean.myMethod}" />
<f:attribute name="myAttribute" value="myValue" />
</h:commandLink>

Well, I didn't see this answer from BalusC on another related question. I still don't understand why my code worked on WF9 and broke on WF10, but here's a simple solution, where we don't have to change anything but the component itself:
<!-- the CC xhtml -->
<h:commandLink>
<f:ajax listener="#{cc.methodListener}" />
<f:attribute name="myAttribute" value="myValue" />
</h:commandLink>
// the CC implementation
public void methodListener(AjaxBehaviorEvent event)
{
FacesContext context = getCurrentInstance();
MethodExpression ajaxEventListener = (MethodExpression) getAttributes().get("methodListener");
ajaxEventListener.invoke(context.getELContext(), new Object[] { event });
}

Related

Keep the parameters from managed Bean to a dialog open in PrimeFaces form using JSF

I have a problem training with forms in PrimeFaces. I will try to explain in the best way:
The functional idea is open a dialog in which the user can see certain inputs introduced previously.
The fact is that I need to use (save) some properties from bean which I'm managing in the view when the dialog is opened.
Example in code:
My xhtml says:
</h:form>
<!-- Code Here -->
<p:commandButton value="Preview" action="#{mockBean.showPreview}"/>
<!-- Code Here -->
<p:dialog id="myDialog"
widgetVar="myDialog"
modal="true"
appendToBody="true"
resizable="false">
<h:form>
<p:panelGrid columns="2">
<!-- ... Code Here -->
<h:outputText value="Name" />
<h:outputText value="#{mockBean.name}"
style="display:block;
width:225px"/>
<!-- ... Code Here -->
</p:panelGrid>
</h:form>
</p:dialog>
</h:form>
My Bean says: (MockBean.java)
<!-- ... Code Here -->
public void showPreview(){
RequestContext context = RequestContext.getCurrentInstance();
context.execute("myDialog.show()");
}
<!-- ... Code Here -->
The mockBean.name property will be empty in dialog when, previously, and later, in view, the bean has a correct value
I suspect that the solution will be easy but i'm newbie in this technologies and just'm introducing to this.
Sorry if the question is absurd in part or something like that.
Greetings.
You must update the Dialog, so it can read the saved values in your bean.
The Dialog is static, it is filled when you page is rendered, and it doesn't render again unless you tell him to do so.
You could use the actionListener of your commandButton to save the properties, combined with the onClick="PF('myDialog').show()" and update="myDialog"
Something like this:
<p:commandButton value="Save and Open Dialog" actionListener="#{mockBean.saveProperties()}" update="myDialog" onclick="PF('myDialog').show()"/>
Buuut, Since you are oppening the dialog from your bean, you could just try the code bellow:
public void showPreview(){
RequestContext context = RequestContext.getCurrentInstance();
context.execute("myDialog.show()");
context.update("myDialog");
}
If you just want to display some message from backing bean then there is no need to use another <h:form> tag inside a <h:form> tag. Your code should look something like this.
<h:form>
<!-- Code Here -->
<p:commandButton value="Preview" action="#{mockBean.showPreview}" update="myDialog" />
<!-- Code Here -->
<p:dialog id="myDialog"
widgetVar="myDialog"
modal="true"
appendToBody="true"
resizable="false">
<p:panelGrid columns="2">
<!-- Code Here -->
<h:outputText value="Name" />
<h:outputText value="#{mockBean.nombre}"
width:225px"/>
<!-- ... Code Here -->
</p:panelGrid>
</p:dialog>
</h:form>
And your java code should be as.
public void showPreview(){
RequestContext context = RequestContext.getCurrentInstance();
context.execute("PF('myDialog').show();");
}

Making h:panelGroup clickable, and send back repeated variable

I need to make a panelGrid (one of man that are populated from a list of objects) clickable, and then send the item associated with it to the backing bean.
My HTML so for:
<ui:repeat var="searchItem" value="#{bean.filteredSearchItems}" varStatus="searchResult">
<h:panelGrid>
<!-- I get some info here from the searchResult object -->
</h:panelGrid>
<f:ajax event="click" listener="{bean.clickFlight}" />
<f:param name="lfi" value="#{searchResult.index}" />
</ui:repeat>
I know that (in my backing bean) I need a function called clickSearchItem() that can handle the ajax call, so to test all of this, I did the following in my backing bean:
public void clickFlight()
{
HttpServletRequest req = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
String lfi = req.getParameter("lfi");
if (lfi == null)
log.info("LFI WAS RETURNED AS NULL :(");
log.info("HOPEFULLY AN INDEX OF SOME SORT!: " + lfi);
}
Nothing is getting logged - the click doesn't register. Has anyone else had this problem? Does anyone know how to solve it?
The <f:ajax> needs to be nested into a component implementing ClientBehaviorHolder. If you intend to use <h:panelGrid> (which generates a HTML <table>) for that, then you should be nesting the <f:ajax> in the component itself.
<h:panelGrid>
<f:ajax event="click" listener="{bean.clickFlight}" />
<!-- I get some info here from the searchResult object -->
</h:panelGrid>
The <f:param> is only recognized by renderer of <h:outputFormat> and <h:commandXxx>. Provided that you're targeting a Servlet 3.0 compatible container (Tomcat 7, Glassfish 3, JBoss 6/7, etc) which thus supports EL 2.2, then you can just pass it as method argument instead:
<h:panelGrid>
<f:ajax event="click" listener="{bean.clickFlight(searchResult.index)}" />
<!-- I get some info here from the searchResult object -->
</h:panelGrid>
you can even pass the whole object if preferred:
<h:panelGrid>
<f:ajax event="click" listener="{bean.clickFlight(searchResult)}" />
<!-- I get some info here from the searchResult object -->
</h:panelGrid>
An alternative, if you need <f:param> per se, would be to use <h:commandLink> instead.
<h:commandLink>
<f:ajax event="click" listener="{bean.clickFlight(searchResult)}" />
<f:param name="lfi" value="#{searchResult.index}" />
<h:panelGroup>
<!-- I get some info here from the searchResult object -->
</h:panelGroup>
</h:commandLink>

Ajax-update another component inside same composite component

When using ajax inside a composite component, the ajax feature isn't working. What's wrong with the code?
the composite component:
<composite:interface>
<composite:attribute name="filter" required="true" type="java.lang.String" />
<composite:attribute name="list" required="true" type="java.util.List" />
</composite:interface>
<composite:implementation>
<h:inputText value="#{cc.attrs.filter}">
<f:ajax event="keyup" render="#{cc.clientId}:table#{cc.clientId}" />
</h:inputText>
<h:dataTable id="table#{cc.clientId}" value="#{cc.attrs.list}" var="elem">
<h:column>
<h:outputText value="#{elem}" />
</h:column>
</h:dataTable>
</composite:implementation>
now the bean
#ManagedBean
#SessionScoped
public class Ajaxcc
{
private String filter;
private List<String> list;
public Ajaxcc()
{
list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
}
public List<String> getList()
{
List<String> filteredList = new ArrayList<String>();
for (String s : list)
{
if (filter == null || filter.trim().equals(""))
{
filteredList.add(s);
}
else if (s.contains(filter))
{
filteredList.add(s);
}
}
return filteredList;
}
public String getFilter()
{
return filter;
}
public void setFilter(String filter)
{
this.filter = filter;
}
}
now the view:
<h:form>
<myCustomComponent:ajaxcc list="#{ajaxcc.list}" filter="#{ajaxcc.filter}" />
</h:form>
I'm using myfaces 2.1.10, deploying on a tomcat 7.0.39 by maven.
Expected behaviour: the list on the web site should be reduced to one, two and four whenever I press the o - button
Failure: the list isn't reduced.
What could be the solution?
BTW, if I put the content of the composite component into the view, it works correctly:
<h:form>
<h:inputText value="#{ajaxcc.filter}">
<f:ajax event="keyup" render="table" />
</h:inputText>
<h:dataTable id="table" value="#{ajaxcc.list}" var="elem">
<h:column>
<h:outputText value="#{elem}" />
</h:column>
</h:dataTable>
</h:form>
In this case pressing o reduces the list to the expected values. Besides, I found out that the response of the composite component ajax call seems to be "empty" for the data table field values; the response of the direct ajax call contains the new list.
<h:inputText ...>
<f:ajax ...render="#{cc.clientId}:table#{cc.clientId}" />
</h:inputText>
<h:dataTable id="table#{cc.clientId}" ...>
This is unnecessarily clumsy. Both components are in the same naming container (the composite itself!), so the prefix is implicitly already done by the composite itself. The suffix is valid, but just unnecessary. The composite component's own ID already enforces the uniqueness in the context of its parent naming container component. Opening the page in browser, rightclick-view source and observing the IDs in generated HTML output should already have given insights about that.
Just keep it simple:
<h:inputText ...>
<f:ajax ...render="table" />
</h:inputText>
<h:dataTable id="table" ...>
See also:
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"
Is it possible to update non-JSF components (plain HTML) with JSF ajax?

JSF passing object to template

I have two jsf pages. layout.xhtml and page.xhtml. Layout looks like this:
<ui:composition>
<h:panelGroup id="menu" layout="block">
<h:outputText value="#{menuBean}" />
<h:form>
<ui:repeat var="menuItem" value="#{menuBean.menuItems}">
<button:menuItem label="#{msgs[menuItem.label]}" action="#{menuBean.selectItem(menuItem.label)}" update="#{update}" />
</ui:repeat>
</h:form>
</h:panelGroup>
<ui:repeat var="menuItem" value="#{menuBean.menuItems}">
<h:panelGroup layout="block" rendered="#{menuBean.selectedItemLabel eq menuItem.label}">
<ui:include src="#{menuItem.page}" />
</h:panelGroup>
</ui:repeat>
And page like this:
<h:panelGroup binding="#{page}" layout="block">
<ui:decorate template="../template.xhtml">
<ui:param name="menuBean" value="#{pageBean}" />
<ui:param name="update" value=":#{page.clientId}" />
</ui:decorate>
</h:panelGroup>
When I first time render page everything is ok (menuItems are rendered etc). After I click any button I get Target Unreachable, identifier 'menuBean' resolved to null.
Can anyone explain me what is happening why the menuBean isn't assigned again and if there exists another way to achieve this kind of thing (to have some generic layout page, pass some object there and generate page)? My beans are managed by Spring.
UPDATE:
I guess the problem is somehow connected to my composite button which looks like this:
<composite:interface name="menuItem">
<composite:attribute name="action" targets="button" />
<composite:attribute name="styleClass" />
<composite:attribute name="label" />
<composite:attribute name="update" />
<composite:attribute name="rendered" />
<composite:insertChildren />
</composite:interface>
<composite:implementation>
<h:commandButton id="button" value="#{cc.attrs.label}" style="width: 150px;" action="#{cc.attrs.action}"
rendered="#{empty cc.attrs.rendered ? true : cc.attrs.rendered}" styleClass="menu-item #{cc.attrs.styleClass}" type="button">
<f:ajax render="#{cc.attrs.update}" />
</h:commandButton>
</composite:implementation>
If I replace the tag by standard h:commandButton everything works perfectly. I am passing object into template and the template is passing the passed object into composite, but I am definitely missing something.
I think the problem is connected with the scope of the beans. As I understand your pageBean backing bean have request scope. Try to change scope on wider one (it should be view scope #ViewScoped). If you use CDI annotations instead of JSF then you can look at #ConversationalScope (or you can use MyFacesCODI library as Luiggi noted).
The problem I am facing to BalusC already described on his blogspot:
http://balusc.blogspot.cz/2011/09/communication-in-jsf-20.html#ViewScopedFailsInTagHandlers
Simply put at the moment of postback request (ajax refresh of button) bean passed into my composite button does not exist. As BalusC noted, solution can be creating of standard UIComponent instead of using composite shortcut.

Primefaces Calendar <f:ajax> and <p:ajax> issue

I am currently facing issue using with primefaces Calendar control
My requirement is to invoke a java method on change of the Calendar control value either from the input box or using the button and also pass a set of parameters(Java object and the field id) to the java method.
below is the code that I have implemented and it is not working
<p:calendar showOn="button" id="effectiveDt" navigator="true" pattern="MM/dd/yyyy"
styleClass="overrideable" value="#{coverage.startDate}">
<f:ajax event="change" render="hiddenStartDt" onevent="myEvent" listener="#{certInquiry.ovrrrideListener}"/>
<f:attribute name="covg" value="#{coverage}" />
<f:attribute name="field" value="StartDate" />
</p:calendar>
here myEvent is a java script method which will be called after the ajax is completed where i am checking the ajax status is success and performing some action.
method signature of the java method is.
*public void overrideListener(AjaxBehaviorEvent e) *
I tried with all kinds of methos signatures like
public overrideListener(ValueChangeEvent e)
public overrideListener(ActionEvent e)
public ovrrrideListener(DateSelectEvent event)
nothing was working for me
I have tried using also even this was not working
I also tried using valueChangeListener if the method gets called but it was not invoking the method
this is working for and fields using the
<h:selectBooleanCheckbox class="overrideable"
value="#{coverage.clIndicator}">
<f:ajax event="change" render="hidgenLiabClaimsMdInd" onevent="myEvent" listener="#{certInquiry.overrideListener}"/>
<f:attribute name="covg" value="#{coverage}" />
<f:attribute name="field" value="ClIndicator" />
</h:selectBooleanCheckbox>
<h:inputText id="genLiabEachOccAmt" value="#{coverage.eachOccAmt}"
class="amt_field overrideable">
<f:ajax event="change" render="hidgenLiabEachOccAmt" onevent="myEvent" listener="#{certInquiry.overrideListener}"/>
<f:attribute name="covg" value="#{coverage}" />
<f:attribute name="field" value="EachOccAmt" />
</h:inputText>
Please help me in solving this issue for the Calendar component.
Make the ajax event="dateSelect" and you should get the date value in your BB when you use ovrrrideListener(DateSelectEvent event).

Resources