i have a composite component that contains a form with ajax. what i can't figure out is why it only works for the #form/#all render parameter.
<cc:interface>
<cc:attribute name="radioItem"/>
<cc:attribute name="radioItems" />
<cc:attribute name="compImage" />
<cc:attribute name="compSpecification"/>
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<h:form>
<div id="options">
<h:selectOneRadio value="#{cc.attrs.radioItem}" >
<f:selectItems value="#{cc.attrs.radioItems}" />
<f:ajax render=":details" />
</h:selectOneRadio>
</div>
<div id="details">
<h:graphicImage value="#{cc.attrs.compImage}"/>
<h:outputText value="#{cc.attrs.compSpecification}"/>
</div>
</h:form>
</cc:implementation>
What am i missing?
This problem is two-fold:
You're specifying an absolute client ID :details while the element to be re-rendered is inside the same UINamingContainer parent, so JSF won't be able to locate it in the component tree. You need to specify a relative client ID details.
You have specified the ID on a plain vanilla HTML element instead of a real JSF component, so JSF won't be able to locate it in the component tree. You need to use a real JSF component. You need to replace <div> by <h:panelGroup layout="block">.
So, fix it both:
<h:form>
<div id="options">
<h:selectOneRadio value="#{cc.attrs.radioItem}" >
<f:selectItems value="#{cc.attrs.radioItems}" />
<f:ajax render="details" />
</h:selectOneRadio>
</div>
<h:panelGroup id="details" layout="block">
<h:graphicImage value="#{cc.attrs.compImage}"/>
<h:outputText value="#{cc.attrs.compSpecification}"/>
</h:panelGroup>
</h:form>
That it works with #form and #all is just because JSF can locate the element to be re-rendered relative to the component on which the ajax action is been invoked, namely the parent UIForm and UIViewRoot component respectively.
Related
In my JSF 2.2 application on Glassfish 4.0, i need to allow update of single fields within a page.
For each field, i created a h:form containing two ui:fragments.
Each of there fragments is rendered based on the value of a boolean edit attribute named enteEdit in my backing bean FascicoloDettaglio.
FascicoloDettaglio is #ViewScoped.
The first fragment contains an h:outputText with the value from my bean, and a h:commandLink used to update the edit attribute and fire an AJAX request to re-render the form, which causes the first fragment to disappear and the second fragment to appear.
The second fragment contains the h:inputText used to edit the field, and the submit h:commandButton, which resets the edit attribute and then AJAX-calls the action method.
However, the second button never works; the action is not called and the button simply does nothing; while it should call the action and then re-render the entire form, turning the second fragment off and the first on.
I can't understand why it doesn't work.
This is the JSF code fragment:
<h:form>
<ui:fragment rendered="#{not fascicoloDettaglio.enteEdit}">
<h:outputText value="#{fascicoloDettaglio.fascicolo.ente}" />
<h:commandLink rendered="#{request.isUserInRole('affgen')}">
<h:graphicImage name="img/modify.png" width="24" height="24" title="Modifica" />
<f:ajax render="#form" />
<f:setPropertyActionListener target="#{fascicoloDettaglio.enteEdit}" value="true" />
<f:setPropertyActionListener target="#{fascicoloDettaglio.ente}" value="#{fascicoloDettaglio.fascicolo.ente}" />
</h:commandLink>
</ui:fragment>
<ui:fragment rendered="#{fascicoloDettaglio.enteEdit}">
<h:inputText value="#{fascicoloDettaglio.ente}" requiredMessage="Introdurre l'ente ricorrente." required="true" styleClass="largetext #{component.valid ? '' : 'inputvalidationerror'}">
<f:validateLength maximum="255" />
</h:inputText>
<h:commandButton action="#{fascicoloDettaglio.salvaEnte}" value="Salva">
<f:setPropertyActionListener target="#{fascicoloDettaglio.enteEdit}" value="false" />
<f:ajax execute="#form" render="#form" />
</h:commandButton>
<h:messages />
</ui:fragment>
</h:form>
Any help would be very appreciated.
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.
I have this code which is used to select how many rows to display in jsf table:
<!-- Set rows per page -->
<h:outputLabel for="rowsPerPage" value="Rows per page" />
<h:inputText id="rowsPerPage" value="#{AccountsController.rowsPerPage}" size="3" maxlength="3" />
<h:commandButton styleClass="bimage" value="Set" action="#{AccountsController.pageFirst}" >
<f:ajax render="#form" execute="#form"></f:ajax>
</h:commandButton>
<h:message for="rowsPerPage" errorStyle="color: red;" />
I want to edit the code this way: I want to replace the code with h:selectOneManu and option to insert custom value with AJAX support:
<h:selectOneMenu id="setrows" value="#{AccountsController.rowsPerPage}" converter="javax.faces.Integer" maxlength="3">
<f:selectItem itemValue="10" itemLabel="10" />
<f:selectItem itemValue="50" itemLabel="50" />
<f:selectItem itemValue="100" itemLabel="100" />
<f:selectItem itemValue="500" itemLabel="500" />
<f:selectItem itemValue="332" itemLabel="Custom" />
<f:ajax render="customrowperpage" />
</h:selectOneMenu>
<h:inputText id="customrowperpage" value="#{AccountsController.rowsPerPage}" rendered="#{AccountsController.rowsPerPage == '332'}" required="true" />
But for some reason the code is not working. Can you help me to find the problem. Also I want to update AJAX functionality when I select number from the h:selectOneMenu list AJAX call to update the form.
There are 2 problems.
First, as JavaScript runs in the client side, you can't ajax-update a JSF component which isn't rendered to the client side. There's then simply nothing in the HTML DOM tree which JavaScript can select, manipulate and replace. You should instead ajax-update a JSF component which is always rendered to the client side.
<h:panelGroup id="customrowperpage">
<h:inputText value="#{AccountsController.rowsPerPage}" required="true"
rendered="#{AccountsController.rowsPerPage == '332'}" />
</h:panelGroup>
See also Why do I need to nest a component with rendered="#{some}" in another component when I want to ajax-update it?
Second, an Integer never equals a String. You're in the rendered attribute testing Integer rowsPerPage against String '332'. Remove those singlequotes to make it a fullworthy number.
<h:panelGroup id="customrowperpage">
<h:inputText value="#{AccountsController.rowsPerPage}" required="true"
rendered="#{AccountsController.rowsPerPage == 332}" />
</h:panelGroup>
I'm having a bit of trouble getting AJAX calls to fire properly in a nested composite component with Mojarra 2.0.3 and PrimeFaces.
The child composite component looks something like this:
<cc:interface componentType="therapy">
<cc:attribute name="therapyType" type="java.lang.String" required="true"/>
<cc:attribute name="patientId" type="java.lang.String" required="true"/>
<cc:attribute name="showHistory" type="java.lang.Boolean" required="false" default="true"/>
<cc:attribute name="width" type="java.lang.String" required="false" default="350px"/>
<cc:attribute name="maxHistory" type="java.lang.String" required="false" default="3"/>
<cc:attribute name="collectDoctor" type="java.lang.Boolean" required="false" default="false"/>
<cc:attribute name="collectCareDate" type="java.lang.Boolean" required="false" default="false"/>
<cc:attribute name="important" type="java.lang.Boolean" requred="false" default="false"/>
</cc:interface>
<cc:implementation>
<script>
function #{cc.clientId}Toggle(){
$("##{cc.clientId}_newbutton").toggle();
$("##{cc.clientId}_savebuttons").toggle();
if(#{cc.attrs.collectDoctor}){
$("##{cc.clientId}_doctor").toggle();
}
if(#{cc.attrs.collectCareDate}){
$("##{cc.clientId}_care").toggle();
}
$("##{cc.clientId}_newTherapy").toggle(50);
}
function #{cc.clientId}rowHighlight(event){
if(event.status == 'begin'){
$("##{cc.clientId}_loader").toggle();
}
if(event.status == 'success'){
$("##{cc.clientId}\\:histTable tr:eq(1)").effect("highlight", {color:"#FED17A", easing:"easeInCubic"}, 2000);
}
}
$(function(){
if(#{cc.attrs.important}){
$("div[class~='ui-panel-titlebar'][id^='#{cc.clientId}']").css("background", "#FED17A");
}
});
</script>
<h:form prependId="false">
<p:panel styleClass="mcoPanel" style="width:#{cc.attrs.width};">
<f:facet name="header">
<h:panelGroup>
<span id="#{cc.clientId}_title">#{cc.myType.word}</span>
<span id="#{cc.clientId}_newbutton" class="mcoPanel-controls">
<span onclick="#{cc.clientId}Toggle();">
<h:graphicImage name="page_new.gif" library="images"/>
</span>
</span>
<span id="#{cc.clientId}_savebuttons" class="mcoPanel-controls" style="display:none;">
<span id="#{cc.clientId}_loader" style="display:none;">
<h:graphicImage name="ajax-loader.gif" library="images" height="16" width="16"/>
</span>
<h:commandLink action="#{cc.saveNewTherapy}">
<f:ajax execute="newOnTherapy newExemption newDoctor newCareDate" render="#form" onevent="#{cc.clientId}rowHighlight"/>
<h:graphicImage name="action_save.gif" library="images"/>
</h:commandLink>
<span onclick="#{cc.clientId}Toggle();">
<h:graphicImage name="page_cross.gif" library="images"/>
</span>
</span>
</h:panelGroup>
</f:facet>
<div id="#{cc.clientId}_newTherapy" class="mcoPanel-new" style="display:none;">
<h:outputLabel for="newOnTherapy" value="Satisfied:" style="position:relative; top: -10px;"/>
<p:selectOneMenu id="newOnTherapy" label="Satisfied" value="#{cc.newOnTherapyValue}" style="width: 60px;">
<f:selectItem itemLabel=""/>
<f:selectItems value="#{cc.yesNoList}"/>
</p:selectOneMenu>
<br/>
<h:outputLabel for="newExemption" value="Exemption:" style="position:relative; top: -10px;"/>
<p:selectOneMenu id="newExemption" value="#{cc.newExemption}" style="width: 150px;">
<f:selectItems value="#{cc.exemptions}"/>
</p:selectOneMenu>
<span id="#{cc.clientId}_doctor" style="display:none">
<br/>
<h:outputLabel for="newDoctor" value="Doctor:"/>
<p:inputText id="newDoctor" value="#{cc.newDoctor}"/>
</span>
<span id="#{cc.clientId}_care" style="display:none">
<br/>
<h:outputLabel for="newCareDate" value="Care Date:"/>
<p:calendar id="newCareDate" label="Care Date" value="#{cc.newCareDate}" showButtonPanel="true">
<f:converter converterId="dateOfBirthConverter"/>
</p:calendar>
</span>
</div>
<h:messages id="#{cc.clientId}_messages" errorClass="errorMessage"/>
<p:dataTable id="histTable" value="#{cc.history}" var="item" rendered="#{cc.attrs.showHistory}">
<!-- Table Output -->
</p:dataTable>
</p:panel>
</h:form>
And the parent composite component looks something like this:
<cc:interface>
<cc:attribute name="title" type="java.lang.String" required="true"/>
</cc:interface>
<cc:implementation>
<h:outputScript name="containerpanel.js" library="js/components" target="head"/>
<p:panel toggleable="true" styleClass="contentPanel" toggleSpeed="500" style="width:1100px;">
<f:facet name="header">
#{cc.attrs.title}
<div class="ui-panel-titlebar-icon ui-corner-all ui-state-default contentSaveAll">
Save All
</div>
<div class="ui-panel-titlebar-icon ui-corner-all ui-state-default contentExpandAll">
++
</div>
<div class="ui-panel-titlebar-icon ui-corner-all ui-state-default contentCollapseAll">
- -
</div>
</f:facet>
<cc:insertChildren>
<!-- Child components go here -->
</cc:insertChildren>
</p:panel>
</cc:implementation>
The implementation is supposed to allow for any number of child components inside the container component.
The child components send ajax requests and update their internal components perfectly if they are placed on a page outside of the Container component. In addition, if I use the same structure of the container, but not an actual composite component, everything works just fine.
As soon as I place the child components as children of the Container component, that's where things go wonky. No ajax calls in the child components fire properly (i.e. the save commandLink) Using firebug to help debug this, I've been able to determine the following:
The ajax post request appears to be being sent. The "begin" onevent is firing, and looking at the console in firebug shows a post being made to the server, with the proper component ids and values being sent back.
The action in the backing bean is not being called. I have a print statement as the first line and it's not printing.
The ajax request appears to be completing properly, the "success" onevent is firing.
Nothing is being updated on the page. The form does not refresh, nor does the p:messages object at the top of the page.
Obviously I can do this without the Container component, but I would prefer to be able to of course take advantage of code reuse. Am I missing something obvious here that I need to do to get the ajax to work properly?
I am having the exact same problem with MyFaces 2.1.6.
I can't make AJAX calls from nested Composite Components using ClientBehavior in the interface of the CC.
Like moneyT wrote the server is being notified of the events specified in the ajax tag, but when the lifecycle went to phase 5 - Invoke Application, the event was null and no listener was called. More and more it looks like a bug in the implementation of MyFaces.
EDIT: I have found another solution which is much better than hardcoding ajax in the composite component. I have refactored the outer composite component to be facelet. It loses some of the quirks of the composites but it gets the job done. Also it is reusable.
The only solution (more of a workaraund than solution) I found is to hardcode ajax in the composite component rather than to use ClientBehavior, like this:
<composite:implementation>
<h:panelGroup layout="block" id="listBox" class="list-box-scroll-pane" style="height:#{cc.attrs.visibleLines*27 + 5}px; width:#{cc.attrs.width}px">
<h:inputText id="submit-value" style="visibility: hidden; width:0px; height: 0px;" value="#{cc.attrs.property}">
<f:ajax event="change"
render="carModel"
listener="#{carDetailsMediator.changeCarMake}"
onevent="initComboboxes"/>
</h:inputText>
<ui:repeat value="#{cc.attrs.items}" var="element" varStatus="loop">
<input id="input-#{cc.attrs.customId}-#{loop.index}" type="hidden"
value="#{element.value}" />
<div id="div-#{cc.attrs.customId}-#{loop.index}" class="list-box-row">#{element.label}</div>
</ui:repeat>
</h:panelGroup>
</composite:implementation>
However this workaround is not very good, because the idea behind composite components is that you can reuse them with many different options. Hardcoding ajax in it kind of limits the options.
I believe your problem is this:
<f:ajax execute="newOnTherapy newExemption newDoctor newCareDate" ...
The execute attribute can refer to the special keywords (Eg. #this, #form, etc...) or according to the documentation:
If a literal is specified, it must be a space-delimited String of component identifiers and/or one of the keywords.
When you are inserting this component as a child to the parent component then the component Ids are going to by dynamically determined and can't be referred to absolutely.
I am building an invoice application. When the user selects the client, then the services associated with the client are re-rendered in a dataTable by an ajax event. But, the ajax event of the selectOneMenu in the dataTable does not fire.
This is the form which lets the user select a client:
<h:form>
<h:selectOneMenu id="clientSelect" value="#{invoiceBean.client}">
<f:ajax execute="clientSelect" listener="#{invoiceServiceListener.processClientValueChange}" render=":test :invoiceData"/>
<f:selectItems value="#{invoiceBean.clientOptions}"/>
</h:selectOneMenu>
</h:form>
This will fire an ajax event which fills the services array in the invoiceBean and re-renders the dataTable which displays the services in another selectOneMenu in the dataTable. This part works properly.
This is the datatable that is re-rendered after selecting the client by the above form:
<h:panelGroup id="test">
<h:dataTable id="invoiceData" value="#{attributes.priceAttributes}" var="loop">
<h:column>
<ui:param name="service" value="service#{loop}" />
<ui:param name="description" value="description#{loop}" />
<ui:param name="price" value="price#{loop}" />
<h:form id="invoiceSelectMenu">
<h:selectOneMenu id="selectMenu" value="#{invoiceBean[service]}">
<f:ajax event="change" execute="selectMenu" listener="#{invoiceServiceListener.procesServiceValueChange}" render="#form" />
<f:attribute name="row" value="#{loop}" />
<f:selectItems value="#{invoiceBean.services}" />
</h:selectOneMenu>
<h:inputText id="description" value="#{invoiceBean[description]}" />
<h:inputText id="price" value="#{invoiceBean[price]}" />
<h:outputText value="#{loop}" />
</h:form>
</h:column>
</h:dataTable>
</h:panelGroup>
The selectOneMenu is filled properly. However, the ajax event in this select one menu does not work after it has been re-rendered by the first form. But, if I manually fill the services array without submitting the first form, then this ajax event executes normally. By the way, this ajax event also sets a value to the description and price input fields, which should be re-rendered when the event happens. The invoiceBean is request scoped.
This is going to be tricky. Due to JSF spec issue 790, whenever you re-render some content containing another <h:form> using <f:ajax>, then its view state will get lost. To solve this, you'd basically need to reference the full client ID of another <h:form> inside <f:ajax> instead of some containing component. But you've basically multiple forms inside a <h:dataTable> which can't be referenced by an absolute client ID.
You'd need to rearrange the code to put the <h:form> outside the <h:dataTable> and change the <f:ajax> to execute only the necessary components.
<h:form id="invoiceDataForm">
<h:dataTable id="invoiceData" value="#{attributes.priceAttributes}" var="loop">
<h:column>
<ui:param name="service" value="service#{loop}" />
<ui:param name="description" value="description#{loop}" />
<ui:param name="price" value="price#{loop}" />
<h:selectOneMenu id="selectMenu" value="#{invoiceBean[service]}">
<f:ajax execute="selectMenu,description,price" listener="#{invoiceServiceListener.procesServiceValueChange}" render="#form" />
<f:attribute name="row" value="#{loop}" />
<f:selectItems value="#{invoiceBean.services}" />
</h:selectOneMenu>
<h:inputText id="description" value="#{invoiceBean[description]}" />
<h:inputText id="price" value="#{invoiceBean[price]}" />
<h:outputText value="#{loop}" />
</h:column>
</h:dataTable>
</h:panelGroup>
and then reference it form the first form as follows:
<f:ajax execute="clientSelect" listener="#{invoiceServiceListener.processClientValueChange}" render=":invoiceDataForm" />
Note that some component libraries such as PrimeFaces have already workarounded/solved this in the component library specific ajax engine. So if it might happen that you're already using PrimeFaces, you could also replace <f:ajax> of the first form by <p:ajax>.
Unrelated to the concrete problem, the <f:attribute name="row" value="#{loop}" /> may not do what you expect it does. It will set null because <f:xxx> tags run during view build time, not during view render time tag. The #{loop} is not available during view build time. But that's subject for a different question :)