Here's my scenario: I'd like to update a page via Ajax in some cases, in other cases, execute a navigation rule. My use case is a login form. I'd like them to receive an error message via ajax if their uname/password fails, but navigate to a new page if it succeeds.
Has anyone done this using JSF2.0 f:ajax apis? I'm not really interested in solutions that go outside standard facelets, jsf2.0, etc.
It's not different from when doing it without ajax. Just return the next view ID as String the usual way via <h:commandXxx action> (and thus not <f:ajax listener>).
So, just
<h:commandButton value="Login" action="#{bean.login}">
<f:ajax execute="#form" render="#form" />
</h:commandButton>
with
public String login() {
// ...
return "nextpage";
}
will work as good as without <f:ajax>. It'll just go to nextpage.xhtml.
See also:
Differences between action and actionListener
JSF f:ajax listener vs commandButton action
Related
I have a an a4j:outputPanel that is rendered based on some boolean condition:
<a4j:outputPanel id="someDisplayRegion" rendered="#{doc.ready && someClass.someBooleanMethod}">
// bunch of stuff //
</a4j:outputPanel>
Then on the same .xhtml page, I have a drop-down menu and selecting one of its options should reRender the above region:
<rich:dropDownMenu>
<f:facet name="label">
<a4j:commandLink styleClass="btn-pulldown">
<span><h:outputText value="Export"></h:outputText></span>
<span class="opener"></span>
</a4j:commandLink>
</f:facet>
<rich:menuItem submitMode="none">
<s:link
rendered="#{someOtherBooleanMethod}"
value="#exportDoc"
action="#{runSomething.exportDoc()}"
reRender="someDisplayRegion"
target="downloadfile"
><s:conversationId /></s:link>
</rich:menuItem>
</rich:dropDownMenu>
However, when I click on the menu item from the drop-down menu, it does not go into someClass.someBooleanMethod and thus, does not re-render someDisplayRegion. Am I doing something wrong?
Consider this point of the RichFaces documentation:
As with most Ajax frameworks, you should not attempt to append or
delete elements on a page using RichFaces Ajax, but should instead
replace them. As such, elements that are rendered conditionally should
not be targeted in the render attributes for Ajax controls. For
successful updates, an element with the same identifier as in the
response must exist on the page. If it is necessary to append code to
a page, include a placeholder for it (an empty element).
So add a wrapper around your outputPanel and target the wrapper in the reRender attribute.
<a4j:outputPanel id="wrapper">
<a4j:outputPanel id="someDisplayRegion" rendered="#{doc.ready && someClass.someBooleanMethod}">
// bunch of stuff //
</a4j:outputPanel>
</a4j:outputPanel>
<s:link reRender="wrapper" [...] />
s:link doesn't have reRender attribute, it's only available on RichFaces components.
rich:menuItem and s:link aren't the best of friends. (especially not in earlier version of RichFaces).
Is there a specific reason why you want to use s:link here ?
Putting the action and the reRender on the menuItem itself should work fine.
I don't understand why you think clicking on the menu item should go into someClass.someBooleanMethod and not into runSomething.exportDoc(). At what point are doc.ready and someClass.someBooleanMethod being set to true? You might put a debugging statement in your code that verifies these are being set to true. If they are set to true and your a4j:outputPanel is still not rendering then you have a problem. I use the s:link as you do here and it works, but I remember having to fiddle with it. Make sure the action fired in the s:link returns a String. "actions" have to return strings that can be used for navigation though in my case the page navigates to itself (like yours).
I need you to recommend me a JSF component that can help in the following scenario(I will first paste an image that will help me explain):
This page that you see in the image is a registration page, each of the panels have different fields and gadgets, when the register button is clicked, a new user is saved into the database.
The problem i have is in the show buttons. The buttons on top of each panel when clicked should display one panel and hide the other, but they must not trigger the field validation.
I use field validation by the attribute "validator"(in combination with a backing bean method) that most of JSF input fields have.
Currently everything that you see there is inside one h:form.
-what should i do to display a panel and hide the other without triggering the validation of the panel that is hiding?
-Is there another alternative to the h:commandLink or h:commandButton(they trigger the validation)?
-Putting each panel in a different h:form can do the trick?(Is that permited?)
-What do you think would be the best approach?
Use p:tabView Of primefaces, then put your contents(registration panels) in separate tab, use separate form for both of the tab, it will solve your problem....
e.g.
<p:tabView>
<p:tab title="panel1">
<h:form id="form1" prependId="false">
<h:inputText label="Sample Label"/>
<p:commandButton value="register"/>
</h:form>
</p:tab>
<p:tab title="panel2">
<h:form id="form2" prependId="false">
<h:inputText label="Sample Label"/>
<p:commandButton value="register"/>
</h:form>
</p:tab>
</p:tabView>
-what should i do to display a panel and hide the other without triggering the validation of the panel that is hiding?
Use two h:form
s there another alternative to the h:commandLink or h:commandButton(they trigger the validation)?
to make POST you must only use them
I would have used rich:modalpanel for this purpose
For the panel's toggle, Why don't you use JavaScript?
<h:commandButton value="Show panel 1" onclick="$('#panel1').hide();$('#panel2').show();return false;"/>
The return false will restrict the page from submitting, hence page won't refresh and since no data is posted back to server, validation phase will never execute.
Bottom line : IMHO this can be handled at client side itself.
You are aware of the immediate attribute, allowing to refresh without changing data.
Unfortunately it doesn't remember changes entered, so you might end up in the validation explicitly being done in your action instead of by JSF itself.
In my JSF 1.2 webapp I have a page with a <h:commandButton> that invokes an action method on a backing bean. This action will cause data to be removed/replaced in the database, so I want to avoid any situations where the user accidentally clicks on the command button.
I would like to implement a simple "Are you sure?" prompt with "Yes/No" or "OK/Cancel" options using JavaScript. I'm not great with JavaScript and I have never mixed JavaScript with JSF before. Can anyone provide a code snippet to show me how to implement this?
Here is the piece of my JSP page where I declare the command button:
<h:commandButton
id="commandButtonAcceptDraft"
title="#{bundle.tooltipAcceptDraft}"
action="#{controller.actionReplaceCurrentReportWithDraft}"
image="/images/checkmark.gif">
</h:commandButton>
SOLUTION:
The solution provided by BalusC worked just fine. I wanted to also mention that it is easy to use text from a resource bundle as the prompt text. On my page, I load the resource bundle with an element like this:
<f:loadBundle basename="com.jimtough.resource.LocalizationResources" var="bundle" />
The <f:loadBundle> must be inside your <f:view>. Then I add the code provided by BalusC to my command button element but substitute a string from my resource bundle for the 'Are you sure?' text, like this:
<h:commandButton
id="commandButtonAcceptDraft"
title="#{bundle.tooltipAcceptDraft}"
action="#{controller.actionReplaceCurrentReportWithDraft}"
image="/images/checkmark.gif"
onclick="return confirm('#{bundle.confirmationTextAcceptDraft}')">
</h:commandButton>
The line in my English resource file (just a plain text file with key/value pairs) looks like this:
# text displayed in user prompt when calling confirm()
confirmationTextAcceptDraft=This will overwrite the current report and cannot be undone. Are you sure?
Use the JavaScript confirm() function. It returns a boolean value. If it returns false, then the button's default action will be blocked, else it will be continued.
<h:commandButton onclick="return confirm('Are you sure?')" />
Since it already returns boolean, there's absolutely no need to wrap it around in an if a suggested by other answers.
You could add the javascript to the onclick of the button.
<h:commandButton
id="commandButtonAcceptDraft"
title="#{bundle.tooltipAcceptDraft}"
action="#{controller.actionReplaceCurrentReportWithDraft}"
onclick="return confirm('Are you sure?')"
image="/images/checkmark.gif">
</h:commandButton>
This should work. Ideally it should be in a java script file.
<h:commandButton
id="commandButtonAcceptDraft"
title="#{bundle.tooltipAcceptDraft}"
action="#{controller.actionReplaceCurrentReportWithDraft}"
image="/images/checkmark.gif"
onclick="if (!confirm('Are you sure?')) return false">
</h:commandButton>
I'm not sure what event you'll have to listen for (onclick i would assume) as I've never used JSF. Generically speaking, this should work.
var element = document.getElementById('commandButtonAcceptDraft');
element.onclick = function(e){
return confirm('Are you sure etc...');
};
I have an a4j:commandButton which looks like this
<a4j:commandButton id="stopBtn" type="button" reRender="lastOp"
action="#{MyBacking.stop}" value="Stop" />
</a4j:commandButton>
When the app is deployed, and the button clicked, the stop() method is not being called. All the a4j:commandButton examples refer to forms, but this button is not in a form - it's a button the user is going to use to cause the server to run some back-end logic. At the moment, the method is
public void stopNode() {
logger.info("STOPPING");
setLastOp("Stopped.");
}
Other methods which don't use this type of button are updating the lastOp field, but I'm not seeing anything on the console with this one. Am I right to cast this as a button? Should I put this in a h:form tag?
The firebug console says:
this._form is null
which I don't understand.
Any help well appreciated.
UICommand components ought to be placed inside an UIForm component. So, your guess
Should I put this in a h:form tag?
is entirely correct :) This because they fire a POST request and the only (normal) way for that is using a HTML <form> element whose method attribute is set to "post". Firebug also says that a parent form element is been expected, but it resolved to null and thus no actions can be taken place.
Only "plain vanilla" links like h:outputLink and consorts doesn't need a form, because they just fires a GET request.
Yes, wrap it in a form. I'm sure BalusC will post a detailed explanation while I'm typing my answer. (yup, there it is)
I have to ask why you didn't just try a form first, before posting here.
Look at your code:
<a4j:commandButton id="stopBtn" type="button" reRender="lastOp" action="#{MyBacking.stop}" value="Stop" />
You finished <a4j:commandButton with />, why need that orphan </a4j:commandButton> ?
If for some reason you don't want to place the button inside a form, you can do something like this:
<a4j:commandButton onclick="fireAjax()"/>
<h:form>
<a4j:jsFunction name="fireAjax" action=".."/>
</h:form>
I'm somewhat confused about the lifecycle of ManagedBeans of type "request".
In this example i'm using one request bean "userBean" to fill this page and one request bean "userEditBean" to fill the following edit page.
<h:form>
<h:panelGrid border="1" columns="2">
<h:outputText value="Name" />
<h:outputText value="#{userBean.user.name}" />
...
</h:panelGrid>
<h:commandButton value="Edit" action="edit" actionListener="#{userEditBean.init}">
<f:attribute name="user" value="#{userBean.user}"/>
</h:commandButton>
</h:form>
When i press the Edit button a userEditBean is created but the attribute map resolves "user" to null.
Does this mean that the attribute EL is resolved after the userBean has already been destroyed? How can i pass values from incoming beans to outgoing beans?
You're setting the attribute value with an expression, not a static value. Whenever you request the value, the expression will be re-evaluated again. The userBean.user apparently isn't present in the subsequent request. You need to ensure that it is there (in other words, the constructor of the userBean should ensure that the user is been created and set.
There are however alternatives. One of the best is to use Tomahawk's <t:saveState> for that. Add it somewhere in the page:
<t:saveState value="#{userBean.user}" />
That said, I agree with Bozho that the whole approach is a bit strange, but that's another story. You may however get lot of useful ideas out either of the following articles: Communication in JSF and/or Using Datatables. Good luck.
request scope means the bean lives during one request. And you fill your edit page (1st request), and send the edited user (2nd request).
In addition to that, <f:attribute> sets tha attributes in the parent component, not in the request. So in your code the attributes will be found in the button.getAttributes() (if you have bound your button).
Furthermore, it is strange to have an actionListener method named init. Since you don't need the event, you can set the action to be the method which will do the editing operation, and make that method return the navigation-rule you want.