How to trigger JSF bean method on some JavaScript event, e.g. drop event [duplicate] - jsf

I need to execute a JSF managed bean action method using ajax during HTML DOM load event, similar to jQuery's $(document).ready(function() { $.ajax(...) }). I can only use the JavaScript generated by JSF in this project. Is there a way to do it in native JSF? Which event can I use or which JSF ajax function can I use?
I'm using JSF 2.0, Facelets and PrimeFaces.

Several ways.
Use <h:commandScript>. Note that this is only available since JSF 2.3.
<h:form>
<h:commandScript name="commandName" action="#{bean.action}" render=":results" />
</h:form>
<h:panelGroup id="results">
...
</h:panelGroup>
You can invoke it in JS as below:
commandName();
The parameters can be passed as below:
commandName({ name1: "value1", name2: "value2" });
And obtained as below:
String name1 = externalContext.getRequestParameterMap().get("name1"); // value1
String name2 = externalContext.getRequestParameterMap().get("name2"); // value2
To invoke it during load event, set autorun="true".
<h:commandScript ... autorun="true" />
If you're using PrimeFaces, use its <p:remoteCommand>.
<h:form>
<p:remoteCommand name="commandName" action="#{bean.action}" update=":results" />
</h:form>
<h:panelGroup id="results">
...
</h:panelGroup>
You can invoke it in JS as below:
commandName();
This however doesn't use JSF native jsf.ajax.request(), instead it uses PrimeFaces native jQuery (you know, PrimeFaces is a JSF component library on top of jQuery/UI).
The parameters can be passed as below:
commandName([{ name: "name1", value: "value1" }, { name: "name2", value: "value2" }]);
And obtained as below:
String name1 = externalContext.getRequestParameterMap().get("name1"); // value1
String name2 = externalContext.getRequestParameterMap().get("name2"); // value2
To invoke it during load event, set autoRun="true".
<p:remoteCommand ... autoRun="true" />
If you're using OmniFaces, use its <o:commandScript>. The usage is exactly the same as with <h:commandScript> but then available for older JSF 2.x versions.
Simply replace h: by o: in the first example. Historical note: the <h:commandScript> is entirely based off <o:commandScript>.
Use the "hidden form" trick (actually, "hack" is given the ugliness a better wording).
<h:form id="form" style="display:none;">
<h:commandButton id="button" action="#{bean.action}">
<f:ajax render=":results" />
</h:commandButton>
</h:form>
<h:panelGroup id="results">
...
</h:panelGroup>
You can invoke it in JS as below:
document.getElementById("form:button").onclick();
Note the importance of triggering onclick() instead of click(). The onclick() immediately invokes the generated onclick function, while the click() basically only simulates the "click" action on the element, as if a mouse is used, which is not supported in IE, and in some browsers also require the element being actually interactable (i.e. display:none can't be used then).
You can pass parameters via <h:inputHidden> in same form which you fill by JS beforehand. This is demonstrated in How to pass JavaScript variables as parameters to JSF action method?
To invoke it during load event, consider putting it in <h:outputScript target="body">. The target="body" automatically puts the <script> in end of <body>, thus a $(document).ready() wrapper is unnecessary.
<h:outputScript target="body">
document.getElementById("form:button").onclick();
</h:outputScript>
Or, create a custom UIComponent which extends UICommand and generates the necessary JSF native jsf.ajax.request() call. As an example you could look at source code of OmniFaces <o:commandScript>.

Related

Conditionally rendering an <ui:include>

I am trying to toggle a page that shows a <rich:dataTable>. Before I just included the <ui:include> template and it would just show the table the whole time.
<ui:include src="../log/viewDlg.xhtml"/>
Now I want to be able to toggle it on/off on the web-page. Showing on the page with maybe a button or link. How can I achieve it?
Update 1: I am unable to get it to show up for some odd reason, Here is what I wrote so far based on feed back
View:
<a4j:commandLink value="View"
action="#{bean.showview}" render="viewPanel"/>
<h:panelGroup id="viewPanel">
<h:panelGroup id="tableRenderPanel" rendered="#{bean.showPolicyView}">
<ui:include src="../log/viewDlg.xhtml"/>
</h:panelGroup>
</h:panelGroup>
Backing bean:
private boolean showPolicyView = false;
public void showView() {
showPolicyView = !showPolicyView;
}
public boolean isShowPolicyView(){
return showPolicyView;
}
Wrap your <ui:include> inside two <h:panelGroup> elements. There's a catch here, you can't rerender a conditional component. Why's this? because when the element's rendered attribute resolves to false, it will not be considered while rendering the view so it can't be the target of an operation (in this case, related to renderization).
Jumping to the code, you'll have this:
<h:panelGroup id="wrapperPanel">
<h:panelGroup id="tableRenderPanel" rendered="#{yourBean.renderTable}">
<ui:include src="../log/viewDlg.xhtml"/>
</h:panelGroup>
</h:panelGroup>
yourBean#renderTable is a Boolean property that determines if the component will be rendered. When it evaluates to false, the component is not included in the component tree.
Toggling the view
To toggle the view, simply create a bean method that either refreshes the page
<h:commandLink action="#{yourBean.toggleTableView}"/>
or the particular panel through AJAX. To do this in JSF 1.2, rely on extensions like RichFaces to introduce AJAX, if you can. For example, should you choose RichFaces, you can use <a4j:commandLink/> and its handy render (or reRender in older versions) attribute to achieve what you could do normally with an <f:ajax/> in JSF 2
<a4j:commandLink action="#{yourBean.toggleTableView}" reRender="wrapperPannel"/>
Or, another alternative is
<a4j:commandLink action="#{yourBean.toggleTableView}">
<a4j:support event="oncomplete" reRender="wrapperPannel"/>
</a4j:commandLink>
Please note that the reRender attribute may vary depending on the structure of your page, but it should always reference the id of the wrapping panel in the end. Also, reRender was renamed to simply render in late RichFaces versions.
So, assuming you have a renderTable property (getter + setter) in yourBean, the toggleTableView must change it, in order to dinamically define if the component is to be rendered or not (renderTable = false).
Introducing RichFaces
Check this link for help in setting up RichFaces in your project.
I like the use of ui:include better than inserting h:panelBoxes like here:
<ui:fragment rendered="#{myBean.yourCondition()}">
<ui:include src="viewA.xhtml"/>
</ui:fragment>
<ui:fragment rendered="#{not myBean.yourCondition()}">
<ui:include src="viewB.xhtml"/>
</ui:fragment>
Advantage: Tag handlers do not represent components and never become a part of the component tree once the view has been built. It won't interefere with your CSS - the h:panelBox, in contrary, inserts a div or span.
... Another approach would be c:choose, which works but can cause render phases issues.
<c:choose>
<c:when test="#{myBean.yourCondition()}">
<ui:include src="viewA.xhtml"/>
</c:when>
<c:otherwise>
<ui:include src="viewB.xhtml"/>
</c:otherwise>
</c:choose>
Caution: When fiddling with tag handlers (like any c:xxx), be sure to know the difference between UI Components and Tag Handlers. Namely that UI Components and Tag Handlers are renderend in different phases. That implies that you cannot create a variable in a composite component and use it in a nested tag handler. c:choose and ui:include are both tag handlers, so normally it's not a problem. Read the link, it's a very short example and very insightful.

Javascript error in XHTML page in JSF 2

I have the following code in an xhtml page in JSF 2. But when the page loads I get an javascript error that
document.getElementById("country") is null or not an object.
Why is this happening?
<h:form>
<h:panelGrid columns="2">
Selected country locale :
<h:inputText id="country" value="#{country.localeCode}" size="20" />
Select a country {method binding}:
<h:selectOneMenu value="#{country.localeCode}" onchange="submit()"
valueChangeListener="#{country.countryLocaleCodeChanged}">
<f:selectItems value="#{country.countryInMap}" />
</h:selectOneMenu>
<script>
alert("hi");
document.getElementById("country").disabled=true;
</script>
</h:panelGrid>
</h:form>
It's not finding your component. Since your <h:inputText> is inside of an <h:form> the id will have the following pattern
formName:componentName. Since you did not specify an id for <h:form>, JSF will generate one for you. That's why you are seeing j_id1926454887_72d35e53:country where j_id1926454887_72d35e53 is the form id. If you want to deal with simpler id, then you should add an id value to your <h:form>. For example
<h:form id="form">
<h:inputText id="country" value="#{country.localeCode}" size="20" />
</h:form>
The id for <h:inputText> will now be form:country.
Even simpler, you could simply add prependId = "false" to your form
<h:form prependId="false">
and now your id for <h:inputText> will simply be country.
How can I get this id dynamically?
I'm going to modify your code slightly to achieve what you want (I'm thinking that you want to disable the input based on a specific event). Consider this simpler example
<h:form>
<h:inputText id="country" value="#{country.localeCode}" size="20" onclick="disableInputText(this.form)" />
</h:form>
Here I'm attaching a javascript function to an HTML DOM Event handler. In this case, I want the input to be disabled when I click on it (hence onclick). I'm also passing the form as a reference in the function. Then, define your Javascript like this.
<script>
function disableInputText(form) {
form[form.id + ":country"].disabled = true;
}
</script>
We simply grab the input in the javascript with the corresponding id via the object form and set its disabled attribute to true. (You can sort of view the form object as Map).
Also check the link below for more attributes of <h:inputText> and the different handlers you can use (the ones with on as prefix).
inputText Tag Attribute.
Also, check out the answer with the most votes to get a bigger picture on how ids are generated and other ways on how they can be determined.
How can I know the id of a JSF component so I can use in Javascript
It's seems to be a naming issue. the id of the field is not rendered as country.
i found a question that seems relevant. Composite components & ID

Command Button inside composition page in JSF

I have the same problem as user1598186 has stated in his question here : p:commandButton doesn't call bean's method in an <ui:include> page
However, no solution has been given (he has removed <ui:include> tags altogether and used variables instead)
Are there any ways of using <ui:include> and still have my backing bean's method executed, when I'm calling it inside the commandButton.
Any help will be much appreciated.
EL 2.2 method parameters (so, #{bean.method()} instead of #{bean.method}) can be used to pass a method signature that can be used in the actionListener attribute of a commandButton. The following is an example of passing a ManagedBean property as well as passing a method signature:
Main Page
<ui:include src="/jointeam.xhtml">
<ui:param name="propertyValue" value="#{managedBean.property1} />
<ui:param name="method" value="#{managedBean.performAction()}" />
</ui:include>
jointeam.xhtml
...
<h:inputText value="#{propertyValue}" />
...
<p:commandButton value="Submit" actionListener="#{method}" />
You can see how powerful this is in terms of code reuse and for many instances is less verbose and easier to use than composite components.

Calling Managed Bean Method From JavaScript [duplicate]

This question already has an answer here:
How to invoke a JSF managed bean on a HTML DOM event using native JavaScript?
(1 answer)
Closed 4 years ago.
I have an application with a client-side image map with multiple sections defined. I need to call a method in the Managed Bean from the <area> onclick attribute.
This doesn't work:
<area id="ReviewPerson" shape="rect" coords="3, 21, 164, 37" href="#"
onclick="#{personBean.method}" alt="Review Person" id="reviewPersonArea"
title="Review Person" />
Since my hands are tied on the image map (unfortunately), how can I call a managed bean method from within the <area> tag?
If you use primefaces, you don't need jquery. You use remoteCommand with a name attribute, and call that name from javascript, so:
<area... onclick="somejavascript();"... />
<p:remoteCommand name="myCommand" actionListener="#{personBean.method}" style="display: none;" />
Javascript:
function somejavascript(){
myCommand();
}
You have a few options. If you are using JSF 2.0 you can build a composite component around these area tags.
The easiest way however would be to invoke a hidden JSF input button.
<h:commandButton id="hdnBtn" actionListener="#{personBean.method}" style="display: none;" />
This will render as an input HTML element on the page that you can access from Javascript and invoke its click event.
onclick="jQuery('#form:hdnBtn').click();"
If you use Seam, Remoting can do this for you: http://docs.jboss.org/seam/2.2.0.GA/reference/en-US/html/remoting.html
Another approach would be do expose the methods as REST services, and call them from your JavaScript using something like jQuery. Here's a good article on this approach: http://coenraets.org/blog/2011/12/restful-services-with-jquery-and-java-using-jax-rs-and-jersey/
If you want complete ADF Faces solution you can try this:
function doClick(){
var button = AdfPage.PAGE.findComponentByAbsoluteId("aButton");
ActionEvent.queue(button,true);
}
This is the most up to date answer, for Java EE 8 (JSF 2.3)
use <h:commandScript> this will create a javascript function which will under the hood invoke the jsf backing bean method, and because it's a javascript you can invoke it from your js code
example
jsf
<h:form>
<h:commandScript id="submit"
name="jsFunction"
action="#{bean.method}"/>
</h:form>
and the js code
</script>
function invoke()
{
jsFunction();
}
</script>
for more visit Ajax method invocation
If you use primefaces, you can use a hidden input field linked to a managed bean, and you can initialize its value using javascript, for PrimeFaces, the PF function can be used to access a widget variable linked to the hidden input, in this way:
<script>
function codeLoginAddress() {
PF('wvLoginLat').jq.val( lat1 ); // set in getLocation
PF('wvLoginLng').jq.val( lng1 )
}
<script>
<p:inputText type="hidden" widgetVar="wvLoginLat" value="#{userSession.lat}" />
<p:inputText type="hidden" widgetVar="wvLoginLng" value="#{userSession.lng}" />
<script>
// at end of page, initialization code
getLocation(); // sets lat1 and lng1
codeLoginAddress(); // sets the value of the widget variables
</script>

Can't define a event and bind an <a4j:support/> on this event at the same time

Let's consider this simple code:
<h:form id="myForm">
<h:inputText id="myInput">
<a4j:support event="onchange" actionListener="#{myBean.doSomething}"/>
</h:inputText>
</h:form>
this will generate the following HTML code:
<input id="myForm:myInput" type="text" name="myForm:myInput" onchange="A4J.AJAX.Submit(...)" />
Now, I just add something in the onchange event of my <h:inputText>:
<h:form id="myForm">
<h:inputText id="myInput" onchange="alert('foobar');">
<a4j:support event="onchange" actionListener="#{myBean.doSomething}"/>
</h:inputText>
</h:form>
This will generate the following HTML code:
<input id="myForm:myInput" type="text" name="myForm:myInput" onchange="alert('foobar');" />
As you can see, the Ajax code is not added anymore. This is a really strange behavior as far as I am concerned. Why the <a4j:support> does not attach the Ajax call if the event is already defined in the input field?
So my question is how to make the <a4j:support> working on an event that is already defined in the input? Of course, the solution must run both the Javascript code defined in onchange and the Ajax call.
In others words, I would like to have the following HTML:
<input id="myForm:myInput" type="text" name="myForm:myInput" onchange="alert('foobar'); A4J.AJAX.Submit(...)" />
I am using Richfaces 3.3.2 and JSF 1.2
EDIT
Of course, I can move the onchange Javascript code in the onsubmit attribute of the <a4j:support>, doing something like that:
<h:inputText id="myInput">
<a4j:support onsubmit="alert('foobar');" event="onchange" actionListener="#{myBean.doSomething}"/>
</h:inputText>
But is it the only way??
If this behaviour is not explicitly documented, I would consider this as a bug in Ajax4jsf. Report it to Ajax4jsf/RichFaces guys over there at JBoss.org. I've seen problems like that before with <a4j:commandButton onclick="someFunction()">
I already had this problem before, and I found that RichFaces 3.3.x will execute only the code for the onchange event defined with a4j:support and will ignore the onchange code defined with the JSF component.
In my case the workaround was simple, for my case it was valid to use other event instead of "onchange" (not sure if it was onclick or onselect), so I attached my code to this other event and my code worked, but I'm not sure if this could work for you. If you really need the onchange event for both elements, you'll have to do just as BalusC said and report it to the RichFaces folks.
More about the context
In fact, I can't really modify the event value of the <a4j:support>, like suggesteed by Abel Morelos, because what I am trying to do is to add a custom component that execute some client-side validation. This validation calls a Javascript function, so my custom component modifies the onchange value of his parent.
My JSF code will looks like:
<h:inputText id="myInput">
<my:validation .../>
<a4j:support event="onchange" actionListener="#{myBean.doSomething}"/>
</h:inputText>
This code is quite equivalent to the following code, except that the onchange of the <h:inputText> is added automatically by my component:
<h:inputText id="myInput" onchange="if (!checkSomeValidation()) { return false; }">
<a4j:support event="onchange" actionListener="#{myBean.doSomething}"/>
</h:inputText>
So as you can understand, my custom component directly modifies the onchange event of the <h:inputText>, and due to the problem with <a4j:support>, the Ajax call is not binded to the input component at the end.
The solution
When linked to a JSF component, the <a4j:support> will "transform" itself to a facet, whose name is org.ajax4jsf.ajax.SUPPORTxxx, where xxx is the event name. So in my case, the <h:inputText> will have a facet named org.ajax4jsf.ajax.SUPPORTonchange.
So what I do in my Java code of my custom component, is to check if the parent (the <h:inputText> here), has such a facet.
If no, it means that the parent has no <a4j:support event="onchange"/> linked to it. So in this case, I modify the onchange attribute of my <h:inputText/>
If yes, it means that there is a <a4j:support event="onchange"/> linked to the parent. So I modify the facet itself using the following code:
HtmlAjaxSupport facet = (HtmlAjaxSupport) getParent().getFacet("org.ajax4jsf.ajax.SUPPORTonchange");
if (facet != null) {
facet.setOnsubmit("my Javascript code goes here");
} else {
getParent().setOnchange("my Javascript goes here");
}

Resources