Passing action in <rich:modalPanel> - jsf

There is some way to pass action in <rich:modalPanel>. I want to write simple popup with "Yes" and "No" button, and I want to reuse this popup in different pages, that's why I need that different action was invoked when "Yes" button pressed. There is a way to pass some value in <rich:modalPanel> with <a4j:actionparam>:
<a4j:commandButton value="See details…" id="det"
reRender="popup_dataIdField, …">
<rich:componentControl for="popup"
operation="show" event="onclick" />
<a4j:actionparam name="message" assignTo="#{popupBean.message}"
value="#{myBean.message}" />
</a4j:commandButton>
But is some way to pass action ?

As you're using Facelets then look at Rick Hightower's article and the section "Passing Actions". It's the same basic method to what you've used, but also passing in the method on the bean.
Eg:
<ui:include src="./WEB-INF/popup/popup.xhtml">
<ui:param name="yesAction" value="save"/>
<ui:param name="cancelAction" value="cancel"/>
<ui:param name="backingBean" value="#{thisPageBean}"/>
</ui:include>
And then in the popup:
<a4j:commandButton id="yesButton" value="#{msgs.yesButton}"
action="#{backingBean[yesAction]}"/>
Note that you use the #.

I find the answer by myself :). Maybe it will be helpful for somebody.
I've done this using Facelets. When I'm include popup to my page I pass parametr to it:
<ui:include src="./WEB-INF/popup/popup.xhtml">
<ui:param name="yesAction" value="${thisPageBean}" />
</ui:include>
Where thisPageBean - is bean from what popup invoked.
Then in my popup I wrote:
<a4j:commandButton id="yesButton" value="#{msgs.yesButton}"
action="#{yesAction.someAtion}"
</a4j:commandButton>
And with this I invoke thisPageBean.someAtion. All magic is ${thisPageBean}, it's is necessary to us $ but no #.

Yes I dont see any problems with doing this. You can use my code if you want:
<rich:modalPanel id="popup" width="261" height="386" autosized="true"left="180" top="200" keepVisualState="true">
<h:panelGrid id="panelGrid">
<h:outputText value="#{PopupBean.output}" id="popupMessage"/>
<a4j:commandLink action="#">
<h:outputText value="Close" />
<rich:componentControl for="popup" operation="hide" event="onclick"/>
</a4j:commandLink>
</h:panelGrid>
</rich:modalPanel>
<h:panelGrid columns="2">
<a4j:commandLink action="#" reRender="panelGrid">
<h:outputText value="Yes" />
<rich:componentControl for="popup" operation="show" event="onclick"/>
<a4j:actionparam name="message" assignTo="#{PopupBean.output}" value="#{TestBean.input1}"/>
</a4j:commandLink>
<a4j:commandLink action="#" reRender="panelGrid">
<h:outputText value="No" />
<rich:componentControl for="popup" operation="show" event="onclick"/>
<a4j:actionparam name="message2" assignTo="#{PopupBean.output}" value="#{TestBean.input2}"/>
</a4j:commandLink>
</h:panelGrid>
Basicly the output in the modal panel will contain the values in the TestBean
Edit (the misunderstanding):
I believe you will have to define your modal panel like this:
<rich:modalPanel id="popup" width="261" height="386" autosized="true"left="180" top="200" keepVisualState="true"
binding="#{PopupBean.popupPanel}">
</rich:modalPanel>
And in your managed bean you will have to addChildren to your modal panel dynamically with java like this:
public String action_PoppingThePanel() {
HtmlCommandButton button = new HtmlCommandButton();
button.setValue("Yes");
String action = "#{TestBean.action_yes}";
MethodExpression methodExpression =
FacesContext.getCurrentInstance().getApplication().getExpressionFactory().
createMethodExpression(FacesContext.getCurrentInstance().getELContext(), action, null,
new Class<?>[0]);
button.setActionExpression(methodExpression);
getPopupPanel().getChildren().add(button);
button = new HtmlCommandButton();
button.setValue("No");
String action = "#{TestBean.action_no}";
methodExpression =
FacesContext.getCurrentInstance().getApplication().getExpressionFactory().
createMethodExpression(FacesContext.getCurrentInstance().getELContext(), action, null,
new Class<?>[0]);
button.setActionExpression(methodExpression);
getPopupPanel().getChildren().add(button);
getPopupPanel().setRendered(true);
getPopupPanel().setShowWhenRendered(true);
return null;
}

Related

Button in dialog inside p:dialog is not calling controller method

I've got a problem as described in the title.
Small description of the problem is as following:
I have button which is used to open dialog. Then, inside that dialog, there is button which opens another dialog on top of the first one. After clicking second button I want method from controller to be called but nothing happens. Value in h:outputText is read properly, so I guess it is not a problem with connection controller->view.
I'm using:
Spring web 3.1.2.RELEASE
JSF 2.2.10
Primefaces 5.1
Code:
beans.xml
<bean id="testController" class="test.TestController" />
TestController.java
public class TestController implements Serializable
{
private static final long serialVersionUID = 7028608421091861830L;
private String test;
public TestController()
{
test = "abc";
}
public void testMethod()
{
test = "cba";
}
public String getTest()
{
return test;
}
}
test.xhtml
<h:panelGrid columns="1" cellpadding="5">
<p:commandButton value="Basic" type="button" onclick="PF('dlg1').show();" />
</h:panelGrid>
<p:dialog widgetVar="dlg1">
<h:outputText value="Resistance to PrimeFaces is futile!" />
<h:panelGrid columns="1" cellpadding="5">
<p:commandButton value="Basic" type="button" onclick="PF('dlg2').show();" />
</h:panelGrid>
<p:dialog widgetVar="dlg2">
<h:outputText value="#{testController.test}" />
<p:commandButton value="Call method" type="button" actionListener="#{testController.testMethod}" />
</p:dialog>
</p:dialog>
What I tried:
adding appendToBody="true" to each p:dialog
changing from p:commandButton to p:button
changing from actionListener to action
but nothing helps.
I would be grateful for any help or advice of what can be the reason of not calling given method.
There are 3 problems.
You're nesting <p:dialog> components. This doesn't make sense. Separate them.
A <p:dialog> must have its own <h:form>, particularly when you explicitly use appendToBody="true" or appendTo="#(body)", otherwise nothing can be submitted because JavaScript would relocate the dialog out of its position in the HTML DOM tree to the end of body, causing it to not be sitting in a form anymore.
A <p:commandButton type="button"> acts as a "click" button, not as a submit button. Remove that attribute from submit buttons.
All in all, this is how it should look like:
<h:form>
<h:panelGrid columns="1" cellpadding="5">
<p:commandButton value="Basic" type="button" onclick="PF('dlg1').show();" />
</h:panelGrid>
</h:form>
<p:dialog widgetVar="dlg1">
<h:form>
<h:outputText value="Resistance to PrimeFaces is futile!" />
<h:panelGrid columns="1" cellpadding="5">
<p:commandButton value="Basic" type="button" onclick="PF('dlg2').show();" />
</h:panelGrid>
</h:form>
</p:dialog>
<p:dialog widgetVar="dlg2">
<h:form>
<h:outputText value="#{testController.test}" />
<p:commandButton value="Call method" actionListener="#{testController.testMethod}" />
</h:form>
</p:dialog>
OK. I guess I found a way to fix this problem.
It seems that the problem was:
type="button"
I deleted it from the list of attributes of each button and now it works even without h:form. Thanks for help.

how do I pass outputtext to other form

as simple as this. But the outputtext is not passed to another function or form. inputtext of course works but looks ugly.
What should I subsititute outputtext with?
<h:form>
<h:outputtext value="xx" />
<h:commandButton action="#{serviceTest.function() }" value="test"
</h:commandButton>
</h:form>
Real world example
<p:column id="average" sortBy="#{resultClub.stringAverage}">
<f:facet name="header">Snitt</f:facet>
<h:outputText id="testing" value="#{resultClub.stringAverage}" />
<h:inputHidden id="hiddenAvg" value="#{resultClub.stringAverage}" />
</p:column>
javax.faces.component.UpdateModelException: javax.el.PropertyNotFoundException: /hcp/showAverages.xhtml #96,74 value="#{resultClub.stringAverage}": Property 'stringAverage' not writable on type com.jk.hcp.ResultClub
javax.faces.component.UIInput.updateModel(UIInput.java:867)
javax.faces.component.UIInput.processUpdates(UIInput.java:749)
org.primefaces.component.api.UIData.process(UIData.java:328)
After you described the problem to me, I figure it out for you...
You can do this by using javascript
Outputtext:
<h:outputText id="text1" value="xx" />
Button:
<h:commandButton value="click me"
action="#{serviceTest.function()}" value="test"
onclick="submitFieldValue()" >
</h:commandButton>
XHTML:
This will set the myfield instance variable in your controller. It basically will generate a java script function at compile time which will be able to call the controller.
<a4j:jsFunction name="setTheValue">
<a4j:actionparam name="param" assignTo="#{serviceTest.myfield}"/>
</a4j:jsFunction>
JavaScript:
This will call setTheValue with the outputtext value
function submitFieldValue(){
var x = document.getElementById("text1").value;
setTheValue(x);
}
If you want to have the "xx" value submitted after clicking on the commandButton, you can either use hidden field for it
<h:inputHidden value="some text" />
or you can use
<h:inputText readonly="true" />
which will render the text in an input text that user cannot change (it may look like the outputText), but its value will be submitted after clicking the button.
What I understand is that you want to view some data in the outputtext after the button is pressed, right?
Please if I got it wrong, tell me to delete the answer!
Is this case you need to make the button renders the outputfield upon clicked:
Outputtext:
<h:outputText id="text1" value="#{serviceTest.text1Value}" />
Button:
<a4j:commandButton value="click me"
action="#{serviceTest.function() }" value="test"
render="text1" >
</a4j:commandButton>
As you can see I used a4j:commandButton which will enable me to render any element on the page by id upon click.

Submit popup panel content, rich:popupPanel

I am trying to submit values in a pop-up panel inside another panel that has a submit/action event. But before opening pop-up panel I need to invoke a function on my managed bean that create a new entity object. The outer panel has the only h:form, since you can't nest them. I have wrapped the pop-up panel in a a4j:region to submit only this part when the use submits the values inside the pop-up panel. This works, but not the execution of the preparing function that need to be invoked when the pop-up panel executes. I have tried a4j:commandLink but that component don't work together with the rich:popupPanel (strange since both of them are Richfaces components?!). So I have to relay on the h:commandLink and use ajax.
How can I invoke a function on my managed bean when the link to open/render the pop-up panel fires?
(What is the correct pattern for this?)
PS. The initial question has changed, but not the problem concerning submitting values in a pop-up panel.
Part of the xhtml file:
<h.form>
...
<a4j:region>
<rich:popupPanel id="popup_sys_user_req" modal="false" autosized="true" resizeable="false">
<f:facet name="header">
<h:outputText value="Request New Sector/Category" />
</f:facet>
<f:facet name="controls">
<h:outputLink value="#"
onclick="#{rich:component('popup_sys_user_req')}.hide(); return false;">
X
</h:outputLink>
</f:facet>
<h:panelGrid columns="2">
<h:outputLabel value="Request New:" />
<h:selectOneMenu id="sys_req_type" value="#{userController.selectedSysUserRequest.sysrequesttype}" required="true" >
<f:selectItems value="#{userController.getSysRequestTypeItems('SECTOR_CATEGORY')}">
</f:selectItems>
</h:selectOneMenu>
<h:outputLabel value="Description:" />
<h:inputTextarea id="user_req_desc" value="#{userController.selectedSysUserRequest.description(desc)}" required="true" requiredMessage="Decription is missing" />
</h:panelGrid>
<a4j:commandButton action="#{userController.CreateSysUserRequest()}" value="Send Request" execute="sys_user_req_form" oncomplete="#{rich:component('popup_sys_user_req')}.hide(); return false;"/>
</rich:popupPanel>
</a4j:region>
</h:form>
The commandLink (re-edit)
<h:commandLink actionListener="#{userController.prepareCreateSysRequest}" value="Request New Sector/Category">
<f:ajax execute="popup_sys_user_req #this" render="popup_sys_user_req">
<rich:componentControl id="popup_ctr" event="click" target="popup_sys_user_req" operation="show"/>
</f:ajax>
</h:commandLink>
----------------------------
//Managed Bean:
public void prepareCreateSysRequest(ActionEvent event ) {
selectedSysUserRequest = new Sysuserrequest();
JsfUtil.log("Prepare Create System User Request");
}
This post continues the dicussion about the pop-up panel.
Greetings Chris.
If I understand correctly you want to submit all form elements inside popupPanel but not outside the panel when you invoke someAction1? I can think of two ways to do this:
1. a4jcommandButton has a limitToList attribute, you can list which components you want to be updated on the server
2. create your popupPanel outside of the first form and then use its own form:
<h:form>
...
<a4j:commandButton action="someAction2"...
</h:form>
<rich:popupPanel>
<h:form>
...
<a4j:commandButton action="someAction1"...
</h:form>
</rich:popupPanel>
Update
If you are using RichFaces 4 you can replace the limitToList attribute with limitRender
The problem is that the popup isn't a child of the form in jsf, you only need to use the domElementAttachment attribute to change that. So your code would look like this:
<h.form>
...
<a4j:region>
<rich:popupPanel id="popup_sys_user_req" modal="false" autosized="true" resizeable="false" domElementAttachment="form">
<f:facet name="header">
...

multiple h:forms in ui:repeat

I'm trying to have multiple h:forms on a page which are rendered with a ui:repeat component. If I have only one generated h:form, everything works perfect. If I have multiple h:forms, only the latest h:form will work! (If I try to submit another form then the last, only null is passed trough my service!)
<ui:repeat var="element" value="#{myService.elementList}">
<rich:simpleTogglePanel switchType="client"
label="#{element.codeElement} - #{element.description}"
opened="#{myService.isUiSelected(element)}"
onexpand="expand#{element.codeElement}()"
oncollapse="collapse#{element.codeElement}()">
<h:form id="newKindForm">
<a:commandLink ajaxSingle="true" id="idLink" onclick="#{rich:component('newTargetPanel')}.show()">
<a:commandButton id="myButton"
value="new Form"
action="#{myService.newForm(element)}"/>
</a:commandLink>
</h:form>
<rich:modalPanel id="newTargetPanel" autosized="true" width="450">
<f:facet name="header">
<h:outputText value="My Pannel Title" />
</f:facet>
<f:facet name="controls">
<h:panelGroup>
<h:graphicImage value="/img/close.png"
id="hidelink"
styleClass="hidelink" />
<rich:componentControl for="newTargetPanel"
attachTo="hidelink"
operation="hide"
event="onclick" />
</h:panelGroup>
</f:facet>
<!-- THIS IS THE PROBLEMATIC FORM -->
<h:form>
<a:include viewId="../myRepeatableForm.xhtml" />
</h:form>
</rich:modalPanel>
</ui:repeat>
Some suggestion?
Thank You
you can contain everything in a single form, and use execute attribute to submit only the elements you want. like this:
<h:form id="newKindForm" prependId="false">
<ui:repeat id="newKindRepeat" var="element"
value="#{myService.elementList}" varStatus="varStatus">
<h:panelGroup id="newKindPanel">
<a:commandButton id="myButton"
execute="newKindRepeat:#{varStatus.index}:newKindPanel"
onclick="#{rich:component('newTargetPanel')}.show()"
value="new Form"
action="#{myService.newForm(element)}"/>
</h:panelGroup>
</ui:repeat>
</h:form>
please notice prependId of h:form, id and varStatus of ui:repeat, execute of a:commandButton.
btw, supposing that a: stands for ajax4jsf, i wouldn't recommend using commandButton nested in commandLink.
Is #{myService.elementList} a List ? If so, I'm not sure if that will work. Try using javax.faces.model.DataModel instead. Be sure to read the documentation carefully. It works something like this:
public Datamodel getElements(){
if(elements == null)
elements = new ListDataModel(getElementList());
return elements;
}
public List<Element> getElementList(){
elementList = foo(); // load the elements in your List like you did before
return elementList;
}
in your view: <ui:repeat var="element" value="#{myService.elements}">
I'm not familiar with RichFaces, this is the only suggestion I can make.

h:selectOneMenu labels not changing on a4j rerender

I have an h:selectOneMenu set up that looks something like this:
<f:view>
<h:form id="tehForm">
<h2>Header</h2><br/>
<a4j:outputPanel id="msgPanel" ajaxRendered="true">
<h:messages styleClass="message"/>
</a4j:outputPanel>
<a4j:outputPanel id="mainPanel" ajaxRendered="true"><br/>
Select an item:<br/>
<h:selectOneMenu id="itemMenu" value="#{bean.itemId}">
<f:selectItem itemValue="-1" itemLabel="Please Select..."/>
<s:selectItems value="#{bean.item}" itemValue="#{item.id}" var="item" label="#{item.name}"/>
<a4j:support event="onchange" action="#{bean.selectItem}"/>
</h:selectOneMenu>
<rich:spacer width="10px"/>
<a4j:commandLink value="Create New" action="#{bean.createNew}"
rendered="#{bean.selectedItemId != 0}"/>
<h:outputText rendered="#{bean.selectedItemId gt -1}" value="Item Name: "/><br/>
<h:inputText rendered="#{bean.selectedItemId gt -1}" value="#{bean.selectedItem.name}" maxlength="50" size="75"/><br/><br/>
<a4j:commandButton value="Save New" action="#{bean.save}" rendered="#{bean.selectedItemId == 0}"/>
<a4j:commandButton value="Save Changes" action="#{bean.save}" rendered="#{bean.selectedItemId gt 0}" oncomplete="jsRerender();" />
<a4j:jsFunction name="jsRerender" rerender="mainPanel"/>
</a4j:outputPanel>
</h:form>
</f:view>
When creating new items, the new item does show up in the drop-down, but if I change the "name" attribute of my item and save, the label doesn't change to the new name until after the next request, despite the data in the bean having changed.
So I worked around it by forcing a second call to rerender again when saving changes is complete. The trouble is, this rerenders the messages panel as well, so I nuke any messages that may have been displayed. Using limitToList="true" in the a4j:jsFunction has the same effect as not calling it.
I need suggestions; either a new target for the rerender function, or another way of approaching the problem. Thanks in advance!
Try removing the oncomplete javascript and using reRender="mainPanel":
<a4j:commandButton value="Save Changes"
action="#{bean.save}"
rendered="#{bean.selectedItemId gt 0}"
reRender="mainPanel" />
In your version I think that "Save Changes" button without reRender attribute will rerenders all the page, thus refreshing the messages.

Resources