I've been working on CRUD application, and I need to export data from database to csv file.
In order to export, I had to disable ajax, in a manner shown in following code:
<p:commandButton value="Export" ajax="false" action="myController.export"/>
In the method invoked, I create the file and download it via OmniFaces utility method:
Faces.sendFile(file, true);
Using the same method, I check if there actually is any data, and if there isn't any data, warning dialog is shown:
RequestContext.getCurrentInstance().showMessageInDialog(new FacesMessage(FacesMessage.SEVERITY_WARN, "Warning!", "No available data for export."));
Now, while all of this does work as intended, the problem is that because ajax is disabled, dialog cannot be dynamically shown, and page is reloaded. If ajax is enabled, dialog is shown dynamically, but file download doesn't start.
I've been trying to work around this issue, by using Monitor download, or by force clicking second button, but so far I haven't made any progress on the matter.
Is there any generally acceptable way of solving issues like this one?
Is there any generally acceptable way of solving issues like this one?
You basically want to fire an ajax request first and in its oncomplete check if it's successful, and then trigger a synchronous request to download the file (note that you can't download files with ajax). You could make use of FacesContext#validationFailed() (or OmniFaces Faces.validationFailed()) to mark the validation fail state which is available via args object which PrimeFaces injects in the oncomplete function context (note that ajax related attributes such as oncomplete don't work when ajax is disabled).
Something like this:
<p:commandButton
value="Export"
action="#{bean.export}"
oncomplete="if (args && !args.validationFailed) PF('download').jq.click()" />
<p:commandButton
widgetVar="download"
styleClass="ui-helper-hidden"
action="#{bean.download}" ajax="false" />
public void export() {
// Prepare file locally.
if (fail) {
// Show message your way and then set validation failed as below.
Faces.validationFailed();
}
}
public void download() throws IOException {
Faces.sendFile(file, true);
// Delete local file afterwards?
}
Note that a hidden <p:commandButton> is being used instead of <p:remoteCommand> as the latter doesn't support ajax="false".
Related
Im currently looking for a solution to invoke a pagenavigation from "foo.xhtml" to "bar.xhtml" and starting a download dialogue at the same time. I already implemented a solution which worked on my test tomcat, but the JavaScript got cut out on the targetplatform, a WAS 8.0.0.9.
<c:if test="#{Bean.downloadPreconditions()}"> <!-- checks a simple boolean variable which gets set to 'true' after all informations for the download are set-->
<script>
window.onload = function() {
document.getElementById('form:download').onclick();
}
</script>
<h:commandLink id="download"
action="PrimeFaces.monitorDownload(start, stop);">
<p:fileDownload value="#{Bean.downloadFile}"></p:fileDownload>
</h:commandLink>
In this solution i start the download via JavaScript AFTER i redirected to the targetpage "bar.xhtml" from "foo.xhtml".
The PreRenderView solution does not work, because i visited the view before and it does not get freshly instantiated.
I tried several slightly different PrimeFaces.monitorDownload(start, stop); version with an attached <p:fileDownload>, as well as a download invoked by a backingbean like it is described here.
I am aware that im trying to call 2 requests to the server with one single click. I want to know if it is still somehow possible to maybe first switch to the target view and after that call the download dialogue in an automated way.
So, i still could not figure out why my JavaScript did not get executed properly, but i found a workaround:
Instead of calling the commandLink via proper JS at runtime, (Also note here: the commandLink needs to be defined above the JS, or as in my example a commandLink with the functionality already exists somewhere else on this site) I just invoked the link with the hardcoded primefaces syntax:
<c:if test="#{Bean.downloadPreconditions()}"> <!-- checks a simple boolean variable which gets set to 'true' after all informations for the download are set-->
<script>
window.onload = function() {
var firstDownlink = document.getElementById("form:DownloadLink"); //this refers to a link, that already exists on my page, alternativly just create one but do not apply any visual effects to it so it stays hidden
firstDownlink.onclick.apply(firstDownlink);
}
</script>
</c:if>
I have a picklist component (Primefaces 6.x ) in one of my pages. The picklist has a "transfer" event listener as shown in the showcase:
<p:ajax event="transfer" listener="#{pickListView.onTransfer}" update="msg" />
This works pretty well and my method is called every time i add or remove items from the target list.
My question is if it is possible to trigger the event handler only when adding items in the target list or if there is a way to catch the remove from target action in my method and ignore all validations that i am doing in bean method.
There is an advantage to source being open and having a good IDE for code completion. Both can show that the TransferEvent.java has isAdd() and isRemove() methods. These can easily be used to differentiate in an eventHandler which one is actually used.
public void onTransfer(TransferEvent event) {
if (event.isAdd()) {
// Do actual work
}
}
p:terminal does not have a update attribute.
Does it support any kind of ajax event?
how do I update another jsf component as a response to a terminal command?
It does not have any ajax events as can be seen in the documentation and/or the source. But in the implementation of commands for the terminal that you implement yourself, you can do anything you want. Including using the RequestContext to update other components.
At least that is what I expected (and did not explicitly test, sorry). Updating from the commandHandler does not work since the commandHandler is called during the render response phase and you cannot add updates to components in that phase.
See also the comment on the answer here: Can I update a JSF component from a JSF backing bean method?.
So the actual good answer is the one from the #Leo himself (although this answer helped a little ;-))
Here what worked for me
<p:terminal
id="terminal"
widgetVar="term"
prompt="Lab >> "
commandHandler="#{labMB.handleCommand}"
welcomeMessage="Welcome to Lab" />
<p:remoteCommand
name="rc"
update="history" />
<p:dataTable
id="history"(...)
and
public String handleCommand(String command, String[] params) {
RequestContext context = RequestContext.getCurrentInstance();
//(do things here)
context.execute("rc();");
}
it seems that just calling
context.update("form:history");
I'm using PrimeFaces version 3.5.
I want to implement the message informing that the filter in the DataTable have return too many rows and only first 50 will be displayed.
I've tried to do it with both Messages and outputText. I set the text to display and request update:
RequestContext context = RequestContext.getCurrentInstance();
context.update("form:message");
context.update("form:text");
message = "Too many rows";
The components:
<p:message id="message" for="message"/>
<h:outputText id="text" value="#{userPicker.model.message}"/>
However, I don's see the proper update sections in partial-request response. Is it possible to request the update of components from within the LazyDataModel.load function (and if, what I'm doing wrong here)? If it is not possible (why?) how else can I force refresh of element from DataTable filtering?
The load method of LazyDataModel is invoked in render phase, when it's too late to add additional components to update.
However, it is possible to add JavaScript execution command:
context.execute("show_message()");
Where function show_message will display the message on client side, which enables to accomplish the task with single request.
Imagine I have the jsf form and the button to print report on form data. The button needs to open the data on the new page (target=blank), but h:button doesn't suit as I need to save the data just before the report page open. So, I use h:commandButton which makes the save action, and then redirects to the report page:
<h:commandLink styleClass="reportButton" action="#{polisBean.doReportPrint}"
target="_blank" id="reportListLink">
Print report
</h:commandLink>
#ViewScoped
#Named
public class PolisBean
...
public Object doReportPrint() {
if (canEdit() && this.submit() == null) {
return null;
}
return "printReport";
}
<navigation-case>
<from-outcome>printReport</from-outcome>
<to-view-id>/polises/reportList.xhtml</to-view-id>
<redirect include-view-params="true">
<view-param>
<name>id</name>
<value>#{polis.id}</value>
</view-param>
</redirect>
</navigation-case>
The entity is saved perfectly and the new page is open with the report. Good. But when I go back to the initial form page and try to save again - the view is expired and built by new (as this is not a postback request, this is a totally new request for JSF, because I just made the redirect from this page!). Although the redirect was done while opening second page, this doesn't prevent from loosing all view scoped bean data..
I tried the long-running conversationScope from CDI, but it throws the "ContextNotActiveException: WELD-001303 No active contexts for scope type javax.enterprise.context.ConversationScoped" exception when I turn back to the initial page...
Has anyone solved the problem? How can I do the form submit with the redirect (to new page) but not loose the initial data?
If anyone is interested, I found the workaround. Not very beautiful, but allows to work with redirects to "blank" pages:
The receipt is to do the refresh of the initial page just after the link was opened in new window (see onclick):
<h:commandLink styleClass="reportButton" action="#{polisBean.doReportPrint()}" onclick="window.setTimeout(refreshPage, 2000)"
target="_blank" id="reportListLink" >
print report
</h:commandLink>
But that's not as simple as it seems to be, I cannot use the simple location.reload() or smth similar. In the doReportPrint() method I do the save operation and if the entity does not exist, it is created. As I use GET-parametrized request for my application, I will need to refresh page using the following address with id parameter:
/polises/polis.jsf?id=80
So, the the new jsf request needs to be done, not a simple JS refresh, which contains the id of newly created entity. So I use this approach:
In doReportPrint I save the newly created id to the session:
public Object doReportPrint() {
if (canEdit() && this.submit() == null) {
return null;
}
context().getExternalContext().getSessionMap().put("justUpdatedPolisId", entity.getId());
return "printReport";
}
And the refresh of the initial page is done the following way:
public Object doRefresh() {
Object justUpdatedPolisId = context().getExternalContext().getSessionMap().get("justUpdatedPolisId");
if (justUpdatedPolisId != null) {
entity.setId((Long)justUpdatedPolisId);
context().getExternalContext().getSessionMap().remove("justUpdatedPolisId");
return "save";
}
// If for some reasons we have not found it - moving to polises table
return "polises?faces-redirect=true";
}
Save outcome results in polis.jsf reopen (using faces-config navigation) and the id is attached from entity.id automatically
Another trick is how to call the JSF action from JS. I tried to imitate h:commandLink click (just copied the generated JS code for the link), but this didn't work, as the view is recreated and the same for bean and its properties, actions simply wasn't called. So I used the JS code for PrimeFaces p:commandLink and it worked great for me (dont' forget to set "update" to #none and "process" to #this):
<script type="text/javascript">
function refreshPage() {
PrimeFaces.ajax.AjaxRequest('/KaskoCalculator/polises/polis.jsf',{formId:'polisForm',async:false,global:true,source:'polisForm:doRefresh',process:'polisForm:doRefresh',update:'#none'});
}
</script>
So now the initial page is being refreshed in 2 seconds after the report being saved and opened in new windows. Too ugly and too burdensome, but it works for me. May be will help anyone else.
I am facing the same issue.
Using p:commandLink, or h:commandLink with target=_blank, the initial page backing bean data is gone after opening the new window.
The refresh will go back to the initial state of the page without ajax updates. but I need to keep the same state before clicking the print command.