Render or update a page while still running inside a method - jsf-2

I was wondering if you could render or update a page while you still inside a method;
here is an example:
the .xhtml file:
<h:form id="form">
<h:commandButton value="change" actionListener="#{bean.changeText}">
<f:ajax render="out" />
</h:commandButton>
<h:outputLabel id="out" value="#{bean.text}" />
</h:form>
and here is the changeText Method:
public void changeText() throws InterruptedException{
text = "test1";
FacesContext.getCurrentInstance().renderResponse();
Thread.sleep(2000);
text = "test2";
}
Now is it possible to set the output label (id = out) to be rendered (or changed) to "test1", then after the 2 second have elapsed it changes to "test2"?
Well looking at the JSF life cycle i thought it isnt possible, but maybe i got the life cycle wrong, or one of you guys know a work around.

No, that's not possible. The render response only starts once the action method returns.
There are basically 2 ways to achieve the desired behavior: polling or pushing. Based on your question history, you're using Java EE 7 + JSF 2.2 + PrimeFaces. You could for pushing use PrimeFaces <p:socket> or homegrow one using Java EE 7's new WebSocket API (JSR-356).

Related

Dynamically add Primefaces Hotkey control

I am building an app where one of the requirements is heavy use of hotkeys. So heavy in fact that we want to pull those hotkeys back into a central controller.
The basic idea is each page will have a key associated with it. When jsf hits that key, it will go back to a central enummap to decide what hotkeys are supposed to be associated with that page and what methods are supposed to be added to the keys of that page.
My problem is the dynamic load. I can't use the
For now, I have this
<h:form binding="#{hotkeyController.form}">
<f:event listener="#{hotkeyController.genKeyHandler('HOTKETEST')}" type="preRenderComponent"/>
<p:outputPanel id="displayHotkey">
<h:outputText value="#{hotkeyController.keyText}" rendered="#{not empty hotkeyController.keyText}"/>
</p:outputPanel>
</h:form>
Then for my backing bean i have this
public void genKeyHandler(String pageKey) {
Hotkey hotkey = new Hotkey();
hotkey.setBind("ctl+shift+d");
hotkey.setAsync(true);
// "#{hotkeyController.keyHandler('HOTKETEST', 'F1')}"
hotkey.setActionExpression(createMethodExpression("#{hotkeyController.keyHandler('HOTKEYEXAMPLE', 'CTLSHIFTD')}", null,
String.class, String.class));
hotkey.setUpdate("displayHotkey");
form.getChildren().add(hotkey);
hotkey = new Hotkey();
hotkey.setBind("ctl+shift+a");
hotkey.setAsync(true);
// "#{hotkeyController.keyHandler('HOTKETEST', 'F1')}"
hotkey.setActionExpression(createMethodExpression("#{hotkeyController.keyHandler('HOTKEYEXAMPLE', 'CTLSHIFTA')}", null,
String.class, String.class));
hotkey.setUpdate("displayHotkey");
form.getChildren().add(hotkey);
}
Now this works in the sense that it actually inserts the hotkey into the form. I can see it in inspector. But, my problem is two fold. One, if I refresh the page, I get duplicate ID errors, if I use the preRenderView, I get "Error restoring component" errors. seems like no combination will work.
Is there any way to make this work? All I want is to have the bean method that inserts the hotkeys fire the first time a page is loaded and only fired once in the life of the page/view/whatever. This shouldn't be this hard.
My suggestion is that you use an exclusive form for the hotkeys.
Here is the sample:
<h:form id="mainForm">
<p:remoteCommand name="loadHotKeys" action="#{hotkeyController.genKeyHandler('HOTKETEST')}" update=":hotkeyForm" global="false" process="#this" />
<p:outputPanel id="displayHotkey">
<h:outputText value="#{hotkeyController.keyText}" rendered="#{not empty hotkeyController.keyText}"/>
</p:outputPanel>
</h:form>
<h:form id="hotkeyForm" binding="#{hotkeyController.form}" />
<script type="text/javascript">
jQuery(document).ready(loadForm());
</script>
And the genKeyHandler method should clear the hotkeyForm (form.getChildren().clear()) before adding the hotkeys.

JSF ViewState not updated when submitting 2 forms

I have a though issue with JSF 2. I use Mojarra 2.1.14 with Primefaces 3.1.4
I have a page with 2 forms : formA and formB. The two forms contains each the ViewState in an hidden input field.
<h:form id="formA" binding="#{sessionBean.formA}">
<h:commandButton value="formA" action="#{sessionBean.actionA}">
<f:ajax/>
</h:commandButton>
</h:form>
<h:form id="formB" binding="#{sessionBean.formB}">
<h:commandButton value="formB" action="#{sessionBean.actionB}">
<f:ajax/>
</h:commandButton>
</h:form>
User submits formA with an Ajax action. Inside the Java action I update explicitly formA and formB (which are binded).
public void actionA(){
FacesContext.getCurrentInstance().getPartialViewContext().getRenderIds().add(formA.getClientId());
FacesContext.getCurrentInstance().getPartialViewContext().getRenderIds().add(formB.getClientId());
System.out.println("action A!!");
}
public void actionB(){
System.out.println("action B!!");
}
In the Ajax responses there is the HTML code for formA and formB ( element) and the ViewState.
JSF updates the HTML of formA and formB and set the ViewState of the calling form : formA. formB do not contains any ViewState.
User submit formB with an Ajax action. Because ViewState is not defined, postBack is false and the renderResponse is set to true in the RESTORE phase, skipping the INVOKE APPLICATION phase: the action is not called. After the response VIEW_STATE is updated and if user sumbit formB, the action is called.
Is it a JSF 2 bug or limitation? Or Do I do something wrong ?
You can find a maven projet on GitHub:
https://github.com/nithril/jsf-multiple-form
Thanks in advance for your help!
Problem you are facing is connected with known issue with JSF JavaScript library. Workaround is to explicitly set client id of another form in rendered attribute of f:ajax tag:
<h:form id="formA" binding="#{sessionBean.formA}">
<h:commandButton value="formA" action="#{sessionBean.actionA}">
<f:ajax render=":formB #form"/>
</h:commandButton>
</h:form>
<h:form id="formB" binding="#{sessionBean.formB}">
<h:commandButton value="formB" action="#{sessionBean.actionB}">
<f:ajax render=":formA #form"/>
</h:commandButton>
</h:form>
More about this:
Ajax rendering of content which contains another form
javax.faces.ViewState is missing after ajax render
Rendering other form by ajax causes its view state to be lost, how do I add this back?
There is an alternative trick if you use MyFaces 2.0.x / 2.1.x that will update the forms correctly, adding the following script:
window.myfaces = window.myfaces || {};
myfaces.config = myfaces.config || {};
//set the config part
myfaces.config.no_portlet_env = true;
See JSF Ajax and Multiple Forms.
This option is a lot better because you don't need to worry about fix every place in your webapp where you use multiple forms, just add it to the main template and that's it.

How to find indication of a Validation error (required="true") while doing ajax command

I have a form inside a dialog which I close by clicking on commandbutton with ajax ,
like this
<h:commandButton value="Add" action="#{myBean.addSomething(false)}"
id="add_something_id" >
<f:ajax render="#form someTable" execute="#form"
onevent="closeAddNewSomethingDialogIfSucceeded"></f:ajax>
</h:commandButton>
and here is the js code for closing the dialog
function closeAddNewSomethingDialogIfSucceeded(data) {
if(data.status === 'success') {
$("#dialog_id").dialog("close");
}
}
No problems till here...
Now I changed some of the dialog form fields into required="true" and now I want to prevent the closing of the dialog of i got validation errors...
But the ajax data.status still reaches its success state , and I can't figure out what indication of validation failure I can hook on...
any ideas?
Thanks to BalusC answer I did the following:
in JSF , added :
<h:panelGroup id="global_flag_validation_failed_render">
<h:outputText id="global_flag_validation_failed" value="true"
rendered="#{facesContext.validationFailed}"/>
</h:panelGroup>
the f:ajax was changed into
<f:ajax render="#form someTable global_flag_validation_failed_render"
and in js added the following check
if(data.status === 'success') {
if($("#global_flag_validation_failed").length === 0){
$("#dialog_id").dialog("close");
}
}
Not specifically for required="true", but you can check by #{facesContext.validationFailed} if validation has failed in general. If you combine this with checking if the button in question is pressed by #{not empty param[buttonClientId]}, then you can put it together in the rendered attribute of the <h:outputScript> as follows:
<h:commandButton id="add_something_id" binding="#{add}" value="Add" action="#{myBean.addSomething(false)}">
<f:ajax execute="#form" render="#form someTable" />
</h:commandButton>
<h:outputScript rendered="#{not empty param[add.clientId] and not facesContext.validationFailed}">
$("#dialog_id").dialog("close");
</h:outputScript>
(note that you need to make sure that the script is also re-rendered by f:ajax)
A bit hacky, but it's not possible to handle it in the onevent function as the standard JSF implementation doesn't provide any information about the validation status in the ajax response.
If you happen to use RichFaces, then you could just use EL in the oncomplete attribute of the <a4j:xxx> command button/link. They are namely evaluated on a per-request basis instead of on a per-view basis as in standard JSF and PrimeFaces:
<a4j:commandButton ... oncomplete="if (#{!facesContext.validationFailed}) $('#dialog_id').dialog('close')" />
Or if you happen to use PrimeFaces, then you could take advantage of the fact that PrimeFaces extends the ajax response with an additional args.validationFailed attribute which is injected straight in the JavaScript scope of the oncomplete attribute:
<p:commandButton ... oncomplete="if (args && !args.validationFailed) $('#dialog_id').dialog('close')" />
(note that & is been used instead of &, because & is a special character in XML/XHTML)
Or you could use the PrimeFaces' RequestContext API in the bean's action method to programmatically execute JavaScript in the rendered view.
RequestContext.getCurrentInstance().execute("$('#dialog_id').dialog('close')");
No conditional checks are necessary as the bean's action method won't be invoked anyway when the validation has failed.
Two things
1) Checking for an error in the 'onevent' function
Surely you have a message tag for the mandatory field?
<h:message id="m-my-field-id" for="my-field-id" errorClass="error-class"/>
So you can check for the error-class something like
var message = $('#m-my-field-id');
if(message.hasClass('error-class'){
//do this
}
else{
//do that
}
2) The DOM isn't up to date on success
Yes, I can see the message on the page in Firefox, yet jQuery tells me it is not there.
I have found that using the smallest possible timeout is sufficient to fix this
setTimeout(
function(){
setErrorStyle(source.attr('id'));
},
1
);
I think you should take a look at PrimeFaces' RequestContext. This would help you trigger client-side code on the server side.
#BalusC
in your example code the clientId of the button is not set as a param because it is a AJAX request. So
not empty param[add.clientId] is always false.
But this works:
param['javax.faces.source'] eq add.clientId
(tested with jsf-impl-2.2.12.redhat-1)
regards

Get id of parent naming container in template for in render / update attribute

I have a template and in its Definition I use several forms and buttons.
The problem is the definition (define) xhtml file does not know the component hierarchy.
And for example I want to update the element "table2" in a different form in the same define file.
Template Insert:
<p:tabView id="nav"> <!-- nav -->
<ui:insert name="content_nav">content navigation</ui:insert>
</p:tabView>
defines the first level of my hierarchy "nav"
Template define:
<ui:define name="content_nav">
<h:form id="form1"> <!-- nav:form1 -->
<h:dataTable id="table1"/> <!-- nav:form1:table1 -->
<p:inputText value="#{bean.value}"/>
<p:commandButton action="..." update="nav:form2:table2"/>
</h:form>
<h:form id="form2">
<h:dataTable id="table2"/> <!-- nav:form2:table2 -->
<!-- other elements -->
</h:form>
</ui:define>
In my define part I don't want to know "nav"!
How can I do this? or how can I move one naming component upwards?, or save the highest parent complete id in a variable?
sometimes i saw something like:
update=":table2"
But I could not find any informations about this?, the JavaEE 6 documentation just mentions the # keywords.
Ugly, but this should work out for you:
<p:commandButton action="..." update=":#{component.namingContainer.parent.namingContainer.clientId}:form2:table2" />
As you're already using PrimeFaces, an alternative is to use #{p:component(componentId)}, this helper function scans the entire view root for a component with the given ID and then returns its client ID:
<p:commandButton action="..." update=":#{p:component('table2')}" />
ugly answer works well
update=":#{component.namingContainer.parent.namingContainer.clientId}:form2:table2
mainly more useful updating from opened dialog to parent datatable
You may use binding attribute to declare EL variable bound to JSF component. Then you may access absolute client id of this component by using javax.faces.component.UIComponent.getClientId(). See example below:
<t:selectOneRadio
id="yourId"
layout="spread"
value="#{yourBean.value}"
binding="#{yourIdComponent}">
<f:selectItems value="#{someBean.values}" />
</t:selectOneRadio>
<h:outputText>
<t:radio for=":#{yourIdComponent.clientId}" index="0" />
</h:outputText>
Try this:
<h:commandButton value="Click me">
<f:ajax event="click" render="table" />
</h:commandButton>
Additionally to the solutions above I had the problem, that I had to dynamically generate the to-be-updated components (many) based on server-side logic (with maybe harder to find out nesting).
So the solution on the server-side is an equivalent to update=":#{p:component('table2')}"1 which uses org.primefaces.util.ComponentUtils.findComponentClientId( String designId ):
// UiPnlSubId is an enum containing all the ids used within the webapp xhtml.
// It could easily be substituted by a string list or similar.
public static String getCompListSpaced( List< UiPnlSubId > compIds ) {
if ( compIds == null || compIds.isEmpty() )
return "" ;
StringBuffer sb = new StringBuffer( ":" ) ;
for ( UiPnlSubId cid : compIds )
sb.append( ComponentUtils.findComponentClientId( cid.name() ) ).append( " " ) ;
return sb.deleteCharAt( sb.length() - 1 ).toString() ; // delete suffixed space
}
called via some other method using it, e.g. like ... update="#{foo.getCompListComputed( 'triggeringCompId' )}".
1: first I tried without too much thinking to return public static String getCompListSpaced0() { return ":#{p:component('table2')}" ; } in an ... update="#{foo.getCompListSpaced0()} expression, which of course (after thinking about how the framework works :) ) is not resolved (returned as is) and may cause the issues with it some users experienced. Also my Eclipse / JBoss Tools environment suggested to write :#{p.component('table2')} ("." instead of ":") which did not help - of course.

JSF field highlighting with ajax posts?

I'm building on BalusC's solution to highlight and focus fields in JSF. My plan is to output a JSON array with ids and then have a method be called which will process this array. This works fine when I don't use <f:ajax/>
Here is my phase listener solution:
public void beforePhase(PhaseEvent event) {
FacesContext facesContext = event.getFacesContext();
List<String> highlightFields = new ArrayList<String>();
Iterator<String> highlightFieldsItr = facesContext
.getClientIdsWithMessages();
while (highlightFieldsItr.hasNext()) {
StringBuilder sb = new StringBuilder();
sb.append("#");
sb.append(highlightFieldsItr.next().replaceAll(":", "\\\\:"));
highlightFields.add(sb.toString());
}
JSONArray jsonHighlightFields = new JSONArray(highlightFields);
facesContext.getExternalContext().getRequestMap()
.put("errorFields", jsonHighlightFields.toString());
}
Basically this would produce errorFields value with something like ["#some\:id1", "#some\id2"]. Then I can do something like this in my root layout file:
<script>
var errorFields = ${errorFields}; // This will xlate to ["#some\\:id1", "#some\\:id2"
$(document).ready(function(){
processInputErrors(errorFields);
});
</script>
With a processInputErrors function like this:
function processInputErrors(ids) {
for (id in ids) {
if (focus == false) {
jQuery(ids[id]).focus();
focus = true;
}
jQuery(ids[id]).addClass('input-error');
}
}
However, I need to somehow obtain this list in the function which gets called on success of an ajax post.
Now f:ajax does have the onevent attribute and this function does get called, but I'm not sure exactly what it gets passed. How would I be able somehow pass the invalid Ids from the phase listener to this function? It seems to be passed an object which represents the HTMLInputElement?
<f:ajax event="change" onevent="test" render="test test_msg" immediate="true" />
Happy to hear about alternative suggestions or ideas. The goal is basically to focus and highlight the field(s) which are invalid not only on a full post-back but also when using f:ajax.
Thanks!
That article was more targeted on JSF 1.x. JSF 2.x offers now a lot of advantages of which the following are beneficial for your particular requirement:
You can refer the current component in EL by #{component}. In case of input components this is the UIInput which in turn has an isValid() method. This could be used in styleClass attribute.
You can use <f:ajax> to re-render parts of the view, also <script> elements.
1+1 is...
<h:inputText id="input1" value="#{bean.input1}" required="true" styleClass="#{component.valid ? '' : 'error'}">
<f:ajax render="#this input1message focus" />
</h:inputText>
<h:message id="input1message" for="input1" />
...
<h:inputText id="input2" value="#{bean.input2}" required="true" styleClass="#{component.valid ? '' : 'error'}">
<f:ajax render="#this input2message focus" />
</h:inputText>
<h:message id="input2message" for="input2" />
...
<h:panelGroup id="focus"><script>jQuery(":input.error:first").focus();</script></h:panelGroup>
No need for a PhaseListener anymore. You could if necessary wrap the input markup boilerplate in a composite component or a tag file.
Back to your concrete question about the onevent attribute, check this answer: JSF 2: How show different ajax status in same input?

Resources