PrimeFaces Ajax Navigation with Browser History/Hashtag - ajax

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.

Related

PrimeFaces disable submit on pressing enter key

PrimeFaces disable submit on pressing enter key.
I’m, running PrimeFaces 5.1 running on WildFly 8.2 Final.
I have dialog, with two inputNumbers and two buttons. And the first inputNumber does some calculation on ajax blur event. Next to it is button which does some calculation in bean. And the problem is that when users press enter while focus is in inputNumber the button’s action gets fired and it’s really annoying. Is there a way to disable submitting with enter key on dialog?
Here is small xhtml dialog which can simulate my behavior:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:pe="http://primefaces.org/ui/extensions" >
<p:dialog id="id_example" header="Test dialog"
widgetVar="exampleDialog" modal="true" closable="true" >
<h:form id="id_example_form">
<p:panelGrid columns="3" styleClass="noBorders">
<h:outputText value="Input 1:" />
<pe:inputNumber id="Input1" value="#{exampleBean.number1}">
<p:ajax event="blur" update="valueInput1" />
</pe:inputNumber>
<p:commandButton value="Check something else" action="#{exampleBean.checkForUsername()}"
update=":growl_form" />
<h:outputText value="Input 1:" />
<p:inputText id="valueInput1" value="#{exampleBean.number1}" />
<p:commandButton value="Save" action="#{exampleBean.save()}" oncomplete="PF('exampleDialog').hide();"
update=":growl_form" />
</p:panelGrid>
</h:form>
</p:dialog>
</ui:composition>
And the bean:
package si.pucko.beans;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import si.pucko.util.Util;
#Named(value = "exampleBean")
#ViewScoped
public class ExampleBean implements Serializable {
private static final long serialVersionUID = 1L;
private BigDecimal number1;
public ExampleBean() {
number1 = new BigDecimal(BigInteger.ONE);
}
public BigDecimal getNumber1() {
return number1;
}
public void setNumber1(BigDecimal number1) {
this.number1 = number1;
}
public void checkForUsername() {
Util.ShowWarning("Just testing");
}
public void save() {
Util.ShowWarning("Saved");
}
}
The catch is i can't disable enter key with:
<h:form onkeypress="if (event.keyCode == 13) { return false; }">
Because client asked for hotkeys support and enter is used for submiting forms, recalculation some other values in some cases etc...
I think you use JavaScript to capture the enter key press and do nothing.
<h:form onkeypress="if (event.keyCode == 13) { return false; }">
Ref: https://stackoverflow.com/a/5486046/201891
return false; cancels an event across browsers if called at the end of an event handler attribute in the HTML. This behaviour is not formally specified anywhere as far as I know.
Ref: https://stackoverflow.com/a/1648854/201891
Update
It sounds like you want to disable the Enter key only when focus is in a particular field. You can write a Javascript method for that too and bind it to onkeypress. Write a Javascript method something like "if the enter key was pressed and the focus is in this field, return false; otherwise, return true".
As the answer referenced by Nimnio says, this is specific to HTML and browsers.
I consider this behavior to be inappropriate when using PrimeFaces.
I prefer to disable it globally, for all forms like this:
$('form').off('keypress.disableAutoSubmitOnEnter').on('keypress.disableAutoSubmitOnEnter', function(event) {
if (event.which === $.ui.keyCode.ENTER && $(event.target).is(':input:not(textarea,:button,:submit,:reset)')) {
event.preventDefault();
}
});
The target check allows the other default behaviors to work, like adding a line break in a textarea by pressing Enter.
To take into account new ajaxically added forms you'll need to call the above script after every AJAX request. There are multiple ways to do that, such as a <script> in a p:outputPanel autoUpdate="true", or calling a function in a p:ajaxStatus's oncomplete callback.
If this solution is not appropriate for some reason then consider the more localized one:
<h:form onsubmit="return false;">
Returning false here disables the non-AJAX default submit.
Putting a dummy/hidden button before the one you want to stop works best for me
<p:commandButton style="visibility: hidden;"/> <!-- dummy hidden button to stop keyPress from firirng submit -->
I used following solution:
<h:form onsubmit="return false;">
This prevents form submit. It works well only in case if you have ajax only behavior on this form.
It is default browser behavior to hunt a form for jQuery(":submit") elements and trigger the first listed on the form, when the enter key is pressed.
this will look strange on debugger, because if you have a function such as onclick="handle(event);".
You go to the text field, hit enter, and you will see an "artifical" onclick event with being passed to your primary submit action for that form.
The surest way to be in-control of what happens, I would say, is not by means of onkeypress as explained above. I found that to not work in all cases. On soame cases the form onkeypress simply does not get triggered, and you do not have then the opportunity to return flase; / event.preventDefault();. I am not 100% sure of all cases that justfied the onkeypress not getting triggered, but I suspect framework code preventing event bubbling in some instances.
Ultimately, what is really happening is your form is being submitted by your browser default behavior on ENTER withing input text field. It is not the bubling of the event to the form that submits the form. That is the flaw of trying to handle the event on the form and preventing default behavior there. That might just be too late.
And this is very easy to verify. If you tune youe inpu text and onkeypress always to event.preventDefault(). (A) you kill the textbox no text gets written on the texbox (B) The event still bubles up, but the browser does not submit the form anyway.
So the most important element of all is: where is the browser aiming at when it acts with the default behavior?
So what you can decide instead is that what you will take control of where the browser unleashes its default behavior. You can direct it to a dummy button.
This is the one and only one mechanism I have found to be really reliable to be in control of what happens when you hit enter on a text field withing a form is to have on that form a well known artifical button, e.g.:
<input type="submit" onclick"return false;" style="border:none; background-color: #myAppBrackgroundColor;" />
You get the idea, to make sure that the submit event is being routed by the browser to a button under your control.
You have to be careful, IE in particular is quirky.
If not for IE, you could simply style your button as display: none.
Because of IE, the browser will route the default submit action to a visible submit button, if one exists.
Therefore, your button cannot be in display none. You have to make it logically visible, but phsysically invisible. For that you supress the border and give it an appropriate background for your application.
This method is 100% reliable.
onkeydown and onchange()
<h:form id="inputform">
<p:inputText onkeydown="if (event.keyCode === 13) {onchange();return false;}" >
<p:ajax update="updatearea" />
</p:inputText>
</h:form>

Primefaces Ajax updated form does not trigger commandButton action

I have a problem with a form not triggering its commandButton action method.
When I submit the form without prior update (not selecting any node in the tree), the method triggers just fine.
As soon as the form is Ajax-updated, the commandButton won't call its action anymore.
Here is the JSF code:
<p:layoutUnit position="center">
<p:tree orientation="horizontal" value="#{flightTypeController.tree}" var="node"
selectionMode="single" selection="#{flightTypeController.selectedNode}">
<p:ajax event="select" listener="#{flightTypeController.onNodeSelect}" update=":typesTree"/>
<p:treeNode>
<h:outputText value="#{node.name}"/>
</p:treeNode>
</p:tree>
<h:form id="typesTree">
<p:inputText disabled="true" id="outputParent" value="#{flightTypeController.selectedOne.name}"/>
<p:inputText id="outputName" value="#{flightTypeController.current.name}"/>
<p:commandButton ajax="false" icon="ui-icon-disk" value="#{bundle.general_create}" action="#{flightTypeController.create()}"/>
</h:form>
</p:layoutUnit>
And the java listener:
public void onNodeSelect(final NodeSelectEvent event) {
final Object res = event.getTreeNode().getData();
if (res instanceof FlightType) {
selectedOne = (FlightType) res;
} else {
selectedOne = null;
}
}
I already check BalusC's bible and JS Fix but without success.
I've seen similar behaviours quite often (and had to find workarounds) so I might have misunderstood something fundamental.
Oh, yes, I checked multiple times : no nested forms in my code.
The JS fix which you found is hooking on jsf.ajax.addOnEvent which is only triggered by <f:ajax>, not by PrimeFaces components which use jQuery under the covers.
To cover PrimeFaces ajax requests as well, grab the current version of the JS fix (I have recently updated that post) and add the following to apply this fix on jQuery ajax requests as well:
$(document).ajaxComplete(function(event, xhr, options) {
if (typeof xhr.responseXML != 'undefined') { // It's undefined when plain $.ajax(), $.get(), etc is used instead of PrimeFaces ajax.
fixViewState(xhr.responseXML);
}
}
Disclaimer: I haven't tried your specific use case. But, theoretically, it should solve your problem.

render page title using jsf 2

is there any way to include the page's title inside an f:ajax tag, so that its content is also updated at run time?
currently my f:ajax tag looks like this -
<f:ajax render="#form">
i tried to add an id to the h:head tag, and add that id to the 'render' property of the f:ajax tag, but if i do so i get an error when the page loads -
<f:ajax> contains an unknown id 'pageHead' - cannot locate it in the context of the component B1
cheers,
eRez
Interesting question. I think there is no such command in render of ajax (like #form) to re-render the page title. What you can do is to wrap <title> with <h:form> or other "grouping" component (eg. h:panelGroup as Daniel said) like this :
<h:form id="titleForm">
<title>#{fooBean.fooTitle}</title>
</h:form>
And then call ajax update eg. with button :
<h:commandButton immediate="true" value="Change title">
<f:ajax event="click" render=":titleForm" listener="#{fooBean.changeTitle}"/>
</h:commandButton>
Bean:
private String fooTitle;
public void changeTitle() {
this.fooTitle= "updatedTitle";
}
// getter/setter
I don't know what else could you do, this should work. So wrap your title with <h:form id="..."> and then render it from ajax.

JSF f:ajax listener not called

I am trying to have an h:inputText switch out with a different one when an h:commandButton is clicked. To do this, I am trying to tie an f:ajax command with the h:commandButton, with the listener setting a value on the bean (deciding which one to display), and re-rendering the area.
I have tried using listener on the f:ajax, actionListener on the h:commandButton, action on the h:commandButton with execute on the f:ajax. Nothing worked. The mothed I am trying to call is not being called at all - there is no println (see what follows).
The panelGroup is being re-rendered, which is why I need the onevent attribute that re-attaches some JavaScript hint text based on the title (I had an earlier question involving this).
The method I am trying to call:
public void morePressed(AjaxBehaviorEvent e) {
easySearch = !easySearch;
System.out.println("Made it!");
}
The JSF segment that is not working (note the last h:commandButton is trying to re-render the panelGroup):
<h:form>
<h:panelGroup id="switchSearchTexts">
<h:inputText accesskey="s" alt="Search" id="searchBoxPeople" title="Search Plebeians" valueChangeListener="#{peopleBean.simplePersonQuery}" size="25" rendered="#{peopleBean.easySearch}">
<f:ajax render="peopleDataTable" event="keyup" />
</h:inputText>
<h:inputText accesskey="s" alt="Search First Name" id="searchBoxFN" title="Search First Name" size="25" rendered="#{!peopleBean.easySearch}">
<f:ajax render="peopleDataTable" event="keyup" />
</h:inputText>
</h:panelGroup>
<div id="expandBox">
<h:inputText id="searchBoxLN" alt="Search Last Name" styleClass="hideToggle" title="Search Last Name" size="25" />
<h:inputText id="searchBoxAddress" alt="Search Address" styleClass="hideToggle" title="Search Address" size="25" />
</div>
<h:commandButton type="button" styleClass="moreButtonAsText" id="moreButtonAsText" value="▸More">
<f:ajax listener="#{peopleBean.morePressed}" render="switchSearchTexts" event="click" onevent="callFunctionAjaxRequest" />
</h:commandButton>
This is the JavaScript (jQuery) that I attach to the More button on pageload. I give it not because I think it could help, but I don't know if this could interfere with the ajax listener in any way:
$(function() {
textboxHint();
$('input[id$="moreButtonAsText"]').toggle(function(e) {
$(this).prop('value', '\u25c2Less');
$(".hideToggle").show(300);
}
, function () {
$(this).prop('value', '\u25b8More');
$(".hideToggle").hide(100);
$('input[id$="searchBoxAddress"]').prop('value', function() {
return $(this).prop('title');
});
$('input[id$="searchBoxAddress"]').blur();
});
});
I have no idea. As I said, I have tried actionListener on h:commandButton with various appraoches there, as well as various approaches to the listener on the ajax. Can anybody see why the listener does not work?
Update:
What I ended up doing, before having an answer, is converting everything to display and hide based on JavaScript, with stuff I needed hidden if they didn't have javascript initially hidden, etc.
However I need the f:ajax elsewhere, now.
The solution is to take out event="click" on the h:commandButton. I still do now know why this was causing it to break, but hey, whatever works.
I had an issue like this. It turned out that an inputText somewhere had a value="#{something.something}" where the property wasn't settable. The exception wasn't being reported anywhere. I had to put a breakpoint on Exception and all subclasses to find it.
Do you really have a function named callFunctionAjaxRequest in your js code? cause if not it may cause the button not to work (because its being referenced to a not existing function) ,
look at the firebug for a possible errors...
try this version of your command button (event="click" can be removed too... and self execute too)
<h:commandButton type="button" styleClass="moreButtonAsText" id="moreButtonAsText" value="More">
<f:ajax listener="#{peopleBean.morePressed}" render="switchSearchTexts" />
</h:commandButton>
Another thing , in your ajax calls of the upper input texts you got reference to searchBoxPeople twice (instead to searchBoxFN in the second f:ajax), fix it cause otherwise when working with searchBoxFN its f:ajax will try to execute an element that its not being rendered ... (can cause serious issues...)
p.s prependId="false" in your h:form will simplify your selectors in jQuery...
The issue is that the managed bean needs to be set up with the right signature event as an input param. Through lots of testing, I was trying to use the same class taking an AjaxBehaviorEvent. As in the same example on the previous forum.
when I declared an ActionListener event (compliant with the button jsf action), the bean is executed!
I had the same problem and followed your example to help me exactly. I simply (20 hrs) fixed this by including the following in my bean:
The first one now gets fired!
public void actionListener(ActionEvent actionEvent) {
// Add event code here...
System.out.println("Made it!");
}
public void morePressed(AjaxBehaviorEvent e) {
System.out.println("Made it!");
}

JSF AJax - How to render the output from an URL

I am trying to render the output of an URL in the same page, using JSF / Ajax (a4j) and so far I am having no luck. BTW, I am using RichFaces 4.0. For example, if the User clicks a row in a Table (or just a button), I like to show a web page right below the Table, that is relevant to the row clicked. Is there a way I can get the relevant URL using a Backing Bean and invoke that URL using A4J Tags? I searched for such tags or sample code and so far I could not find any.
Thanks for your help.
Embedding the result of an external URL in your webpage can only neatly be done with help of a HTML <iframe> element.
So, this should do:
<h:form>
<h:dataTable value="#{bean.websites}" var="website">
<h:column>#{website.name}</h:column>
<h:column>
<h:commandButton value="show" action="#{bean.setWebsite(website)}">
<f:ajax render=":website" />
</h:commandButton>
</h:column>
</h:dataTable>
</h:form>
<h:panelGroup id="website">
<iframe src="#{bean.website.url}"></iframe>
</h:panelGroup>
with
#ManagedBean
#ViewScoped
public class Bean {
private List<Website> websites; // +getter
private Website website; // +getter+setter
// ...
}

Resources