How does JSF process action listener? - jsf

I'm curious how JSF know that I click the button, with some action and it is even possible to call an action listener with a parameter. I can imagine the server notice the state and EL and call the methods.
Example 1:
<form>
<p:commandButton actionListener="{bean.do_something(bean.info)}" />
</form>
Example 2:
<form>
<h:datatable values=... var="myvar">
<h:column>
<p:commandButton actionListener="{bean.do_something(myvar.info)}" />
</h:column>
</h:datatable>
</form>

During the apply request values phase, the decode() method of all UIComponent instances in the component tree is executed. This is where the necessary HTTP request parameters are checked and collected. In case of UIInput components (<h:inputText> and friends), the submitted value is been obtained. In case of UICommand components (<h:commandButton> and friends), the ActionEvent is been queued.
In case of <p:commandButton> all the magic happens in CommandButtonRenderer#decode() whom a relevant part of the source code is extracted below (line numbers are from PrimeFaces 3.5):
34 public void decode(FacesContext context, UIComponent component) {
35 CommandButton button = (CommandButton) component;
36 if(button.isDisabled()) {
37 return;
38 }
39
40 String param = component.getClientId(context);
41 if(context.getExternalContext().getRequestParameterMap().containsKey(param)) {
42 component.queueEvent(new ActionEvent(component));
43 }
44 }
If you're familiar with basic HTML, you should already know that the name=value pair of every input element and only the pressed button of the enclosing form is been sent as request parameter to the server. The PrimeFaces command button generates basically the following HTML,
<button type="submit" name="formId:buttonId" ... />
where formId:buttonId is printed from UIComponent#getClientId(). It's exactly this value which is been used as HTTP request parameter name (the HTTP request parameter value is the button's label, but that's not further relevant here). If you're familiar with basic Servlets, which JSF runs on top of, then you should also already know that request parameters are available by HttpServletRequest#getParameter(), including the name=value pair of the buttons. This allows distinguishing the pressed button.
As you see in the above decode() method, exactly this UIComponent#getClientId() value is also been used in order to check if the HTTP request parameter map contains the parameter name. If so, then an ActionEvent will be queued which ultimately get invoked during invoke application phase.
As to the EL arguments, it's actually no rocket science. The whole EL expression is just executed during invoke application phase. It's not so that it's been executed during generating the HTML output of the form and then in some way passed as request parameter. No, it's just been executed during the actual invoke application phase.

Related

how is the bean accessed first time?

I am going through the tutorial of Java EE 7 in oracle.
Here is a simple form that gets User name in a input box:
<h:body>
<h:form>
<h:graphicImage url="#{resource['images:duke.waving.gif']}"
alt="Duke waving his hand"/>
<h2>Hello, my name is Duke. What's yours?</h2>
<h:inputText id="username"
title="My name is: "
value="#{hello.name}"
required="true"
requiredMessage="Error: A name is required."
maxlength="25" />
<p></p>
<h:commandButton id="submit" value="Submit" action="response">
</h:commandButton>
<h:commandButton id="reset" value="Reset" type="reset">
</h:commandButton>
</h:form>
I see that value="#{hello.name}" is used in the code. what does this line do?
This is what is mentioned in the tutorial link:
The web page connects to the Hello managed bean through the Expression Language (EL) value expression #{hello.name}, which retrieves the value of the name property from the managed bean.
Q1) But the first time the form is loaded, there is no name attribute attached to hello bean. so Fetching that should return null correct?
Q2) Secondly, how does the value entered in the input box bind to the hello bean's name attribute?
In fact, after the form is submitted, the page is redirected to "response.xhtml" where the value of hello bean's name attribute is fetched in the same way i.e #{hello.name}.
Short answer:
Q1: correct
Q2:
On each page rendering elements, bound to #{hello.name} will receive the value, returned by Hello.getName() method.
On each form submit (in case form is valid), Hello.setName(param) will be called with param equals to value of element, bound to #{hello.name}
Longer answer:
You have:
Hello.java, the class annotated with #Named and #RequestScoped
<h:inputText value="#{hello.name}" /> inside <h:form> on index.xhtml
#{hello.name} inside <h2> on response.xhtml
Prerequisites:
#Named is a CDI annotation.
When application is deployed, the server "registers" Hello.java as a managed bean.
#Named could be used as #Named(name="explicitHelloName"), but if argument isn't provided, the bean will be registered as "lower cased class name", so, after your application is deployed, we have hello to be used wherever it needed.
#RequestScoped says that data of each Hello instance will "live" only during request-response (see this answer for scoping details).
This article greatly describes JSF lifecycle. We are interested in two fazes of it:
RENDER_RESPONSE and UPDATE_MODEL_VALUES.
Let's go step by step:
I. Browser requests index.xhtml
FacesServlet's responsibility (because we are mapping all *.xhtml files to Faces Servlet inside web.xml) is to build html and return it as a response.
Building consists of several phases (look here for details), one of which, RENDER-RESPONSE:
Builds a UIViewRoot
Recursively builds UIViewRoot's children.
During 2) for children, that has some EL expressions, these expressions being "resolved".
In your case we have #{hello.name} expression, bound to value of an UIInput (h:inputText is an UIInput).
<h:inputText> will be rendered as HTML's <input type="text">.
FacesServlet "understands" that value for this input should be taken from something that #{hello.name} represents.
To get the value:
FacesServlet "asking" for hello from container (server)
Container searches for registered hello, finding Hello.java, instantiating it and gives to FacesServlet.
FacesServlet calling for getName() (JavaBean convention to get the "name") of Hello instance and receives the value of Hello's private field name, which after class instantiation is null.
FacesServlet writes <input> without value to response and on index.xhtml you see an empty input.
II. When you press submit button
...and form is valid, there is UPDATE_MODEL phase (more detailed here),.
For our h:inputText component FacesServlet again searches for hello, finding an instance of Hello and calls setName(value) on it.
As <h:commandButtons>'s action attribute is response, browser is about to receive response.xhtml's content (this is not a redirection, see this answer, so, #RequestScoped hello isn't being recreated).
III. response.xhtml
...has <h3>Hello, #{hello.name}</h3>, so, again, FacesServlet must resolve it as described earlier.
But in current request the Hellohave been instantiated and it's field name was already set (in UPDATE_MODEL_VALUES phase) with the value you entered to input, so, <h3>Hello, YOUR_SUBMITTED_TO_INPUT_TEXT_VALUE</h3> is written to response and displayed in browser.
More details about expressions can be found here
Edit
If you use <h:comandButton action="response?faces-redirect=true"/> on the index.xhtml, then after II there will be a redirect to response.xhtml, and Hello bean become recreated, so, you will see "Hello, " (name will be null)

CommandButton action: use value of inputText as parameter in dynamic URL

I need to make a small form where user types a number into the inputField, and clicks on a button, then is sent to a page, using that number as a parameter to the page.
So far I got into this:
<p:inputText id="myText" style="width:75px;" />
<p:commandButton id="myButton" value="Ir"
action="/site/page.xhtml?id=${param['form:myButton']}"
title="Ir" ajax="false" proces="#this,myText" />
tried with ${param['form:myButton']} and #{param['form:myButton']}, error is the same.
Problem is, JSF thinks its a method expression...
GRAVE: Servlet.service() for servlet [Faces Servlet] in context with path [/intranet] threw exception [/myPage action="/site/page.xhtml?id=${param['form:myButton']}".xhtml #95,41 action="/site/page.xhtml?id=${param['form:myButton']}" Not a Valid Method Expression: action="/site/page.xhtml?id=${param['form:myButton']}" with root cause
javax.el.ELException: Not a Valid Method Expression: action="/site/page.xhtml?id=${param['form:myButton']}"
at org.apache.el.lang.ExpressionBuilder.createMethodExpression(ExpressionBuilder.java:236)
at org.apache.el.ExpressionFactoryImpl.createMethodExpression(ExpressionFactoryImpl.java:55)
at org.jboss.weld.util.el.ForwardingExpressionFactory.createMethodExpression(ForwardingExpressionFactory.java:43)
at org.jboss.weld.el.WeldExpressionFactory.createMethodExpression(WeldExpressionFactory.java:64)
at com.sun.faces.facelets.tag.TagAttributeImpl.getMethodExpression(TagAttributeImpl.java:222)
at com.sun.faces.facelets.tag.jsf.ActionSourceRule$ActionMapper2.applyMetadata(ActionSourceRule.java:104)
at com.sun.faces.facelets.tag.MetadataImpl.applyMetadata(MetadataImpl.java:81)
at javax.faces.view.facelets.MetaTagHandler.setAttributes(MetaTagHandler.java:129)
at javax.faces.view.facelets.DelegatingMetaTagHandler.setAttributes(DelegatingMetaTagHandler.java:102)
at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.doNewComponentActions(ComponentTagHandlerDelegateImpl.java:402)
and this is the bottom-most exception trace.
Question: how can I pass the value typed into of the input-field into the action of the Button when the button is clicked, so the browser navigates to the desired page passing the value in the input as a parameter, without resorting to a backing bean.
I don't need to communicate with the server, just forward the page.
any solution using jQuery or plain javascript in tandem with JSF is acceptable too.
using mojarra, primefaces 3.3.1
use <f:param> It's explained in this article from BalusC http://balusc.blogspot.in/2011/09/communication-in-jsf-20.html#ProcessingGETRequestParameters
I do not need a bazooka to kill a mouse. The answer is very simple.
bind a javascript function to the onclick of the button, and in that function, retrieve the value of the input field by its id, then assemble URL with the parameters and navigate away from within the javascript function.
JSF code (assume they are inside a form with myForm as its id):
<p:inputText id="myInput" style="width:75px;" />
<p:commandButton id="myButton" value="Ir" onclick="navega();" title="Ir" ajax="false" proces="#none" />
Javascript script, declared on a separate file or later on in the xhtml document:
function navega() {
var submittedInputValue = $('#myForm\\:myInput').val();
window.location.href = "/site/mypage.xhtml?param1=whatever&id=" + submittedInputValue ;
}
Take note of the prepend id (myForm:myInput), of using \\ to escape the : in jQuery, and using & instead of & in the xhtml so its not treated as an entity.
Note: probably not all of those commandButton attributes are required, feel free to edit this answer and remove the useless ones.
EDIT: removed the submit and changed process to #none on the command button.

commandLink is not fired in a page with a param in its URI

When I call a method in a page with a param in its URI, the method is not invoked unless I pass the parameters of the uri again. For example if I have:
http://maywebsite/myapp/mypage.xhtml?mykey=myvalue
This method results in error (obviously because it renders the page again without params, but the method foo is never invoked):
<h:commandLink value="Do Action" actionListener="#{mybean.foo}"/>
So I added an ajax to only update the component, but the button is not getting fired:
<h:commandLink value="Do Action" actionListener="#{mybean.foo}">
<f:ajax render="somecomponent"/>
</h:commandLink>
When I passed the param values again, the button invokes the method just fine:
<h:commandLink value="Do Action" actionListener="#{mybean.foo}">
<f:param name="mykey" value="myvalue"/>
<f:ajax render="somecomponent"/>
</h:commandLink>
However, this button is included (ui:include) in many pages with different param keys and values. How can I invoke the method without passing the param values?
Im using glassfish 3.1.2, jsf 2.0
Apparently the bean is request scoped and the parameter plays a role in the way how the command link is rendered (e.g. by the rendered attribute on one of its parent components, or by a dynamic include of the template containing the command link).
All those conditions are namely re-evaluated during apply request values phase of the form submit. The developer has to make sure that all those conditions are exactly the same as when the form was presented to the enduser. So, when the bean is request scoped and the parameter is absent, then the command link appears as non-rendered in the component tree and this way its action won't be invoked.
Putting the bean in the view scope is the easiest way to fix this (unless you're using a dynamic <ui:include>, this is then more complicated, you'd need to turn off partial state saving for the particular view).
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated - point 5

Trying to understand immediate="true" skipping inputs when it shouldn't

Just when I thought I had understood immediate... *sigh*
Consider the following JSF page:
<h:inputText value="#{testBean.text}" required="true" />
<h:commandButton actionListener="#{testBean.doFoo}" value="Do Foo" />
<h:commandButton immediate="true" actionListener="#{testBean.doBar}" value="Do Bar" /><br />
<h:outputText value="#{testBean.didSomething}" />
And this backing bean:
public class TestBean {
private String didSomething = "Nothing done yet";
// + getter
public void doFoo() {
didSomething = "Did foo!";
}
public void doBar() {
didSomething = "Did bar!";
}
From all I read about immediate I would expect the following:
When trying to do foo while not providing a value for the input field, the action is never executed because during processValidationsPhase an error occurs, resulting in the page to be re-rendered directly after this phase with an error message. The value of the didSomething remains unchanged. (This works as expected)
When trying to do bar while not providing a value for the input field, the action is executed during applyRequestValuesPhase because of the immediate attribute. The variable didSomething is changed. (This works as expected)
On what happens next, this description states:
"A null return value (as outcome of the action method) causes processing to continue as normal, ie non-immediate components are validated then update-model is executed (if no validation errors occurred). For an action listener method that returns void, it is necessary to call facesContext.renderResponse(); if the normal flow is not desired."
From this I had the idea that processing continues as normal (as my action method does neither return an outcome nor force renderResponse()), resulting in the same validation error. Only difference would be that it occurs after setting didSomething. However, this does not happen. Instead, it feels like the site still skips all remaining phases, with the input field not being touched. It re-renders without error message.
Can someone explain to me where my understanding of how this works is amiss?
With immediate="true" on the button, the action is indeed invoked during apply request values phase and all the remaining phases are skipped. That's also the sole point of this attribute: process (decode, validate, update and invoke) the component immediately during apply request values phase.
All inputs which do not have immediate="true" are ignored anyway. Only inputs which do have immediate="true" are also processed, but this happens also during apply request values phase. Why should the remaining phases be invoked if everything has already taken place in the apply request values phase?
In the Debug JSF lifecycle article you can find the following summary which should enlighten when to (not) use the immediate"true":
Okay, when should I use the immediate attribute?
If it isn't entirely clear yet, here's a summary, complete with real world use examples when they may be beneficial:
If set in UIInput(s) only, the process validations phase will be taken place in apply request values phase instead. Use this to prioritize validation for the UIInput component(s) in question. When validation/conversion fails for any of them, the non-immediate components won't be validated/converted.
If set in UICommand only, the apply request values phase until with update model values phases will be skipped for any of the UIInput component(s). Use this to skip the entire processing of the form. E.g. "Cancel" or "Back" button.
If set in both UIInput and UICommand components, the apply request values phase until with update model values phases will be skipped for any of the UIInput component(s) which does not have this attribute set. Use this to skip the processing of the entire form expect for certain fields (with immediate). E.g. "Password forgotten" button in a login form with a required but non-immediate password field.
See also:
Why was "immediate" attribute added to the EditableValueHolders?

further continuing of double press

In a previous question BalusC gave me good advice on how a button, in place of a commandButton is useful for non ajax navigation. In particular it updates the destination address in the http: position which is useful for the user to bookmark a page.
I tried to use this information to my advantage until I came upon a problem. In a button I tried to use outcome="#{backing.something}" to find out that it gives me a null result. This looks like a timing problem in that action="#{}" is evaluated only when the button is pressed whereas outcome apparently wants a fixed string which gets checked when the page is loaded.
So I went back to commandButton with ajax="false". This has a problem that my navigation address is the page I came from, not the one I am navigating to. This is the wrong bookmark for the user.
I appreciate all the help I have received in stackoverflow on my learning exercise.
Ilan
The <h/p:button outcome> is not intented to invoke a bean action method, but to contain the outcome string directly. Any EL in there is evaluated immediately as a value expression. So the method behind it would immediately be invoked when you just open the page containing the <h/p:button>.
There are in your particular case basically two ways to invoke a bean action method on navigation. If you need to invoke it before the navigation takes place and the action isn't intented to be re-invoked everytime when the enduser reopens/reloads the GET request, then make it a POST-Redirect-GET request. It's a matter of adding faces-redirect=true to the outcome value in query string syntax.
E.g.
<p:commandButton action="#{bean.submit}" ... />
with
public String submit() {
// ...
return "nextpage?faces-redirect=true";
}
This way the browser will be redirected to the target page after POST, hence the enduser will see the target URL being reflected in the address bar.
Or if you need to invoke the action everytime when the enduser reopens/reloads the GET request, do the job in the (post)constructor or preRenderView listener method of the request/view scoped backing bean instead.
E.g.
<p:button outcome="nextpage" ... />
with
#ManagedBean
#RequestScoped
public class NextpageBacking {
public NextpageBacking() {
// In constructor.
}
#PostConstruct
public void onPostConstruct() {
// Or in postconstructor (will be invoked after construction AND injection).
}
public void onPreRenderView() {
// Or before rendering the view (will be invoked after all view params are set).
}
// ...
}
The pre render view listener method needs to be definied as follows in the nextpage
<f:event type="preRenderView" listener="#{nextpageBacking.onPreRenderView}" />
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
Communication in JSF 2.0 - Processing GET request parameters

Resources