open new page (navigate to new site) when selecting a dataTable row - java

There a several related question on this topic on SO and elsewhere, but I couldn't find a definitive answer on this specific question.
I have a p:dataTable and I want the possibility to click on a row and open a detail page (a new page, not a dialogue or window).
I have solved it this way (which I have from the primefaces website, for some reason it is no longer there: http://web.archive.org/web/20101001223235/http://www.primefaces.org/showcase/ui/datatableRowSelectionInstant.jsf):
<p:dataTable var="order" value="#{orderBean.orders}" selection="#{orderBean.selectedOrder}" selectionMode="single" rowKey="#{order.number}">
<p:ajax event="rowSelect" listener="#{orderBean.orderSelect}"/>
<p:column ... />
</p:dataTable>
The navigation is then executed in the bean:
public void orderSelect(SelectEvent event) {
ConfigurableNavigationHandler nh = (ConfigurableNavigationHandler)FacesContext.getCurrentInstance().getApplication().getNavigationHandler();
nh.performNavigation("orderDetail?faces-redirect=true");
}
My Question: is there a way of doing this just inside JSF without the help of a backing bean?
I am also asking because they removed the code exmaple from the primefaces site, which might be an indication that this is not the right way of doing something like that.

Wrap the cell(s) of interest in a simple <h:link>.
<p:column>
<h:link outcome="orderDetail">
...
</h:link>
</p:column>
Use if necessary CSS display:block on the link to let it span the entire cell. You can if necessary pass request parameters using a nested <f:param>.

since it is an ajax request, typically the request/response is used to re-render some components in the web page. What you could do is
<p:ajax event="someventofintrest" onsuccess="javascript:myjsmethod();"></p:ajax>
and
<p:remotecommand name="myjsmethod" action="#{mybean.mybeanmethod}" />
and in the backing bean
public String mybeanmethod(){
return "mynewpage"; // Navigate away to mynewpage.xhtml
}
HTH.

As I didn't find a really perfect solution, this is how I do it now.
I have now a "navigator" class like this
#Component
public class Navigator {
public void nav(String page) {
UIHelper.navigateTo(page);
}
}
And I call this class from my ajax event:
<p:ajax event="rowSelect" listener="#{navigator.nav('orderDetail')}"/>
As I said, not really perfect, but I like the fact that I don't have to write code in my backing bean. (Of course I have to write code for the Navigator, but that I can re-use.)

Related

Primefaces DataTable display only the last item

the Datatable is displaying only the last item added, it seems that every time i press the commandButton the method rewrites the list, what can I do to avoid that?
JSF
<p:commandButton action="#{productoBean.setPedidoActual()}" value="Agregar" update="dt"/>
</p:panelGrid>
<p:dataTable value="#{productoBean.pedidoActual}" var="pedi">
<p:column headerText="Nombre">
<h:outputText value="#{pedi.descripcion}"/>
</p:column>
</p:dataTable>
Bean
public void setPedidoActual() {
Producto pro = productoFacade.find(idProducto);
listPedidoActual.add(pro);
}
public List<Producto> getPedidoActual() {
return listPedidoActual;
}
Your setPedidoActual() method seems like to receive only one item based on the facade service find(idProducto), is that correct? Have you checked what the remote service returns? Probably there is in fact just one record.
Beside that, try to rename your setter/getter from
getPedidoActual() / setPedidoActual()
into
getListPedidoActual() setListPedidoActual()
Now your method names are consistent with your attribute.
EDIT: What happened to the panelGrid tag? Have you purposely omitted or where does it start?

Primefaces cannot update p:tree

I'm working with primefaces 4.0 and JSF 2.2 and I'm currently trying to update a form with a p:tree on it. The commandButton works correctly but it does not update the form or call the init() method until I manually refresh the page. I don't know what I'm doing wrong since the same code does work for a DataTable element.
Here's the code:
<h:form id="preferenciasForm">
<div id="panelTree">
<p:panel id="defTree" style="margin-bottom: 20px">
<p:tree value="#{dtPreferencesBuilder.root}" var="node"
selectionMode="checkbox"
selection="#{dtPreferencesBuilder.selectedNodes}"
style="width:100%; height:100%;" animate="true">
<p:treeNode>
<h:outputText value="#{node.label}" />
</p:treeNode>
</p:tree>
<p:commandButton value="Add preferences"
icon="ui-icon-pencil"
actionListener="#{dtPreferencesBuilder.insertPrefNodes()}"
update=":preferenciasForm" ajax="true" />
</p:panel>
</div>
</h:form>
And here's is the java class.
#ManagedBean(name="dtPreferencesBuilder")
#ViewScoped //I've tried with or without the ViewScoped, neither work
public class PreferencesBuilderBean {
private TreeNode root;
private TreeNode prefRoot;
private TreeNode[] selectedNodes;
#PostConstruct
public void init() {
System.out.println("Building Tree");
selectedNodes=null;
root=null;
prefRoot=null;
root=getStandardTree();
prefRoot=getPreferedTree();
}
The init() is not called as the print is only show on manual reload so the tree is not updated nor the selectedNodes refreshed. Any ideas why it doesn't work?
As I cannot describe bean scopes better than excellent answers already given for similar questions I'll just refer you to the answers by BalusC and Kishor P here.
The init-method (or any method with the #PostConstruct-annotation) will be called by the framework only when the bean is created, after injections and therefore after the constructor has run as rion18 said. It would not be normal to use the method for anything else than initializing work. So create other methods, and call those from actions and actionListeners.
If you want the bean to be the same when you call it with ajax (as you do) it needs to be at least ViewScoped. If you really do want to call the init() every time it should be RequestScoped, but then the bean will be new when you call it with ajax and not remember a thing.

Radio and Checkbox in primefaces datatable

I have to put radio and chechbox controls in primefaces's 2.2.1 datatable but not as a selection mode controls. I want to bind that controls as a values for the backing bean.
For example:
A have a list of some configurations and in that list only one configuration can be active at one time (radiobox) but every single configuration can be active or not (selectbox).
Datatable doesn't have to be editable, because I will change values in another window. Only controls should be in the datatable.
Is that possible?
Yes, it is definitely possible I do this all the time in my datatables. However, you need to keep in mind that each row will require identification. You can do this with objects or parameters to your listener:
//Backing Bean
#ViewScoped
public class TestBean
{
private ArrayList<Element> elements;
TestBean(){...}
public ArrayList<Element> getElements(){...} //Initialize things here
public class Element
{
/* getter/setter and initialization assumed */
private boolean selected;
private String radioSelection = "one";
private String[] radioChoices = new String[]{"one", "two", "three"};
public void selectMe(ActionEvent evt)
{
System.out.println("Selected -> " + this);
}
}
}
//HTML
<p:dataTable value="testBean.elements" var="element">
<p:column>
<p:commandButton value="Select" actionListener="#{element.selectMe}"
</p:column>
<p:column>
<h:selectBooleanCheckbox value="#{element.selected}" />
</p:column>
<p:column>
<h:selectOneRadio value="#{element.radioSelection}">
<f:selectItems
value="#{element.radioChoices}"
var="item"
itemLabel="#{item}"
itemValue="#{item}"
/>
</h:selectOneRadio>
</p:column>
</p:dataTable>
You can (and might want to) use a parameter using the ID from the element or whatever your preferred pattern is. The key is remembering that each row has its own id and you're creating a set of elements, not a single element. That's where you're most likely to get bound up. Seeing as how each button is a first class citizen you should be able to whatever you want with it.
Edit:
I added a Radio/Checkbox example. It sounds like you're new to JSF so I'd advise reading up on the SelectItem class and run through how combo-boxes etc... work. The key to remember (as I stress above) is that you're rendering a lot of components so if you share a value it can easily lead to behavior you don't want. Encapsulate everything and you're usually in good shape.) You can also look at bindings as a possible solution. It all depends on where you'd rather write your code.

JSF2.0 Partial Render with ui:define

I'm using facelet templating and I think I'm running into an issue with ui:define and the JSF lifecycle. My template.xhtml contains a fixed menu in the header, simplified it has links like this:
<h:commandLink value="Click Me">
<f:ajax update="#{myBean.listener}" render="contentpanel"/>
</h:commandLink>
The template.xhtml also contains a ui:insert statement:
<ui:insert name="content">
<h:outputLabel value="content placeholder"/>
</ui:insert>
Now I have a content.xhtml which looks like:
<ui:composition template="template.xhtml">
<ui:define name="content">
<h:panelGroup id="contentpanel"/>
</ui:define>
</ui:composition>
So much for the introduction. When I click the commandlink 'Click Me' I'm calling my listener. This listener sets a reference in a backingbean to dynamically load the content based on the link I clicked.
This rendering is not done the first time I press the commandlink. Well it basically looks like it first does the re-render and then call the listener instead of the other way around. So when I first click, nothing seems to happen. When I click for the second time I see the content that was set for the first click. When I click for the third time I see the content of the second click.
I think it's because the ui:define view is already re-built in the 'Restore View' phase of the JSF lifecycle. Any ideas on how to overcome this?
UPDATE
Seems like my assumption was wrong. The cause of this seems to be something different. The #{myBean.listener} has a #SessionScoped #ManagedProperty which is updated after the CommandLink is clicked. The contentpanel actually loads data via the #{myBean.data} which is #RequestScoped. This #{myBean.data} did not reload the data correctly. I solved it by passing the getData() method directly to the #SessionScoped bean.
Might be a bit confusing. But my conclusion: it does work to partial render a component which is loaded via facelet templating (ui:define / ui:insert)
Seems like my assumption was wrong. The cause of this seems to be something different. The #{myBean.listener} has a #SessionScoped #ManagedProperty which is updated after the CommandLink is clicked. The contentpanel actually loads data via the #{myBean.data} which is #RequestScoped. This #{myBean.data} did not reload the data correctly. I solved it by passing the getData() method directly to the #SessionScoped bean.
Might be a bit confusing. But my conclusion: it does work to partial render a component which is loaded via facelet templating (ui:define / ui:insert)

How to refresh a page after download

I have a commandButton that will invoke a function to download a file (standard stuffs like InputStream, BufferedOutputStream ...) After download success, at the end of the function, I change some values of the current object and persist it into database. All of these work correctly. Now when file is done downloading, the content of the page is not updated. I have to hit refresh for me to see updated content. Please help. Below are the basic structure of my code
document: Managed Bean
getDrawings(): method return a list of Drawing (entity class)
CheckedOutBy: attribute of Entity Drawing
<p:dataTable id="drawing_table" value="#{document.drawings}" var="item" >
<p:column>
<f:facet name="header">
<h:outputText value="CheckedOutBy"/>
</f:facet>
<h:outputText value="#{item.checkedOutBy}"/>
...
</p:dataTable>
<p:commandButton ajax="false" action="#{document.Download}" value="Download" />
Inside my Managed Bean
public void Download(){
Drawing drawing = getCurrentDrawing();
//Download drawing
drawing.setCheckedOutBy("Some Text");
sBean.merge(drawing); //Update "Some Text" into CheckedOutBy field
}
You'd basically like to let the client fire two requests. One to retrieve the download and other to refresh the new page. They cannot be done in a single HTTP request. Since the download needs to be taken place synchronously and there's no way to hook on complete of the download from the client side on, there are no clean JSF/JS/Ajax ways to update a component on complete of the download.
Your best JSF-bet with help of PrimeFaces is <p:poll>
<h:outputText id="checkedOutBy" value="#{item.checkedOutBy}"/>
...
<p:poll id="poll" interval="5" update="checkedOutBy" />
or <p:push>
<p:push onpublish="javaScriptFunctionWhichUpdatesCheckedOutBy" />
Polling is easy, but I can imagine that it adds unnecessary overhead. You cannot start it using standard JSF/PrimeFaces components when the synchronous download starts. But you can stop it to let it do a self-check on the rendered attribute. Pushing is technically the best solution, but tougher to get started with. PrimeFaces explains its use however nicely in chapter 6 of the User Guide.
Well, I decided to go with BalusC's answer/recommendation above, and decided to share my code here for people that may stop by here, 'later'. FYI, my environment details are below:
TomEE 1.6.0 SNAPSHOT (Tomcat 7.0.39), PrimeFaces 3.5 (PrimeFaces Push), Atmosphere 1.0.13 snapshot (1.0.12 is latest stable version)
First of all, i am using p:fileDownload with p:commandLink.
<p:commandLink value="Download" ajax="false"
actionListener="#{pf_ordersController.refreshDriverWorksheetsToDownload()}">
<p:fileDownload value="#{driverWorksheet.file}"/>
</p:commandLink>
Since I have the xhtml above, and since p:fileDownload does not allow oncomplete="someJavaScript()" to be executed, I decided to use PrimeFaces Push to push message to client, to trigger the javascript necessary to unblock the UI, because UI was being blocked whenever I click the commandLink to download file, and for many months, I didn't know how to solve this.
Since I already am using PrimeFaces Push, I had to tweak the following on the client side:
.js file; contains method that handles messages pushed from server to client
function handlePushedMessage(msg) {
/* refer to primefaces.js, growl widget,
* search for: show, renderMessage, e.detail
*
* sample msg below:
*
* {"data":{"summary":"","detail":"displayLoadingImage(false)","severity":"Info","rendered":false}}
*/
if (msg.detail.indexOf("displayLoadingImage(false)") != -1) {
displayLoadingImage(false);
}
else {
msg.severity = 'info';
growl.show([msg]);
}
}
index.xhtml; contains p:socket component (PrimeFaces Push); i recommend all of the following, if you're are implementing FacesMessage example of PrimeFaces Push
<h:outputScript library="primefaces" name="push/push.js" target="head" />
<p:growl id="pushedNotifications" for="socketForNotifications"
widgetVar="growl" globalOnly="false"
life="30000" showDetail="true" showSummary="true" escape="false"/>
<p:socket id="socketForNotifications" onMessage="handlePushedMessage"
widgetVar="socket"
channel="/#{pf_usersController.userPushChannelId}" />
Months (or maybe a year-or-so) ago, i found it necessary to add the following to the commandLink that wraps p:fileDownload, which will refresh the file/stream on server, so you can click the file as many times as you need and download the file again and again without refreshing the page via F5/refresh key on keyboard (or similar on mobile device)
actionListener="#{pf_ordersController.refreshDriverWorksheetsToDownload()}"
That bean method is referenced whenever enduser clicks the commandLink, to download file, so this was perfect spot to 'push' a message from server to client, to trigger javascript on client, to unblock UI.
Below are the bean methods in my app that gets the job done. :)
pf_ordersController.refreshDriverWorksheetsToDownload()
public String refreshDriverWorksheetsToDownload() {
String returnValue = prepareDriverWorksheetPrompt("download", false);
usersController.pushNotificationToUser("displayLoadingImage(false)");
return returnValue;
}
usersController.pushNotificationToUser(); i had to add this one, tonight.
public void pushNotificationToUser(String notification) {
applicationScopeBean.pushNotificationToUser(notification, user);
}
applicationScopeBean.pushNotificationToUser(); this already existed, no change to this method.
public void pushNotificationToUser(String msg, Users userPushingMessage) {
for (SessionInfo session : sessions) {
if (userPushingMessage != null &&
session.getUser().getUserName().equals(userPushingMessage.getUserName()) &&
session.getUser().getLastLoginDt().equals(userPushingMessage.getLastLoginDt())) {
PushContext pushContext = PushContextFactory.getDefault().getPushContext();
pushContext.push("/" + session.getPushChannelId(),
new FacesMessage(FacesMessage.SEVERITY_INFO, "", msg));
break;
}
}
}
You could use the update attribute of the p:commandButton component to re-render the area that you intend to refresh after download, in this case your "drawing_table".
<p:commandButton update="drawing_table" action="#{document.Download}" value="Download" />
As Balusc answered, we cannot get response twice from a single request.
To refresh a page after download, better use the following java script in download link(p:commandbutton) onclick tag.
Example:
<p:commandButton ajax="false" icon="ui-icon-arrowstop-1-s" onclick="setTimeout('location.reload();', 1000);" action="#{managedBean.downloadMethod}" />
this will automatically refresh the page after 1 second, at the same time i.e. before refresh, you will get the download file, based on your download response time, increase the seconds in that script. Seconds should not less than that download response time.

Categories