JSF rendering embedded panel list ajax does not work - jsf

I have two panels, an outer one "outer_lst_panel" and an inner one "inner_lst_panel". The inner one is embedded in the outer one. The problem is that I cannot remove an item from the inner panel. The button "Remove inner item" does not work. If I look in the generated code (see below) it seems to be a problem with the generation of a real id ("mojarra.ab(this,event,'action',0,'inner_lst_panel')"). If I hard code
<h:commandButton
value="Remove inner item"
action="#{bean.do_delete_inner_item(mas, mav)}">
<f:ajax render="aform:j_idt13:0:inner_lst_panel" /> <!-- Hard coded!!! -->
</h:commandButton>
it works. What's wrong?
JSF 2.3.2 from https://maven.java.net/content/repositories/releases/org/glassfish/javax.faces/2.3.2/javax.faces-2.3.2.jar
Bean:
#Named
#ViewScoped
public class Bean
...
<h:form id="aform">
<h:panelGroup id="outer_lst_panel">
<ui:repeat
var="mas"
value="#{bean.mas_lst}">Outer item
<h:panelGroup id="inner_lst_panel">
<ui:repeat
var="mav"
value="#{mas.mav_lst}">Inner item
<h:commandButton
value="Remove inner item"
action="#{bean.do_delete_inner_item(mas, mav)}">
<f:ajax render="inner_lst_panel" /> <!-- Not working!!! -->
</h:commandButton>
</ui:repeat>
<h:commandButton
value="Add inner item"
action="#{bean.do_add_inner_item(mas)}">
<f:ajax render="inner_lst_panel" />
</h:commandButton>
</h:panelGroup>
<h:commandButton
value="Remove outer item"
action="#{bean.do_delete_outer_item(mas)}">
<f:ajax render="aform:outer_lst_panel" />
</h:commandButton>
</ui:repeat>
</h:panelGroup>
<h:commandButton
value="Add outer item"
action="#{bean.do_add_outer_item()}">
<f:ajax render="aform:outer_lst_panel" />
</h:commandButton>
</h:form>
Generated code:
<form
id="aform"
name="aform"
method="post"
action="test.xhtml"
enctype="application/x-www-form-urlencoded">
<input
name="aform"
value="aform"
type="hidden"> <span id="aform:outer_lst_panel"> Outer item <span
id="aform:j_idt13:0:inner_lst_panel"> Inner item <input
id="aform:j_idt13:0:j_idt35:0:j_idt31"
name="aform:j_idt13:0:j_idt35:0:j_idt31"
value="Remove inner item"
onclick="mojarra.ab(this,event,'action',0,'inner_lst_panel');return false"
type="submit"><input
id="aform:j_idt13:0:j_idt42"
name="aform:j_idt13:0:j_idt42"
value="Add inner item"
onclick="mojarra.ab(this,event,'action',0,'aform:j_idt13:0:inner_lst_panel');return false"
type="submit"></span><input
id="aform:j_idt13:0:j_idt43"
name="aform:j_idt13:0:j_idt43"
value="Remove outer item"
onclick="mojarra.ab(this,event,'action',0,'aform:outer_lst_panel');return false"
type="submit"></span><input
id="aform:j_idt44"
name="aform:j_idt44"
value="Add outer item"
onclick="mojarra.ab(this,event,'action',0,'aform:outer_lst_panel');return false"
type="submit"><input
name="javax.faces.ViewState"
id="j_id1:javax.faces.ViewState:0"
value="-2796149469970124814:8174359678892564468"
autocomplete="off"
type="hidden">
</form>
Update:
No, my question is not answered by How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"
It's yet unclear for me why the render attribute from the ajax tag doesn't get a real reference for inner_lst_panel with an continual counter. From my opinion it should be rendered to
onclick="mojarra.ab(this,event,'action',0,'aform:j_idt13:0:inner_lst_panel')
but it doesn't. It becomes
onclick="mojarra.ab(this,event,'action',0,'inner_lst_panel')
and that's wrong and doesn't work.
As a workaround (no, I cannot use render="#form" for other reasons) I can use the following:
<h:form id="aform">
<h:panelGroup id="outer_lst_panel">
<h:panelGroup id="inner_lst_panel" binding="#{foo}">
...
<f:ajax render="#{foo.clientId}" />
...

Related

globalOnly="true" in h:messages and bean validation

I use globalOnly="true" in h:messages to show messages as success or errors during bean methods......
<div class="row">
<div class="col">
<h:messages id="mensagens" warnClass="alert alert-danger" infoClass="alert alert-success" errorClass="alert alert-danger" globalOnly="true" />
</div>
</div>
and h:message for="" to validate field by field with bean validation
<h:inputText id="nome" styleClass="form-control" value="#{lojaMB.filial.nome}" />
<h:message for="nome" class="text-danger" />
It's works well......
But when I click on the "+" green button the modal opens
This modal use other managed bean and other form with ajax...
<h:commandButton value="Salvar" styleClass="btn btn-secondary" action="#{supermercadoMB.salvar}">
<f:ajax execute="#form" render=":mensagens :frmNovaFilial:supermercado #form" onevent="hideModal" />
</h:commandButton>
THE PROBLEM
when I click "Salvar" button the function "hideModal" executes, modal closes and bean validation send a error message by the blank field to parent page h:messages (no h:message ...) ....... but this message doesn't show........ Just shows when I remove globalOnly="true" .....but If I remove globalOnly="true" validations of field by field are show too on h:messages and h:message ......
how resolve this?
Create a parent element to your message component and after process the button update it
In the commandButton process only the nome inputText and the button itself
<h:panelGrid id="grid" columns="1">
<h:inputText id="nome" styleClass="form-control" value="#{lojaMB.filial.nome}" />
<h:message for="nome" class="text-danger" />
</h:panelGrid>
<h:commandButton value="Salvar" action="#{supermercadoMB.salvar}">
<f:ajax execute="#this :nome" render=":grid" onevent="hideModal" />
</h:commandButton>

Customize attribute of one panel id of Composite component .in JSF2,2 [duplicate]

I am writing a composite component that is intended to wrap an input element, and augment it with an 'optional field' designation and h:message element below it.
Here is the component (in input.xhtml file):
<composite:interface/>
<composite:implementation>
<div>
#{component.children[1].setStyleClass(component.children[1].valid ? '' : 'inputError')}
<composite:insertChildren/>
</div>
<h:panelGroup styleClass="optional"
rendered="#{!component.parent.children[1].required}"
layout="block" >
#{msgs['common.optional.field']}
</h:panelGroup>
<h:message id="msgId"
for="#{component.parent.children[1].id}" errorClass="error"/>
</composite:implementation>
Again, the intended usage of the component is to wrap an h:input* element which is expected to be the first and immediate child of it in a using page.
In a using page I would then write:
<h:form id="f1">
<h:panelGrid border="0" columns="2" style="width: 80%">
<f:facet name="header">
<col width="40%" />
<col width="60%" />
</f:facet>
<h:outputLabel styleClass="sectionBody" for="i1"
value="Input 1"></h:outputLabel>
<my:input id="ii1">
<h:inputText id="i1" size="20" maxlength="20" tabindex="0"
required="true" label="Input 1">
</h:inputText>
</my:input>
<h:outputLabel styleClass="sectionBody" for="i2"
value="Input 2"></h:outputLabel>
<my:input id="ii2">
<h:inputText id="i2" size="20" maxlength="20" tabindex="0"
label="Input 2">
</h:inputText>
</my:input>
</h:panelGrid>
<br />
<h:commandButton value="Submit" tabindex="1">
<f:ajax listener="#{terminalImportBean.addTerminal}"
execute="#form" render="#form"/>
</h:commandButton>
</h:form>
This would work just fine using normal posts.
However if I add add f:ajax validation for "Input 1" (id="i1") and try to re-render the composite (ii1) using the following:
...
<h:outputLabel styleClass="sectionBody" for="i1"
value="Input 1"></h:outputLabel>
<my:input id="ii1">
<h:inputText id="i1" size="20" maxlength="20" tabindex="0"
required="true" label="Input 1">
<f:ajax listener="#{myBean.checkInput1}"
event="blur"
render="ii1"/>
</h:inputText>
</my:input>
...
I gen an error in the browser during ajax response processing:
malformedXML: During update: f1:ii1 not found
I tried using f1:ii1, :f1:ii1, but to no avail. When I use msgId or :f1:ii1:msgId it works. Does anybody know why?
I am using Mojarra 2.1.3
Thanks
That's because the composite component does by itself not render anything to the HTML. Only its children are been rendered to HTML. There does not exist any HTML element with ID f1:i11 in the generated HTML output. Open the page in browser, rightclick and View Source. Look in there yourself. There's no such element with that ID. JavaScript/Ajax is encountering exactly the same problem. It can't find the element in order to update it.
To solve this, you'd need to wrap the entire composite body in a HTML <div> or <span> and assign it an ID of #{cc.clientId} which basically prints UIComponent#getClientId().
<cc:implementation>
<div id="#{cc.clientId}">
...
</div>
</cc:implementation>
Then you can reference it as below:
<my:input id="foo" />
...
<f:ajax ... render="foo" />
You can even reference a specific composite component child.
<f:ajax ... render="foo:inputId" />
See also:
Is it possible to update non-JSF components (plain HTML) with JSF ajax?
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"

p:outputPanel does not render page sections

I have two p:ouputPanels which have the rendered attribute. They should be rendered when the value of the underlying getter/setter changes. However, the underlying values change in the right order. The first panel disappears, However the second panel does not show up. Even NOT in the HTML code!
My two panels:
<p:outputPanel id="PanelParent">
<h:form>
<p:outputPanel id="PanelForm" rendered="#{PageService.isVisibilityForm()}">
<h:commandButton value="Button"
action="#{PageService.persist}"
styleClass="btn btn-primary opal_btn submit_form_new" />
</h:form>
</p:outputPanel>
<p:outputPanel id="PanelMessage" rendered="#{PageService.isVisibilityMessage()}">
//Message
</p:outputPanel>
</p:outputPanel>
I appreciate your answer!!!
-UPDATE-
In the code, I reference to the persist method and in this method I change the getter and setter of the visibility for each section.
-UPDATE1-
OK here is in fact my code:
<p:outputPanel id="parentPanel">
<p:outputPanel id="PanelForm"
rendered="#{PageService.isVisibilityForm()}">
<div>
<div>
<h:form class="homepage_invitee_form" action="" method="POST">
<p:messages id="messages" showDetail="false" autoUpdate="true"
closable="true" />
<h:inputText required="true"
value="#{PageService.instance.name}"
name="name" placeholder="Last Name"
styleClass="lastname_new" id="lastname_new"
type="text placeholder" />
<h:commandButton value="Press"
action="#{PageService.persist()}"/>
<f:ajax render="" />
</h:commandButton>
</h:form>
</div>
</div>
</p:outputPanel>
<p:outputPanel id="PanelThank"
rendered="#{PageService.isVisibilityThank()}">
<div>
Message
</div>
</p:outputPanel>
</p:outputPanel>
I really do not see where it fails? ;(
It's like Xtreme Biker said <f:ajax> render attribute will render elements that are already present in the DOM. In your case, <p:outputPanel id="PanelThank"> render attribute was evaluating to false so it was not in the DOM. Therefore, <f:ajax> could not render it. You need to point to something that is always visible. Change
<f:ajax render="" />
to
<f:ajax render=":PanelThank" />
but more importantly change
<p:outputPanel id="PanelThank" rendered="#{PageService.isVisibilityThank()}">
<div>
Message
</div>
</p:outputPanel>
to
<p:outputPanel id="PanelThank">
<p:outputPanel rendered="#{PageService.isVisibilityThank()}">
<div>
Message
</div>
</p:outputPanel>
</p:outputPanel>
Consider refactoring your code also. For instance, action and method in <h:form> is not needed.
rendered attribute defines if JSF will render your component in the DOM tree. Your problem is you cannot update a component which is not in the tree because you simply don't have it:
<p:outputPanel id="PanelForm" rendered="#{PageService.isVisibilityForm()}">
//my Form
<p:outputPanel>
<!--Fails if not PageService.isVisibilityForm(), because you don't have the component itself in the tree! So can't find it-->
<p:ajax update="PanelForm"/>
Your best is to wrap your content in another container which is always rendered and update it instead of the other one:
<h:panelGroup id="updatablePanel">
<p:outputPanel rendered="#{PageService.isVisibilityForm()}">
//my Form
<p:outputPanel>
</h:panelGroup>
<p:ajax update="updatablePanel"/>

Referring composite component ID in f:ajax render

I am writing a composite component that is intended to wrap an input element, and augment it with an 'optional field' designation and h:message element below it.
Here is the component (in input.xhtml file):
<composite:interface/>
<composite:implementation>
<div>
#{component.children[1].setStyleClass(component.children[1].valid ? '' : 'inputError')}
<composite:insertChildren/>
</div>
<h:panelGroup styleClass="optional"
rendered="#{!component.parent.children[1].required}"
layout="block" >
#{msgs['common.optional.field']}
</h:panelGroup>
<h:message id="msgId"
for="#{component.parent.children[1].id}" errorClass="error"/>
</composite:implementation>
Again, the intended usage of the component is to wrap an h:input* element which is expected to be the first and immediate child of it in a using page.
In a using page I would then write:
<h:form id="f1">
<h:panelGrid border="0" columns="2" style="width: 80%">
<f:facet name="header">
<col width="40%" />
<col width="60%" />
</f:facet>
<h:outputLabel styleClass="sectionBody" for="i1"
value="Input 1"></h:outputLabel>
<my:input id="ii1">
<h:inputText id="i1" size="20" maxlength="20" tabindex="0"
required="true" label="Input 1">
</h:inputText>
</my:input>
<h:outputLabel styleClass="sectionBody" for="i2"
value="Input 2"></h:outputLabel>
<my:input id="ii2">
<h:inputText id="i2" size="20" maxlength="20" tabindex="0"
label="Input 2">
</h:inputText>
</my:input>
</h:panelGrid>
<br />
<h:commandButton value="Submit" tabindex="1">
<f:ajax listener="#{terminalImportBean.addTerminal}"
execute="#form" render="#form"/>
</h:commandButton>
</h:form>
This would work just fine using normal posts.
However if I add add f:ajax validation for "Input 1" (id="i1") and try to re-render the composite (ii1) using the following:
...
<h:outputLabel styleClass="sectionBody" for="i1"
value="Input 1"></h:outputLabel>
<my:input id="ii1">
<h:inputText id="i1" size="20" maxlength="20" tabindex="0"
required="true" label="Input 1">
<f:ajax listener="#{myBean.checkInput1}"
event="blur"
render="ii1"/>
</h:inputText>
</my:input>
...
I gen an error in the browser during ajax response processing:
malformedXML: During update: f1:ii1 not found
I tried using f1:ii1, :f1:ii1, but to no avail. When I use msgId or :f1:ii1:msgId it works. Does anybody know why?
I am using Mojarra 2.1.3
Thanks
That's because the composite component does by itself not render anything to the HTML. Only its children are been rendered to HTML. There does not exist any HTML element with ID f1:i11 in the generated HTML output. Open the page in browser, rightclick and View Source. Look in there yourself. There's no such element with that ID. JavaScript/Ajax is encountering exactly the same problem. It can't find the element in order to update it.
To solve this, you'd need to wrap the entire composite body in a HTML <div> or <span> and assign it an ID of #{cc.clientId} which basically prints UIComponent#getClientId().
<cc:implementation>
<div id="#{cc.clientId}">
...
</div>
</cc:implementation>
Then you can reference it as below:
<my:input id="foo" />
...
<f:ajax ... render="foo" />
You can even reference a specific composite component child.
<f:ajax ... render="foo:inputId" />
See also:
Is it possible to update non-JSF components (plain HTML) with JSF ajax?
How to find out client ID of component for ajax update/render? Cannot find component with expression "foo" referenced from "bar"

CommandLink is not called anymore after click on commandButton

There are a list with 100 "Favs" on it. It only shows 10 when the page loads. When I click on "Show More Favs" it shows 10 more Favs.
What's weird about it, it's after I click on Show More Favs, clicking on Remove from list, it's not calling the method in the backBean.
My backBean is ViewScoped.
<ui:repeat value="#{bean.list}" var="fav">
<h:form>
<h:commandLink styleClass="someStyle" title="Remove from list" action="#{bean.remove(fav.id)}"/>
</h:form>
</ui:repeat>
<h:form>
<h:commandButton value="Show More Favs" action="#{bean.showMore}" >
<f:param name="moreFav" value="10" />
<f:ajax event="action" render="#all" />
</h:commandButton>
</h:form>
The culprit is the usage of multiple forms and the render="#all". If you re-render another form from inside an ajax form, the viewstate of the another form will get lost.
You need to change the code so that only the content of another form is re-rendered:
<h:form id="otherform">
<h:panelGroup id="list">
<ui:repeat value="#{bean.list}" var="fav">
<h:commandLink styleClass="someStyle" title="Remove from list" action="#{bean.remove(fav.id)}"/>
</ui:repeat>
</h:panelGroup>
</h:form>
<h:form>
<h:commandButton value="Show More Favs" action="#{bean.showMore}" >
<f:param name="moreFav" value="10" />
<f:ajax event="action" render=":otherform:list" />
</h:commandButton>
</h:form>

Resources