h:outputLink with f:ajax - method called, but link not shown - ajax

This does not work
<h:form style="display: inline;">
<h:outputLink value="#{title.link}" >
#{msg['g.readMore']}
<f:ajax event="click" immediate="true" listener="#{titlesBean.titleClicked(title.id)}" />
</h:outputLink>
</h:form>
What I want it to do is when clicked to call #{titlesBean.titleClicked(title.id)} and then go to the link. The method is called but it doesn't go to the link. There are several other ways to do it (with commandLink and then a redirect, but I would like to know why this is not working).
This is the method itself:
public String titleClicked(long titleId) {
this.titlesManager.addXtoRating(titleId, 1);
return null;
}
Note: this is only a sidenote, but I accidentally found out that this works:
<script type="text/javascript">
$('.share').popupWindow({centerBrowser:1,height:380,width:550});
</script>
<h:form style="display: inline;">
<h:outputLink styleClass="share" value="http://www.facebook.com/sharer.php...">
<img src="images/facebook-icon.jpg" />
<f:ajax event="click" immediate="true" listener="#{titlesBean.titleLiked(title.id)}" />
</h:outputLink>
</h:form>
Check out the styleClass="share"
Update: (I have less than 100 rep, so I cannot answer my own question for 8 hours, this is how to put it delicately - stupid).
I waited for a while, but nobody answered.
So this is my hacked solution ( I don't like it at all, but it works):
<h:form style="display: inline;">
<h:outputLink target="_blank" styleClass="click8" value="#{title.link}" >
#{title.heading}
<f:ajax event="click" immediate="true" listener="#{titlesBean.titleLiked(title.id)}" />
</h:outputLink>
</h:form>
And this is the important part:
<h:head>
<script type="text/javascript">
$(document).ready(function(){
$('.click8').click(function (event){
var url = $(this).attr("href");
window.open(url, "_blank");
event.preventDefault();
});
});
</script>
</h:head>
Note: this has to be in the header, otherwise I had a major bug with the link opening a thousand windows.

It does not work because there's means of a race condition here. Two HTTP requests are been sent simultaneously in the same window. One ajax to the server and one normal to the given link. The one which finishes sooner wins. You want to send the HTTP requests in sync. First the ajax one to the server and when it returns, then the normal one to the given link.
As to your hacky solution, it works because it uses JavaScript to open the URL in a new window instead of the current one and then blocks the link's default action, so the normal response just arrives in a completely separate window while the ajax response still arrives in the initial window. So there's no means of a race condition of two HTTP requests in the initial window anymore.
As to the final solution, this is not possible with standard set of JSF 2.0 components. Using <h:commandLink> and then doing a redirect is indeed doable, but the link is this way not crawlable by searchbots and it fires effectively a POST request, which is IMO more worse than your new window solution.
If you would really like to open the link in the current window, hereby keeping the target URL in the link's href, then I'd suggest to create a simple servlet which does the link tracking and redirecting job and let jQuery manipulate the link target during onclick.
Something like this
<a rel="ext" id="ext_#{title.id}" href="#{title.link}">read more</a>
(HTML element IDs may not start with a digit! Hence the ext_ prefix, you can of course change this whatever way you want.)
with
$(function() {
jQuery('a[rel=ext]').click(function(e) {
var link = jQuery(this);
var url = 'track'
+ '?id=' + encodeURIComponent(link.attr('id'))
+ '&url=' + encodeURIComponent(link.attr('href'));
if (link.attr('target') == '_blank') {
window.open(url);
} else {
window.location = url;
}
e.preventDefault();
});
});
and
#WebServlet(urlPatterns={"/track"})
public class TrackServlet extends HttpServlet {
#EJB
private TrackService trackService;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String id = request.getParameter("id");
String url = request.getParameter("url");
if (id == null || url == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
trackService.track(id.replaceAll("\\D+", "")); // Strips non-digits.
response.sendRedirect(url);
}
}

Related

How to pass a param through a Ajax request

My objective is to save the current cursor position and append new values to it for every new button we enter.To achive it i am trying to send a ajax request and update my back end coordinated every time is focus out of the input field.
I am succesfull i calling the java script function before calling by backing bean action method.But for some reason i am unable to see my request param values when ever i make a ajax request.
<p:inputText id="testing1" value="#{dropDownView.city}">
<p:ajax event="keyup" onstart="callOnAjax();" listener="#{dropDownView.assignCity()}" execute="#this" update="out1" >
<f:param value="test" name="#{articlePromo.promocionArticuloId}"/>
<h:inputHidden id="x" value="#{bean.x}" />
</p:ajax>
<script type="text/javascript">
function callOnAjax(){
$("#detailsPanel").bind("keydown keypress mousemove", function() {
var $form = jQuery(this).closest("form");
$form.find("input[id$=':x']").val($(this).caret().start);
alert("Current position: " + $(this).caret().start);
});
}
</script>
And in my dropDownView Controller
public void assignCity()
{
System.out.println("positon of x"+getX()+"position of y"+y);
FacesContext context = FacesContext.getCurrentInstance();
String id = context.getApplication().evaluateExpressionGet(context, "#{articlePromo.promocionArticuloId}", String.class);
city =country;
}
I tried all different approaches using hidden as well.But i dont see the value in my controller.I even hard coded the request param value and hidden attribute value.But still not succesfull.Any help is much appreciated.

PrimeFaces Ajax Navigation with Browser History/Hashtag

I have implemented a web application which is a one-page-design. Which basically loads a single page then updates with AJAX the central content. The code is the following:
<h:body>
<pe:layout id="page" fullPage="true">
<!-- West -->
<pe:layoutPane id="west" position="west" >
<f:facet name="header">Main Menu</f:facet>
<h:form id="form1">
<p:panelMenu id="panelMenu">
<p:submenu label="Persons">
<p:menuitem value="Person List" update=":centerpanel"
actionListener="#{layout.setAll('formPersonList.xhtml', 'Person List')}">
</p:menuitem>
</p:submenu>
</p:panelMenu>
</h:form>
</pe:layoutPane>
<!-- Center -->
<pe:layoutPane id="content" position="center">
<h:panelGroup id="centerpanel" layout="block">
<ui:include id="include" src="#{layout.navigation}" />
</h:panelGroup>
</pe:layoutPane>
</pe:layout>
</h:body>
This basically works, but I want to enable browser navigation as well. For example like: http://ticketmonster-jdf.rhcloud.com/ with the hashtags on the url. So using the back/forward button I can go to the equivalent option.
Any idea how to do this?
I have created a blog post explaining how to get this to work using jQuery BBQ, based on your question.
With jQuery BBQ you can keep track of state, history and allow bookmarking while dynamically modifying the page via AJAX and/or DHTML.. just click the links, use your browser's back and next buttons, reload the page..
First we should include jQuery BBQ.
<h:outputScript library="js" name="jquery.ba-bbq.min.js" />
Now consider we have the menu (with all our nav rules)
<p:submenu label="Meat">
<p:menuitem outcome="/meat/steak.xhtml" value="Steak" />
<p:menuitem outcome="/meat/burger.xhtml" value="Burger" />
<p:menuitem outcome="/meat/chicken.xhtml" value="Chicken" />
<p:menuitem outcome="/meat/kebab.xhtml" value="Kebab" />
</p:submenu>
Then the centered content
<pe:layoutPane id="content" position="center">
<h:panelGroup id="centerpanel" layout="block">
<ui:include id="include" src="#{mainBean.currentNav}" />
</h:panelGroup>
</pe:layoutPane>
the include reflects the currentNav clicked.
now define a remoteCommand inside the form
<p:remoteCommand name="updateNav"
actionListener="#{mainBean.updateNav()}"
update=":centerpanel"/>
This remoteCommand will update our currentNav based on the hashtag.
Create your JS file or include the following code into the document ready
$(document).ready(function() {
$('.ui-menuitem-link').click(function(event) {
event.preventDefault();
var currentNav = $(this).attr('href').
substr($(this).attr('href').indexOf("/faces") + 6)
window.location.hash = '#' + currentNav;
})
$(window).bind('hashchange', function(e) {
var url = $.param.fragment();
updateNav([{name: 'currentNav', value: url}]); //remoteCommand Call
})
$(window).trigger('hashchange');
});
Basically first we handle our clicks on the menu items, setting the hash of the window.
then we define an event of the hashchange of the window, using the help of jQuery BBQ.
ManagedBean
public void updateNav() {
FacesContext context = FacesContext.getCurrentInstance();
Map map = context.getExternalContext().getRequestParameterMap();
currentNav = (String) map.get("currentNav");
}
For a complete code, please see my newly post created for the question.
Primefaces Hash Navigation Using jQuery BBQ
And also the example is available on github.
Hope it helps.
If I understand well your question, you want to navigate with back/forward button. You can do this with LinkedList in your backing bean:
private LinkedList<String> historyForBackPage= new LinkedList<String>();
public void setLastBackPage(String navigationCase) {
historyForBackPage.push(navigationCase);
if (historyForBackPage.size() > yourMaxSize) {
historyForBackPage.pollLast();
}
}
public String getLastBackPage() {
return historyForBackPage.pop();
}
and always add last page when you call layout.setAll method. The simple commandButton call getLastBackPage() method. Before try it, please configure navigation case in faces-config.xml.
If your case cannot work navigation case, because you work only one XHTML, than you could add back/foward page name for your bean and render your page. May be simple JavaScript call onclick="window.history.go(-1); return false;" it could be usefull in your case. I don't know. Please try it!
In my answer I focused only to back button, but I think you can adapt foward button in same way.
By the way, the Breadcrumb is nice PrimeFaces feature.
If you want to catch browser back clicking action, you can use one JavaScript or Ajax script.
For Ajax script please check this answer https://stackoverflow.com/a/10050826/1047582.

PrimeFaces: Dialog closes on validation errors [duplicate]

Minimal example dialog:
<p:dialog header="Test Dialog"
widgetVar="testDialog">
<h:form>
<p:inputText value="#{mbean.someValue}"/>
<p:commandButton value="Save"
onsuccess="testDialog.hide()"
actionListener="#{mbean.saveMethod}"/>
</h:form>
</p:dialog>
What I want to be able to do is have the mbean.saveMethod somehow prevent the dialog from closing if there was some problem and only output a message through growl. This is a case where a validator won't help because there's no way to tell if someValue is valid until a save is submitted to a back end server. Currently I do this using the visible attribute and point it to a boolean field in mbean. That works but it makes the user interface slower because popping up or down the dialog requires hitting the server.
The onsuccess runs if ajax request itself was successful (i.e. there's no network error, uncaught exception, etc), not if action method was successfully invoked.
Given a <p:dialog widgetVar="yourWidgetVarName">, you could remove the onsuccess and replace it by PrimeFaces RequestContext#execute() inside saveMethod():
if (success) {
RequestContext.getCurrentInstance().execute("PF('yourWidgetVarName').hide()");
}
Note: PF() was introduced in PrimeFaces 4.0. In older PrimeFaces versions, you need yourWidgetVarName.hide() instead.
If you prefer to not clutter the controller with view-specific scripts, you could use oncomplete instead which offers an args object which has a boolean validationFailed property:
<p:commandButton ...
oncomplete="if (args && !args.validationFailed) PF('yourWidgetVarName').hide()" />
The if (args) check is necessary because it may be absent when an ajax error has occurred and thus cause a new JS error when you try to get validationFailed from it; the & instead of & is mandatory for the reason explained in this answer, refactor if necessary to a JS function which you invoke like oncomplete="hideDialogOnSuccess(args, 'yourWidgetVarName')" as shown in Keep <p:dialog> open when validation has failed.
If there is however no validation error and the action method is successfully triggered, and you would still like to keep the dialog open because of e.g. an exception in the service method call, then you can manually trigger validationFailed to true from inside backing bean action method by explicitly invoking FacesContext#validationFailed(). E.g.
FacesContext.getCurrentInstance().validationFailed();
Using the oncomplete attribute from your command button and really simple script will help you a lot.
Your dialog and command button would be something similar to this:
<p:dialog widgetVar="dialog">
<h:form id="dialogView">
<p:commandButton id="saveButton" icon="ui-icon-disk"
value="#{ui['action.save']}"
update=":dataList :dialogView"
actionListener="#{mbean.save()}"
oncomplete="handleDialogSubmit(xhr, status, args)" />
</h:form>
</p:dialog>
An the script would be something like this:
<script type="text/javascript">
function handleDialogSubmit(xhr, status, args) {
if (args.validationFailed) {
dialog.show();
} else {
dialog.hide();
}
}
</script>
I've just googled up this solution. Basically the idea is to use actionListener instead of button's action, and in backing bean you add callback parameter which will be then check in button's oncomplete method. Sample partial code:
JSF first:
<p:commandButton actionListener="#{myBean.doAction}"
oncomplete="if (!args.validationFailed && args.saved) schedulesDialog.hide();" />
Backing bean:
public void doAction(ActionEvent actionEvent) {
// do your stuff here...
if (ok) {
RequestContext.getCurrentInstance().addCallbackParam("saved", true);
} else {
RequestContext.getCurrentInstance().addCallbackParam("saved", false);
}
}
Hope this helps someone :)
I use this solution:
JSF code:
<p:dialog ... widgetVar="dlgModify" ... >
...
<p:commandButton value="Save" update="#form" actionListener="#{AdminMB.saveTable}" />
<p:commandButton value="Cancel" oncomplete="PF('dlgModify').hide();"/>
Backing bean code:
public void saveTable() {
RequestContext rc = RequestContext.getCurrentInstance();
rc.execute("PF('dlgModify').hide()");
}
I believe this is the cleanest solution.
Doing this you don't need to change your buttons code.
This solution overrides the hide function prototype.
$(document).ready(function() {
PrimeFaces.widget.Dialog.prototype.originalHide = PrimeFaces.widget.Dialog.prototype.hide; // keep a reference to the original hide()
PrimeFaces.widget.Dialog.prototype.hide = function() {
var ajaxResponseArgs = arguments.callee.caller.arguments[2]; // accesses oncomplete arguments
if (ajaxResponseArgs && ajaxResponseArgs.validationFailed) {
return; // on validation error, prevent closing
}
this.originalHide();
};
});
This way, you can keep your code like:
<p:commandButton value="Save" oncomplete="videoDetalheDialogJS.hide();"
actionListener="#{videoBean.saveVideo(video)}" />
The easiest solution is to not have any "widget.hide", neither in onclick, neither in oncomplete. Remove the hide functions and just put
visible="#{facesContext.validationFailed}"
for the dialog tag

Keep p:dialog open when a validation error occurs after submit

Minimal example dialog:
<p:dialog header="Test Dialog"
widgetVar="testDialog">
<h:form>
<p:inputText value="#{mbean.someValue}"/>
<p:commandButton value="Save"
onsuccess="testDialog.hide()"
actionListener="#{mbean.saveMethod}"/>
</h:form>
</p:dialog>
What I want to be able to do is have the mbean.saveMethod somehow prevent the dialog from closing if there was some problem and only output a message through growl. This is a case where a validator won't help because there's no way to tell if someValue is valid until a save is submitted to a back end server. Currently I do this using the visible attribute and point it to a boolean field in mbean. That works but it makes the user interface slower because popping up or down the dialog requires hitting the server.
The onsuccess runs if ajax request itself was successful (i.e. there's no network error, uncaught exception, etc), not if action method was successfully invoked.
Given a <p:dialog widgetVar="yourWidgetVarName">, you could remove the onsuccess and replace it by PrimeFaces RequestContext#execute() inside saveMethod():
if (success) {
RequestContext.getCurrentInstance().execute("PF('yourWidgetVarName').hide()");
}
Note: PF() was introduced in PrimeFaces 4.0. In older PrimeFaces versions, you need yourWidgetVarName.hide() instead.
If you prefer to not clutter the controller with view-specific scripts, you could use oncomplete instead which offers an args object which has a boolean validationFailed property:
<p:commandButton ...
oncomplete="if (args && !args.validationFailed) PF('yourWidgetVarName').hide()" />
The if (args) check is necessary because it may be absent when an ajax error has occurred and thus cause a new JS error when you try to get validationFailed from it; the & instead of & is mandatory for the reason explained in this answer, refactor if necessary to a JS function which you invoke like oncomplete="hideDialogOnSuccess(args, 'yourWidgetVarName')" as shown in Keep <p:dialog> open when validation has failed.
If there is however no validation error and the action method is successfully triggered, and you would still like to keep the dialog open because of e.g. an exception in the service method call, then you can manually trigger validationFailed to true from inside backing bean action method by explicitly invoking FacesContext#validationFailed(). E.g.
FacesContext.getCurrentInstance().validationFailed();
Using the oncomplete attribute from your command button and really simple script will help you a lot.
Your dialog and command button would be something similar to this:
<p:dialog widgetVar="dialog">
<h:form id="dialogView">
<p:commandButton id="saveButton" icon="ui-icon-disk"
value="#{ui['action.save']}"
update=":dataList :dialogView"
actionListener="#{mbean.save()}"
oncomplete="handleDialogSubmit(xhr, status, args)" />
</h:form>
</p:dialog>
An the script would be something like this:
<script type="text/javascript">
function handleDialogSubmit(xhr, status, args) {
if (args.validationFailed) {
dialog.show();
} else {
dialog.hide();
}
}
</script>
I've just googled up this solution. Basically the idea is to use actionListener instead of button's action, and in backing bean you add callback parameter which will be then check in button's oncomplete method. Sample partial code:
JSF first:
<p:commandButton actionListener="#{myBean.doAction}"
oncomplete="if (!args.validationFailed && args.saved) schedulesDialog.hide();" />
Backing bean:
public void doAction(ActionEvent actionEvent) {
// do your stuff here...
if (ok) {
RequestContext.getCurrentInstance().addCallbackParam("saved", true);
} else {
RequestContext.getCurrentInstance().addCallbackParam("saved", false);
}
}
Hope this helps someone :)
I use this solution:
JSF code:
<p:dialog ... widgetVar="dlgModify" ... >
...
<p:commandButton value="Save" update="#form" actionListener="#{AdminMB.saveTable}" />
<p:commandButton value="Cancel" oncomplete="PF('dlgModify').hide();"/>
Backing bean code:
public void saveTable() {
RequestContext rc = RequestContext.getCurrentInstance();
rc.execute("PF('dlgModify').hide()");
}
I believe this is the cleanest solution.
Doing this you don't need to change your buttons code.
This solution overrides the hide function prototype.
$(document).ready(function() {
PrimeFaces.widget.Dialog.prototype.originalHide = PrimeFaces.widget.Dialog.prototype.hide; // keep a reference to the original hide()
PrimeFaces.widget.Dialog.prototype.hide = function() {
var ajaxResponseArgs = arguments.callee.caller.arguments[2]; // accesses oncomplete arguments
if (ajaxResponseArgs && ajaxResponseArgs.validationFailed) {
return; // on validation error, prevent closing
}
this.originalHide();
};
});
This way, you can keep your code like:
<p:commandButton value="Save" oncomplete="videoDetalheDialogJS.hide();"
actionListener="#{videoBean.saveVideo(video)}" />
The easiest solution is to not have any "widget.hide", neither in onclick, neither in oncomplete. Remove the hide functions and just put
visible="#{facesContext.validationFailed}"
for the dialog tag

JSF 2: page redirect after ajax call

I'm stuck in a navigation case problem similar to this one.
In a few words, I'm trying to redirect navigation from one page to another, using an ajax rendered h:commandLink.
Here's the backing bean
#ManagedBean
public class StartBean {
public void search(){
FacesContext
.getCurrentInstance()
.getExternalContext()
.getFlash()
.put("result", "hooray!")
;
}
public String showResult(){
return "result?faces-redirect=true";
}
}
and the starting page
<h:body>
<h:form prependId="false">
<h:commandButton value="Click" action="#{startBean.search}">
<f:ajax execute="#this" render="#form"/>
</h:commandButton>
<br/>
<h:commandLink
action="#{startBean.showResult()}"
rendered="#{flash.result != null}"
value="#{flash.result}"
/>
</h:form>
</h:body>
whereas result page is just showing a message. Both pages are on web module context root.
It happens that the h:commandLink is correctly displayed after ajax submit, but clicking on it causes a page refresh. It doesn't redirect towards the result page, as expected.
After it, if page is reloaded (F5), result page is shown. It seems to be a rendering cycle matter.
Any suggestion?
Thanks in advance.
The rendered attribute of all input and command components is re-evaluated when the form is submitted. So if it evaluates false, then JSF simply won't invoke the action. The Flash scope is terminated when the request/response of the search() method is finished. It isn't there in the Flash scope anymore when you send the request of the showResult(). I suggest to put the bean in the view scope and bind the rendered attribute to its property instead.
#ManagedBean
#ViewScoped
public class StartBean {
private String result;
public void search(){
result = "hooray";
}
public String showResult(){
return "result?faces-redirect=true";
}
public String getResult() {
return result;
}
}
with
<h:commandLink
action="#{startBean.showResult}"
rendered="#{startBean.result != null}"
value="#{startBean.result}"
/>
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated

Resources