Invoke Navigation and FileDownload in JSF without JS - jsf

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>

Related

How to control the sequence of firing the java methods [duplicate]

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".

JSF 2.2 Post Request forward to another view through preRenderView event - PF resources are missing

Here is the scenario.
View A - Is loaded by typing a URL in the browser. Submit a post request by pressing a primefaces (PF - 5.2)command button and then navigate to another view View B.
View B - Has a preRenderView event - on postBack=false, which checks for a specific request parameter and if that is present forwards (not redirect) to another view C, using navigation handler.
View C - Problem is when this view loads up, Primefaces object (javascript object) is found as null, e.g. error as "Uncaught TypeError: Cannot read property 'cw' of null" against this script PrimeFaces.cw("CommandButton","widget_next3",{id:"next3",widgetVar:"widget_next3"});
View C has components related to PF, so i guess PF js and css should get added automatically.I didn't added any PF js or css or jquery js manually, all getting included automatically through PF component ResourceDependency. But in this case neither PF css gets included nor js.
-- Everything works fine on View C, if View B - preRenderView event navigates to View C with (redirect) faces-redirect=true.
In an error scenario if i check through jquery - it gives PF object as null.
<h:outputScript>
$( document ).ready(function() {
console.log( "ready!" );
console.log( "Windows PrimeFaces=" + (window.PrimeFaces) );
console.log( "PrimeFaces=" + (PrimeFaces) );
});
</h:outputScript>
But i am wondering why the problem is only with PF (js and css), why JQuery object "$" is working fine - because the above function works fine.
Also in the error scenario if i explicitly include the following then everything is fine
<h:outputScript name="primefaces.js" library="primefaces"/>
<h:outputStylesheet name="primefaces.css" library="primefaces" />
<h:outputStylesheet name="theme.css" library="primefaces-aristo" />
Thanks for looking at it.
Was this error preceded by the warn "Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/"?
I was facing the same error and found out that my browser (chrome) was not loading PrimeFaces scripts after the ajax warning. It's like after the warning, Chrome would set async=true by default and somehow mess up with primefaces javascript files.
In order to test, I added the following instruction in each view and solved the problem.
$(document).ready(function() {
$.ajaxPrefilter(function( options, originalOptions, jqXHR ) { options.async = false; })
});
I'm still trying to understand why Chrome is not loading the javascript files through an async request.

How do I ajax-update a component at an interval of 1 second?

I was asked this question in an interview at Dell.
new Date() gives you a Date object initialized with the current date / time.
Let suppose the right top corner of my screen shows the present time, and one has done like this.
<h:outputText value="#{bean.presentDateTime}" />
How will this be modified to always show the present date with time?
Basically, you want to hit the server and update the result at timed intervals. This is called "polling". Standard JSF has no builtin facilities for this, so you still need to write a bit of JS code yourself (or, theoretically, grab a JSF component which transparently renders the desired JS code for you). In JavaScript, you can use setInterval() to execute a function at intervals.
So, once you give the timestamp component a fixed ID,
<h:outputText id="presentDateTime" value="#{bean.presentDateTime}" />
and you supply the below hidden form whose command button ajax-updates it,
<h:form id="hiddenForm" style="display:none">
<h:commandButton id="updatePresentDateTime">
<f:ajax render=":presentDateTime" />
</h:commandButton>
</h:form>
then you can get it to run with the below script executed during DOM/window/body ready.
<script>
setInterval(function() {
document.getElementById("hiddenForm:updatePresentDateTime").click();
}, 1000);
</script>
Don't use it for this very purpose (a client side clock) in production though. It's at most OK as an answer to an interview question (or as an example in some showcase page). In production, better grab "pure" JS, if necessary in combination with a poll which runs at a reasonable longer interval, e.g. 1 minute. Or if your environment supports it, use push via WS or SSE.

Primefaces datatable custom pagination not updating

I am working on an application where we have our older code base (Struts 1) and are now building out a newer code base using JSF to run alongside the rest of the (old) application. The older datatables that we have across our application show the following for pagination:
"Showing 1 to 20 of 50 entries"
I have been tasked with getting the new PF datatable to display the same way.
I was able to get this working for the first page by overriding the encodePaginatorMarkup method of the primefaces DataRenderer class. The problem comes when the page is changed - this method is not being called to update the pagination. This is confusing to me because the current page (and everything else) gets updated in the normal pagination template without this method getting called.
I looked at the AJAX response being sent back when changing pages and don't see any HTML generated for updating the pagination (only the total count). This leads me to believe that everything is being handled in JS.
The way I'm currently thinking of implementing it would be to use an f:ajax for changing the page, and in the oncomplete pass the widgetVar value for the table and have a common method that will update the pagination based on properties of that datatable in JS. This seems like overkill and I don't want to over-complicate the code if I don't have to.
Is there any better way to go about this? Does anyone have any other suggestions?
Well since there is no other input I went ahead and implemented it using the page event:
<p:dataTable id="users" widgetVar="userTable" paginator="true" rows="20" sortBy="userId">
<p:ajax event="page" oncomplete="setTimeout(function() { update_pagination('userTable'); }, 0)" />
...
</p:dataTable>
The setTimeout was required because the JS object was still not updated when called from oncomplete - so I bumped it into the next event loop, where the values are up-to-date.
Here is the update_pagination JS function:
function update_pagination(table) {
table = PF(table);
var paginator = table.cfg.paginator;
var $pageStart = $(table.jq).find('.paginator_pagestart');
var $pageEnd = $(table.jq).find('.paginator_pageend');
$pageStart.html(paginator.page*paginator.rows+1);
$pageEnd.html(Math.min(paginator.rowCount, paginator.page*paginator.rows+paginator.rows));
}
I pass it the table name, grab the actual PF object and then use the paginator part of the cfg to update the current entries being shown. I use Math.min because on the last page it might not have enough data, and I didn't want it reading "Showing 1 to 20 of 7 entries", but rather "Showing 1 to 7 of 7 entries"

JSF 2.0: how to save the view after redirect (target=blank)

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.

Resources