I am working on an application where users will be evaluating a piece of art and filling out a review form as they go along. They may spend a substantial amount of time on the form, so I want to save it automatically for the user, say every 5 minutes. I was hoping I could use javascript and set a timer for the 5 minutes and then have it basically execute the entire form via ajax and I could save the data to the database in case the user gets disconnected, etc. Unfortunately I cannot seem to find any way to do this. Getting the model updated with the data is not a problem, but I can't figure out how to get it to invoke the method (similar to how the action normally would when it was submitted.) I don't want or need it to re-render anything, just let me call a method to save the data. How can I do this?
Problems implementing the solution
I tried implementing the solution of the hidden command link but I'm getting some very strange behavior. I am not sure what is causing this. First, some background on the implementation. Form #1 creates a bean (None scoped) and puts it into the flash, then redirects to Form #2. Form #2 is the big form I was writing about, where I want to implement the auto-save. Form #2 has a ViewScoped bean. In the PostConstruct for this bean, it retrieves the value from flash, and populates a property field. So far so good. This works perfectly without the javascript. I can press the command button to submit the form, and all is well. However, when I introduce the javascript, when it executes I get a null pointer exception from the variable that should have been populated from the flash by the PostConstruct. How is this javascript interfering with that? Once I have populated the property of view scoped bean with the object, it should not matter if its removed from flash scope, right? FYI if I remove ONLY the javascript code and leave everything else it goes back to working fine when I press the button to submit.
Form #1
<h:form>
... bunch of form objects ...
<h:commandButton "Start New" action="#{someRequestScopedBean.someMethod"/>
</h:form>
code for someRequestScopedBean.Method:
public String someMethod() {
// bunch of logic here
FacesContext.getCurrentInstance()
.getExternalContext()
.getFlash()
.put("myFlashObj", myFlashObj);
return "form2?faces-redirect=true";
}
view scoped bean used in form 2:
#ManagedBean
#ViewScoped
public class someViewScopedBean {
//bunch of properties here
#PostConstruct
public void initialize() {
this.myObject = (MyObject) FacesContext.getCurrentInstance()
.getExternalContext()
.getFlash()
.get("myFlashObject");
public void saveDraft() {
// save to database
}
}
Form 2 page:
<h:outputScript library="javax.faces" name="jsf.js"/>
<h:form id="myForm">
... whole bunch of fields here ...
... real button for user to submit ...
<h:commandButton value="Submit myForm"
action="#{someViewScopedBean.save}" />
... hidden button for auto-save by javascript ...
<h:commandLink id="hiddenSaveDraft" style="display: none;"
action="#{someViewScopedBean.saveDraft}" >
<f:ajax execute="#form" />
</h:commandLink>
<script>
function saveDraft() {
document.getElementById('qForm:hiddenSaveDraft').onclick();
window.setTimeout('saveDraft()',15000);
}
saveDraft();
</script>
</h:form>
Since you wrote down Javascript as one of your tags i'm going to assume you incorporate Client side Javascript code in your app.
First of all use a hidden <h:commandButton/> or a hidden <h:inputText/> ( I use the latter in cases where I need to hold some information regarding a certain variable)
In the form you are submitting add one of these:
<h:commandButton style="display:none" id="clickme">
<f:ajax execute="#form">
</h:commandButton>
<h:inputText style="display:none" id="changeme">
<f:ajax execute="#form">
</h:inputText>
In your Javascript code add this code to either click or change:
setInterval(function() {
document.getElementById("clickme").onclick();
}, 3000); // update every 3 seconds
setInterval(function() {
document.getElementById("changeme").onchange();
}, 3000); // update every 3 seconds
Both will work fine. Just make sure they are within the form you are updating.
I figured out the final solution, and the cause of problems I had implementing it the first time around. It had to do with WHEN the javascript was being fired. The script code was being fired at the exact point I placed the <script></script> block, which was before the page was completely loaded and probably before the DOM was complete. This was causing all kinds of nasty, including duplicate invocations of #PostConstruct.
I fixed it by using a javascript event listener to fire when the page was completely loaded. This is important because I am using facelets templating, and I didn't have access to the <h:body onload= attribute. The listener is a useful and elegant solution. The script block can be placed anywhere in the page. Here is what the script block looks like:
<script>
function saveDraft() {
document.getElementById('qForm:saveDraft').onclick();
window.setTimeout(saveDraft,300000);
}
function initSaveTimer(e) {
window.setTimeout(saveDraft,300000);
}
this.addEventListener("load",initSaveTimer,true);
</script>
This will invoke the hidden button every 5 minutes to save the form.
Related
I am making webpage about ride sharing/selling bus tickets using JSF.
On one particular page, logged users can publish their ad (offering or looking for ride).
They should enter info like: ad title, origin city, destination city, departure time, etc.
What I want is, that users get weather forecast of destination city showed in popup after they hit submit.
This also means I am consuming service like openweathermap.
Basically, I have one h:form and h:commandButton like this:
<h:commandButton value="Publish" action="#{adBean.publishAd}" type="submit" onclick="showPopup()">
</h:commandButton>
The problem is that I don't know how to pass bean value of
<h:inputText value="#{adBean.ad.destinationCity}"/>
to JavaScript method showPopup().
I've tried this, but destinationCity turns out to be null.
<h:commandButton value="Publish" action="#{adBean.publishAd}" type="submit" onclick="showPopup(#{adBean.ad.destinationCity})">
</h:commandButton>
Why is this happening and how can I pass its value to JavaScript code?
It is null because it is put in an attribute (onclick) that is used for javascript. Attributes like these are 'rendered' at the time the page is displayed, not (again) at submission time or other moments. So if it is initialized null at that time, what is transferred from the server to the client is
<button value="Publish" ... onclick="showPopup(null)" />
A quote from #BalusC about this in How to invoke a bean action method using a link? The onclick does not work:
In JSF, only the attributes which interpret the EL expression as a MethodExpression can be used to declare action methods. All other attributes are interpreted as ValueExpression and they are immediately executed when the HTML output is generated by JSF. This covers the onclick attribute, whose value should actually represent a JavaScript function.
The (h:?)inputText on the client-side is rendered as a plain html inputText, so if you want access to its value before submitting (like I suspect you want to), you can do so in the showPopup like you would in plain html/javascript.
How to do this is mentioned in many stackoverflow Q/A so either pick one that suites you best e.g. How do I get the value of text input field using JavaScript?, but make sure to also read How can I know the id of a JSF component so I can use in Javascript
I' have seen many answers here but none worked for what I want, I need to do this in a way that the form is not submited, and by using the hidden button approach (with an ajax call) and calling the click() event from javascript, the method is called, but I need to pass an int in the method, how can I do that?
I can get the int from a javascript var or from a hidden input text field, just don't know how to do that, can anyone help with that?
JSF code:
<script type="text/javascript" language="javascript">
var position;
</script>
<h:inputHidden id="hiddenHolder" value="#{backingBean.beanPosition}" />
<h:commandButton id="hiddenButton" style="display: none;">
<f:ajax listener="#{backingBean.testMethod(need to send here javascript var or inputHidden value)}"/>
</h:commandButton>
Bean function;
public void testMethod(int i){
//do stuff
}
When I change the hiddenHolder value from javascript that is not reflected in the backingBean, I guess it needs a submit for that to work. That is why I need to pass the value in the method call.
When I change the hiddenHolder value from javascript that is not reflected in the backingBean
You're indeed not telling JSF to process the input value. The <f:ajax execute> should be used for this, which defaults to #this (the current input or command component). As the <f:ajax> is enclosed inside a command component, only the command component itself (its action) will be processed.
So, just explicitly specify the input value along with the command component:
<f:ajax execute="#this hiddenHolder" ... />
Or, if this all is within the same form, use #form:
<f:ajax execute="#form" ... />
See also:
How to invoke a JSF managed bean on a HTML DOM event using native JavaScript?
How to use jsf.ajax.request to manually send ajax request in JSF
Calling JSF managed bean method with arguments in jQuery
Use the OmniFaces commandScript for this. Works great (or use the PrimeFaces remoteCommand if you already use that)
To prevent the rest of the form being submitted, use the execute attribute like with an f:ajax call (process attribute on p:remoteCommand) or use separate forms
I have a web app written in angularjs that I want to send respective values from individual pages to a server via a GET request but I'm having it done automatically when the page loads.
A snippet of my code is below:
<script language="javascript">
function loaded(){
document.getElementById("dataSubmit").submit();
}
</script>
<form id="dataSubmit" action="http://127.0.0.1" method="get" target="submission.frame">
<input name="data" type="hidden" value={{myData}}>
<input type="submit" id="subbut" value="submit">
<iframe name="submission.frame" hidden></iframe>
</form>
<script>
window.onload = loaded();
</script>
My code above automatically sends the input data named "data" with value {{myData}} which, depends on what page the user is on, to the ip 127.0.0.1 and a script runs at that end and writes the data to a text file.
The problem is that when I try to pass the Angularjs expression {{myData}}, instead of passing the data fetched automatically when the page is loaded, it sends a string literal "{{myData}}" and writes that to the file at the other end.
However, when I click the 'submit' button that I've added to the form, the data sends perfectly and the value that I wanted is written to the file.
I tried using an onload function so that the submission only occurs after the page has loaded so the expression has had time to evaluate but still no luck.
For those wondering, my iframe tag makes it so that the submission form is invisible so when the submission occurs it does not redirect the user to the IP that the data is being sent to.
To clarify the problem, the angular expression {{myData}} is not being evaluated when the form is submitted upon page load, but when the submit button is clicked, {{myData}} evaluates and the value is sent. I need the value of {{myData}} to be submitted in the form when the page loads.
Any help would be greatly appreciated.
What you are facing is a pretty normal behavior, of course the page is finished loading but Angular didn't have time to evaluate the template interpolations.
To be honest I'm not sure what you are trying to achieve with that automated form submition, you can just use the $http service to send the data to whatever end-point you want. This way you know exactly when you have the data attached to the variables.
Tho if for some REALLY weird reason you still want to do the normal javascript form submition you need to create a directive in which you will include the $window service and add scome code looking like:
var autoSubmit = function () {
$scope.$watch("myDataVariable", function (newVal) {
if (newVal) {form.submit()}
})
}
$window.bind("onload", autoSubmit);
The code is not tested, it's a sample of how I would do it ... well I won't do something like that but you know what I mean.
In my JSF page I have a hidden form with an inputText, outputText and submit button. I'm using a script which on certain events fills the inputText and performs a click on the button. The button calls a method in a backing bean in which I need to do some actions and then set the value of the outputText. To do it I find the UIOutput component and set its value. In my javascript I need to perform some other action after the button is clicked but the problem is that it may take some time for the action to be completed and the outputText to be filled with the return value. So the next action in the javascript does not read the correct value. I thought of adding a change event on the outputText so to perform my action only after the value is updated but I have the same problem as this JS Events: hooking on value change event on text inputs.
So, if you were using an a4j:commandButton, you could just use the oncomplete attribute to some JS code that would execute after completing the AJAX request.
Since you are using a h:commandButton with an f:ajax, the way to do that is to use the f:ajax attribute onevent, and check if it succeeded like this:
<script>
function doWhatever(e){
if(e.status == 'success'){
doYourThingHere();
}
}
</script>
<h:commandButton action="#{someMB.someAction}">
<f:ajax ... onevent="doWhatever" />
</h:commandButton>
If you use Richfaces, you may want to take a look at a4j:ajax component, see this question.
UPDATE: removed incorrect else that assumed request didn't succeeded. See the answer to this question to see other meanings for the status attribute.
I am open to a different way to do this, but what I have seems like it should work from the documentation for RichFaces4 and JSF2.
The user flow is like this:
1) There is a 'Check Out' document link implemented with h:outputLink
2) The user clicks it and gets prompted with a dialog to enter check out comments implemented with rich:popupPanel
3) The user enters comments and clicks 'Continue' Button on the rich:popupPanel implemented with h:link (tried h:commandLink and a4j:commandLink also)
4) A new window pops up with the contents set to the h:link outcome attribute
In my broken case, everything works except when I pass a parameter from h:link
with a4j:param, whose value attribute does not resolve the javascript it points to correctly.
<h:outputLink id="promptForCommentsLink"
onclick="#{rich:component('commentsDlg')}.show();return false;"
value="#"> Check Out </h:outputLink>
<rich:popupPanel id="commentsDlg" modal="true">
<h:inputTextarea id="commentsId"/>
<h:link id="continueLink"
outcome="editorPage" <!-- editor for making changes to document -->
target="_blank" <!-- open in it;s own indow -->
value="Continue Check Out"
onclick="#{rich:component('commentsDlg')}.hide();">
<!-- these params get assignd to backing bean properties -->
<a4j:param name="dataId"
value="#{ithRow.id}" assignTo="#{myController.dataId}"/>
<a4j:param name="checkedOut"
value="true" assignTo="#{myController.checkedOut}"/>
<!-- this one is broken. assigns chars 'document.getElementById('..
to #{myController.checkOutComment} -->
<a4j:param name="checkOutComment"
assignTo="#{myController.checkOutComment}"
noEscape="true"
value="document.getElementById('myForm:dataTable:0:commentsId').value"
/>
</h:link>
</rich:popupPanel>
I was thinking maybe
document.getElementById('myForm:dataTable:0:commentsId').value
didn't point to what I typed into the textarea, but by putting another button on the dlg and pointing it's onclick to the same element id, it did indeed alert me with what it typed.
When I stop on the server side view scoped myController.setCheckOutComment(String s) method, it gets passed the string "document.getElementById('myForm:dataTable:0:commentsId').value"
According to RF4 documentation:
The a4j:param tag can be used with non-Ajax components in addition to Ajax components. This includes components which are working through the GET request, such as the h:link
and
Variables from JavaScript functions can be used for the value attribute. In such an implementation, the noEscape attribute should be set to true. Using noEscape="true", the value attribute can contain any JavaScript expression or JavaScript function invocation, and the result will be sent to the server as the value attribute.
Since I seem to be playing by the jsf/rf4 rules, I thought this would be okay.
One thing to note, if I use a4j:commandLink instead of h:link, it does indeed
send the result of javascript evaluated, however, that breaks the opening in its own window
and a few other issues.
Any thoughts on what might be happening, or even a better way to do this?
You could use a a4j:jsFunction with the parameters you need. Then call that function from the onclick in the h:link tag like setParams(#{ithRow.id}, true). Problem remain that you can't pass the value as a parameter to the javascript function. You could though use 'execute' to save the value of the inputArea to a backing bean and let the backend handle the value.
So yes, I would do it differently. I think you could handle the two other params at the backend and I would use 'execute' to store the value of the inputArea.
MAG,
Milo van der Zee