How can I maintain scroll position across postbacks in JSF? - jsf

I am using JSF frontend for a page where an image is uploaded or deleted. Clicking on the upload or delete button causes a postback and the page to reload with the updated status. This however, resets the scroll position of the page. How should I go about retaining the scrollback of this page on the postback actions.

You can do that with an actionListener. For example, in your page (page.jsf for example):
<f:view>
<h:form>
<h:commandLink actionListener="#{bean.method}">
<h:outputText value="Submit" />
<f:param name="anchor" value="image" />
</h:commandLink>
</h:form>
<div id='result'>
<h1><a name='image'>Image</a></h1>
</div>
</f:view>
And the managed bean looks like:
public class Bean {
public void method(ActionEvent actionEvent) {
// Get parameter
String ancla = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("anchor");
try {
FacesContext.getCurrentInstance().getExternalContext().redirect("page.jsf#" + anchor);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Hope this helps

If you are using Apache MyFaces Tomahawk, you can set the parameter AUTOSCROLL then make sure AutoScrollPhaseListener is enabled.
I'm not sure this functionality is specified by JSF, but instead is something extra implemented by Apache MyFaces Tomahawk.
Also, be aware that prior to version 1.1.6, there is a cross-site scripting vulnerability in the AUTOSCROLL implementation.

you can use jquery.. Using cookies..
in ready function
$(function(){
//set window scroll position if cookie is set
window.scroll(0,getCookie('myCookie'));
//unset cookie after setting scroll position
deleteCookie('myCookie');
//make this class objects keep page scroll position
jQuery(window).unload(function() {
setCookie('myCookie', getPageScroll());
});
//-------------------
});
after ready function add this functions..
function setCookie(name,value) {
var date = new Date();
date.setTime(date.getTime()+(10*1000));
var expires = "; expires="+date.toGMTString();
document.cookie = name+"="+value+expires+"; path=/";
}
function getCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function deleteCookie(name) {
setCookie(name,"",-1);
}
i wish this help you..

Related

Scroll down and load more data to ui:repeat

Am building an application using jsf 2.3 and primefaces 6.2 when scroll down i want to load more data and append it to the old one. bellow are my code.
My JSF code
<h:commandScript name="loadMoreTimeline" execute="#this" render=":postForm:panelBlock" action="#{timelineBean.loadMoreTimeline()}" />
<h:panelGroup id="panelBlock" styleClass="panelBlock" layout="block">
<ui:repeat id="timelineRepeater" value="#{timelineBean.timelineList}" var="timeline">
<div data-id="#{timeline.postId}">
<h:outputText escape="false" value="#{timeline.content}" />
</div>
</ui:repeat>
</h:panelGroup>
#Named
#ViewScoped
public class TimelineBean implements Serializable {
private List<Timeline> timelineList;
#PostConstruct
public void init() {
//loaded initially and contains the first N records.
timelineList = feedService.getTimelines();
}
// SQL - SELECT * FROM timeline WHERE post_id < 'post_id' ORDER BY post_id DESC LIMIT 10
public void loadMoreTimeline() {
String lastId = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("lastId");
long postId = Long.valueOf(lastId);
timelineList.addAll(feedService.getTimelines(postId));
}
public List<Timeline> getTimelineList() {
return timelineList;
}
}
Jquery code
$(window).scroll(function() {
if ($(window).scrollTop() === $(document).height() - $(window).height()) {
//get the last div postid
var lastId = $('.post-item:last').attr('data-id');
// calling my commandScript
loadMoreTimeline({lastId: lastId"});
}
});
The above code works very well but it renders all the component tree inside the panelBlock div. I want a situation where it will render only a subset of the component tree based on a specific request parameter.
So i came across Omnifaces component that render just one or more components on a view via a GET parameter. How to partially reload a ui:repeat? My problem is how will it work with my code. Because postConstruct will initially load the first N records, how will i call the loadMoreTimeline() method.
<h:outputScript>
$("#showMore").click(function() {
$items = $("#items");
var params = { start: $items.find("li").length, componentId: "itemsRepeater" };
//jQuery's $.get() reload the <ui:repeat>
$.get(location, params, function(html) {
$items.append(html);
});
});
</h:outputScript>

Previous barcode is displayed when attempting to display dynamically generated barcodes

When I generate barcode dynamically, it always prints the previous input, not the latest code. When I refresh the page with refresh button on the browser, the correct barcode is displayed. But If I refresh the page with a JSF commandbutton, still the previous result. Where have I gone wrong ?
JSF 2.1
Primefaces 4.0
Barbecue 1.5 beta
Chrome/Firefox Latest Updates
<p:graphicImage value="#{barcodeController.createBarcodeByCode(patientController.current.code)}"
style="max-width: 7.5cm; padding: 10px; margin: 10px;" >
</p:graphicImage>
This is from the JSF controller with request scope.
public StreamedContent createBarcodeByCode(String code) {
FacesContext context = FacesContext.getCurrentInstance();
if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
// So, we're rendering the view. Return a stub StreamedContent so that it will generate right URL.
return new DefaultStreamedContent();
} else {
barcode = null;
System.out.println("code = " + code);
if (code == null || code.trim().equals("")) {
return null;
}
File barcodeFile = new File(code);
try {
BarcodeImageHandler.saveJPEG(BarcodeFactory.createCode128C(code), barcodeFile);
barcode = new DefaultStreamedContent(new FileInputStream(barcodeFile), "image/jpeg");
} catch (Exception ex) {
System.out.println("ex = " + ex.getMessage());
}
return barcode;
}
}
References:
1. Dynamic StreamContent Answer by BalusC
2. Dynamic Barcode with Primefaces
Set cache="false" on the p:graphicImage.

Primefaces 4, dynamic menu setCommand method

I'm trying primefaces 4 but there are no documentation around for the new MenuModel. Here, Optimus Prime wrote about the new menu system with a little example.
http://blog.primefaces.org/?p=2594
At this point, he wrote about a setCommand method:
This point to a save method (found in the pf4 showcase: http://www.primefaces.org/showcase/ui/menu/menu.xhtml):
After this introduction, here's the question/problem. I'm creating a dynamic menu from a bean but I don't understand how know the menu clicked by the user and do the right operation.
public void init() {
if (spBean == null) {
System.out.println("spBean is NULL!");
return;
}
for (ServiceProvider sp: spBean.getListaSP()) {
DefaultMenuItem item = new DefaultMenuItem(sp.getNome());
//item.setUrl("#");
item.setIcon("images/sps/" + sp.getImageId() + ".png");
item.setCommand("#{dockMenuBackingBean.setNewMenu}");
//
model.addElement(item);
System.out.println(sp.getNome());
}
}
public void setNewMenu() {
System.out.println("A menu was clicked BUT witch menu? Arghh!!");
//
}
What I want to do, is to change the spSelected in ServiceProviderBackingBean, like I've done in PF3.5:
<p:dock>
<c:forEach items="#{serviceProvidersBean.sps}" var="sp">
<p:menuitem
value="#{sp.spInstanceName}"
icon="/images/sps/#{sp.spInstanceId}.png"
update=":form:spDetail" >
<f:setPropertyActionListener
value="#{sp}"
target="#{serviceProvidersBean.spSelected}" />
</p:menuitem>
</c:forEach>
</p:dock>
Any help?
EDIT:
Actually I'm doing this, but I'm looking for a better and cleaner way to achieve this.
public void init() {
if (spBean == null) {
System.out.println("spBean is NULL!");
return;
}
for (ServiceProvider sp: spBean.getListaSP()) {
DefaultMenuItem item = new DefaultMenuItem(sp.getNome());
//item.setUrl("#");
item.setIcon("images/sps/" + sp.getImageId() + ".png");
String command = String.format("#{dockMenuBackingBean.setNewMenu('%d')}", spBean.getListaSP().indexOf(sp));
item.setCommand(command);
//
model.addElement(item);
System.out.println(sp.getNome());
}
}
public void setNewMenu(Object x) {
Integer selectedId = Integer.parseInt((String)x);
System.out.println("Menu changed " + Integer.toString(selectedId));
//
}
Setting command parameters by setParam(key,value) can be done like that:
In your menu generating bean:
DefaultMenuItem item = new DefaultMenuItem("display list");
item.setId("listMenuItem");
item.setCommand("#{myBean.displayList}");
item.setParam("listId", 1l);
In your managed bean containing the action:
public String displayList(ActionEvent event) {
MenuItem menuItem = ((MenuActionEvent) event).getMenuItem();
Long id = Long.parseLong(menuItem.getParams().get("listId").get(0));
findListBy(id);
}
Reading parameters seems to be a bit complicated. But ActionListeners aren't supported by Primefaces 4 MenuItems (because they aren't derived from UICommand any more) so params seem to be new new way.
Optimus here, use setParam(key,value). You need to update to trunk code though for this.

RichFaces 4 fileupload clear and clear all buttons

Well currently I have this:
<rich:fileUpload addLabel="Agregar" clearAllLabel="Quitar todos"
clearLabel="Quitar" deleteLabel="Quitar"
doneLabel="Completado" uploadLabel="Subir archivos"
fileUploadListener="#{uploadBean.doUpload}"
acceptedTypes="txt, csv"
noDuplicate="true">
<a4j:ajax event="uploadcomplete" render="validationButton"/>
<a4j:ajax event="clear" listener="#{uploadBean.doClearFilesList}"
render="validationButton"/>
</rich:fileUpload>
On the backing bean I have a list of the files uploaded. When I click on Clear/Clear all button the event clear is fired and the method doClearFilesList (which just clears the list of files uploaded) is perfectly when the user hits the Clear All button, but If the user clicks on Clear button It should just delete the item on the list corresponding to the file cleared.
What can I do on my UploadBean.doClearFilesList method to delete a single file from the list? Should be something like:
public void doClearFilesList(){
files.clear(); //when CLEAR ALL is clicked
files.remove(oneFile); //when CLEAR is clicked
validationButtonRendered = false;
}
Any idea?
Cheers
UPDATE
RichFaces 4.1.0 Final
JSF Mojarra 2.1.6
Tomcat 7
I am not clear at which point you failed to run the sample described at https://community.jboss.org/message/727544#727544
However I hope following would work for you which is very similar to above sample.
Page:
<h:head>
<script>
function clear(event) {
var files = new Array();
var data = event.rf.data;
for (var i in data) {
files[i] = data[i].name;
}
clearFunc(files);
}
</script>
</h:head>
<body>
<h:form>
<rich:fileUpload onclear="clear(event);"/>
<a4j:jsFunction name="clearFunc" action="#{del.clearFile}" ajaxSingle="true">
<a4j:param name="fName" assignTo="#{del.fileNames}" />
</a4j:jsFunction>
</h:form>
</body>
Class:
public class Del {
String[] fileNames;
public void clearFile() {
for(String name : fileNames) {
System.out.println(">>" + name);
//Do file removing part here
}
}
public String[] getFileNames() {
return fileNames;
}
public void setFileNames(String[] fileNames) {
this.fileNames = fileNames;
}
}
Add "onclear" attribute to your <rich:fileUpload/> component and call a <a4j:jsFunction/> and pass the file name to it as below.
<rich:fileUpload onclear="clearFunc(event.memo.entry.fileName);" ..../>
Your <a4j:jsFunction/> should be as below.
<a4j:jsFunction name="clearFunc" actionListener="#{uploadBean.clearFile}" ajaxSingle="true">
<a4j:actionparam name="fName" />
</a4j:jsFunction>
Inside the listener method you can access the file name as below.
public void clearFile(ActionEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
String fileName = context.getExternalContext().getRequestParameterMap().get("fName").toString();
System.out.println("fileName = " + fileName);}

JSF PrimeFaces FileDownload problem

I'm using PrimeFaces for a new project and it's quite an impressive set of components.
Anyway, I have problem with "real world" use of filedownload component.
In my page I have a datalist that shows the attachments related to a particular document, and I want provide a link to directly download that file inside the datalist item.
Here's my xhtml code:
<p:dataList id="ListaAllegati" value="#{documentBean.documento.allegati}" type="definition" var="attach" style="border: none" ">
<f:facet name="description">
<h:outputText value="#{attach.name}" />
<p:commandLink ajax="false" title="Download" action="#{documentBean.selectAttach}>
<h:graphicImage style="margin-left: 10px; border: none" value="./images/article.png" height="24" width="24" ></h:graphicImage>
<p:fileDownload value="#{documentBean.downloadFile}"/>
<f:setPropertyActionListener target="#{documentBean.selectedAttach}" value="#{attach}" />
</p:commandLink>
</f:facet>
</p:dataList>
and the relative java bean (request scoped):
private StreamedContent downloadFile;
public StreamedContent getDownloadFile() {
log.info("getter dell'allegato invocato");
InputStream stream = null;
byte[] rawFile = null;
if (selectedAttach == null) {
log.warn("Nessun allegato passato");
return null;
} else {
try {
log.info("Recupero del file " + selectedAttach.getGuid());
rawFile = attachManager.retrieveFile(selectedAttach.getGuid());
} catch (Exception e) {
String msg = "Errore durante il recupero del file";
log.error(msg, e);
FacesMessage fmsg = new FacesMessage(msg, "");
FacesContext.getCurrentInstance().addMessage(null, fmsg);
}
stream = new ByteArrayInputStream(rawFile);
DefaultStreamedContent file = new DefaultStreamedContent(stream,
selectedAttach.getMimeType(), selectedAttach.getName());
return file;
}
}
public void selectAttach() {
log.info("commandLink action invocata");
}
private Allegato selectedAttach;
public Allegato getSelectedAttach() {
return selectedAttach;
}
public void setSelectedAttach(Allegato selectedAttach) {
log.info("Allegato selezionato");
if (selectedAttach==null) log.warn("L'allegato passato รจ nullo");
this.selectedAttach = selectedAttach;
}
So, couple of question:
Am I doing the right thing trying to pass the selected attachment that way? Otherwise, how can I pass a parameter to tell the bean wich attachment has been clicked?
Why the first time I click the command link, nothing happen? It make a roundtrip with server, but nothing happens. Second time, it gives me an exception.
Why documentBean.selectAttach is never called and the documentBean.selectedAttach property is never set (neither the second time)?
Thanks to anyone for any hint
How to get the row object from the datatable is answered in this question:
How can I pass selected row to commandLink inside dataTable?
This answers basically all the three questions.
As to the exception in the second click, that's likely because you didn't return from the catch block when an exception is been thrown in your getDownloadFile() method. You're continuing the remnant of the code flow while the rawFile is still null. Fix it accordingly as well. Add a return null to the end of catch or something. Better yet, you should be posting the entire stacktrace in the question as you don't seem to be able to understand it. It basically already contains the answer :)
Primefaces has its own dedicated servlet for file download and upload components that handle all of this asynchronously.
Try doing something like what I have in my code
<p:commandLink ajax="false" actionListener="#{managedBean.downloadAction(object)}">
<span class="ui-icon icoFolderGo" style="padding-right: 1.5em;" />
<p:fileDownload value="#{managedBean.downloadContentProperty}" />
</p:commandLink>
And in the managed bean,
public void downloadAction(Object object) {
try {
InputStream stream = // get input stream from argument
this.setDownloadContentProperty(new DefaultStreamedContent(stream, "application/pdf", "filename.pdf");
} catch (Exception e) {
log.error(e);
}
}
public void setDownloadContentProperty(StreamedContent downloadContentProperty) {
this.downloadContentProperty = downloadContentProperty;
}
public StreamedContent getDownloadContentProperty() {
return downloadContentProperty;
}

Resources