I do not really understand how getter and setter work althougth it is a basic concept. I have the following code, how is the attribute id sent to Managed Bean? Is it captured by getter method?
My facelet
<p:inputText id="id" value="#{bean.id}">
My managed bean
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
The call of getter and setter methods by #{} expressions is not part of JSF but Expression Language (most known as EL). JSF takes advantage of EL to bind the data of the HTML components to the fields of a bean through proper getters and setters. This is:
If the bean exists, Expression Language will execute the proper getter of the registered bean in the proper scope.
If client performs a form submission or an ajax request, then the components that are sent to the server (usually all the components in the <h:form>, in case of ajax requests you can state which components to send to the server) will contain a new value, and this value will be set to the field with the proper setter method.
For example, you have a SayHelloBean which belongs to request scope:
#RequestScoped
#ManagedBean
public class LoginBean {
private String name;
//proper getter
public String getName() {
return this.name;
}
//proper setter
public void setName(String name) {
this.name = name;
}
}
And these 2 facelets pages (since it's an example I avoid declaring <html>, <h:head>, <h:body> and other elements, just focusing on the relevant code)
Page1.xhtml:
<h:form>
Please tell me your name
<h:inputText value="#{loginBean.name}" />
<h:commandButton action="page2" />
</h:form>
Page2.xhtml:
Hello #{loginBean.name}
This is what happens behind the scenes:
When Page1.xhtml is loaded, a new instance of LoginBean, which we may call loginBean, will be created by JSF and registered into JSP request scope. Since the value of <h:inputText /> is bound to LoginBean#name (which is read as the field name of LoginBean class), then EL will display the value of loginBean#name (which is read as the field name of instance loginBean), and since that is not initialized, EL will display null, as an empty string.
When you submit the form of Page1.xhtml, since LoginBean is #RequestScoped then JSF will create a new instance of LoginBean, which we may call it loginBean2 (adding 2 in the end because this instance is totally different from the loginBean previously created) and will register it in JSP request scope. Since the value of <h:inputText /> is bound to LoginBean#name, JSF will validate and set the data by calling the proper setter. This will make loginBean2#name have the value of the <input type="text"> that was rendered by <h:inputText/>.
At last, JSF will make sure to navigate to Page2.xhtml through forward, where when processing it, it will find #{loginBean.name} and EL will check for the value of loginBean2#name and replace it.
The steps explained here are a very small explanation (and with lot of elements not explained) of the JSF lifecycle and how JSF uses getters and setters.
More info:
How to pass parameter to jsp:include via c:set? What are the scopes of the variables in JSP?
How to choose the right bean scope?
The Lifecycle of a JavaServer Faces Application
Differences between Forward and Redirect
Additional note: since you're learning JSF, avoid putting any business logic code in getters/setters. This is greatly explained here: Why JSF calls getters multiple times
Whenever you use something like
#{someBean.someField}
the EL looks for a someBean.getSomeField() or someBean.setSomeField(...) method, depending on whether you're reading that field or writing in it (which can easily be inferred from the context). JSF never accesses a field directly (i.e without making use of its getter or setter). Try deleting the getter and setter of a given field and you'll see it won't work.
Related
I am working on a JSF app with a form:
<h:inputText value="#{model.firstname}" />
<h:inputText value="#{model.employeeNumber}" converter="javax.faces.Integer"/>
...
Here's the model with bean validation:
#NotNull(message="Please enter a surname")
private String firstname;
#Min(value=1)
#Max(value=2000)
private Integer employeeNumber;
...
//setters and getters
Everything is working nicely apart from the explicitly coded 'back' button within the page which goes to the previous page / form.
I want the values the user entered to restore when they return to the above page again regardless of whether the data is valid.for example, if the user enters abc into the employeeNumber field this String cannot be stored to the Integer on the model.
I understand that JSF stores user entered values into "Request Values" for each UIComponent. It is these I would like to restore rather than that of my model because the above form had not had it's data validated yet.
How can I do this?
(data validation will happen when the user clicks submit).
My colleague has just mentioned there may be a way to accomplish this using omnifaces
As of now, there is. Lot of things were already available in OmniFaces except of only one small missing key part. I just committed a Hacks#getStateHelper() to 2.3 SNAPSHOT which should expose the protected UIComponent#getStateHelper() method into public. Then, it's doable together with help of EditableValueHolderStateHelper already in OmniFaces since 1.0 and with <o:form> and <o:ignoreValidationFailed> in order to invoke action anyway irrespective of conversion/validation errors (as the "back" button should do).
So, if you make sure you use at least OmniFaces 2.3 (currently only available as SNAPSHOT), then you can achieve the requirement with below session scoped helper bean, utilizing several OmniFaces utility classes Faces, Hacks and EditableValueHolderStateHelper:
#Named
#SessionScoped
public class Forms implements Serializable {
private transient Map<String, StateHelper> states = new ConcurrentHashMap<>();
public void saveState(ComponentSystemEvent event) {
UIComponent form = event.getComponent();
FacesContext context = Faces.getContext();
StateHelper state = Hacks.getStateHelper(form);
EditableValueHolderStateHelper.save(context, state, form.getFacetsAndChildren());
states.put(Faces.getViewId(), state);
}
public void restoreState(ComponentSystemEvent event) {
StateHelper state = states.get(Faces.getViewId());
if (state != null) {
UIComponent form = event.getComponent();
FacesContext context = Faces.getContext();
EditableValueHolderStateHelper.restore(context, state, form.getFacetsAndChildren());
}
}
public void removeState() {
states.remove(Faces.getViewId());
}
}
The saveState needs to be invoked during postValidate event of the form component. The restoreState() needs to be invoked during postAddToView event of the form component. The removeState() needs to be invoked during succesful action. Below is an example form:
<o:form>
<f:event type="postAddToView" listener="#{forms.restoreState}" />
<f:event type="postValidate" listener="#{forms.saveState}" />
<h:inputText value="#{bean.string}" required="true" />
<h:inputText value="#{bean.integer}" required="true" />
<h:commandButton value="save" actionListener="#{forms.removeState()}" action="#{bean.save}" />
<h:commandButton value="back" action="#{bean.back}">
<o:ignoreValidationFailed />
</h:commandButton>
<h:messages />
</o:form>
Major advantage of this approach is that no modifications needs to be made to existing validation rules and backing beans, hereby thus keeping all advantages of JSF and BV.
Make sure you clear server session state and/or increase serialVersionUID of Forms class whenever you make changes in the component tree structure of the associated forms, else you'll have to make prechecks and/or properly handle exceptions. Giving the forms and input components a fixed ID is also strongly recommended.
I have dealt with just this problem in the past - by not using the validation annotations such as #NotNull, #Min, and #Max. When using those annotations, invalid data cannot be applied to the model, and so the state cannot be saved on the server.
Instead, I had to code the validation logic in the method behind the Submit button. The downside is that JSF isn't doing the work for you; you have to do it yourself. The upside is that you have more control over exactly when and how the validation is applied.
In a JSF 2.1 application, I need to build a JSF dataTable (using PrimeFaces) that shows only the db records belonging to the logged in user.
So, I need to pass the username to the bean associated to the dataTable's value attribute:
value="#{tableBuilder.records}"
Here is the table bean:
#ManagedBean
#ViewScoped
public class TableBuilder {
private List<MyRecord> records;
private String username;
// getters and setters
}
It's useful to know that the application consists of a single web page, with container-managed authentication implemented through LoginBean, a SessionScoped ManagedBean. This implies the additional effort of notifying to TableBuilder when the user logs in.
The only way I am thinking of is to inject the LoginBean into the TableBuilder through #ManagedProperty annotation, and checking on every request of getRecords if the username property of LoginBean has changed.
Maybe there are better ways?
If your environment supports EL 2.2 (your question history confirms Java EE 6), then "just do it":
<h:dataTable value="#{bean.getModel(user)}">
with
public List<Item> getModel(User user) {
// ...
}
Whether it's the right way or there are better ways, I'll leave in the middle. Keep in mind that a getter is invoked as many times as EL evaluates the value expression.
I have a facelet template with:
<f:metadata>
<o:viewParam name="id" value="#{homeBean.id}" />
</f:metadata>
<h:form>
<h:inputHidden value="#{homeBean.id}" />
<h:inputText value="#{homeBean.user.firstName}" />
<h:commandButton value="Submit" action="#{homeBean.onSave()}" />
</h:form>
and a request scoped bean with:
#Named
#RequestScoped
public class HomeBean {
private Integer id;
private User user;
public void setId(Integer id) {
System.out.println("setId called");
user = // code for loading User entity bean with supplied id
}
// other accessors for id and user
}
Initial page load works well, entity is loaded and displayed in a form, inputHidden is set to entity id. Problem is that submit throws:
javax.el.PropertyNotFoundException - Target unreachable, base expression '. user' resolved to null
probably because getUser is called before setId. How can I solve this? I really would like to have a request scoped bean, I know that this can be easily solved with at least viewaccess scoped bean.
EDIT: Now i noticed that exception is thrown in Process Validations phase, I initially thought that exception is thrown in Update Model Values phase. I changed "private User" to "private User user = new User()" and now it's OK, but it feels little weird.
Regards,
Pavel
The OmniFaces <o:viewParam> sets the request parameter only in the initial request and not in postbacks. This is intented to be used with #ViewScoped beans so that the request parameter isn't unnecessarily been validated, converted and updated on every single postback (because it's already still present in a view scoped bean). The API documentation and the showcase example also explicitly mentions that it should be used with view scoped beans.
You've there however a request scoped bean which get trashed and recreated on every single request, also on postbacks to the same view. So the user property disappears and falls back to default null on every subsequent postback request.
There are basically 2 ways to fix it:
Replace <o:viewParam> by <f:viewParam>. It will call the setter on every request, also on postbacks.
Replace #Named #RequestScoped by #ManagedBean #ViewScoped, this way the bean will live as long as you're interacting with the same view. Or if you insist in using CDI, use #Named #ConversationScoped instead, but you have to manage the begin and end of the conversation yourself.
I would like to be able to change the text in the url bar to reflect the current state of my app (like GMail does).
Mainly i'm referring to changing the url parameters to reflect the values in my form. (Once a user enters a parameter I would like to change the url parameter that corresponds)
Any idea on how to do that?
Thanks!
To the point, you want fire a HTTP GET request instead of a HTTP POST request. Changing the view side is trivial, use
<form action="targetpage.jsf">
instead of
<h:form>
In the managed bean which is associated with targetpage.jsf you however need to do a bit more changes. JSF 1.2 doesn't offer facilities to set GET request parameters for you by the view declaration, nor does it convert/validate the parameters (JSF 2.0 has <f:viewParam> for this).
You need to gather/convert/validate all request parameters yourself in the constructor and/or #PostConstruct of the backing bean and invoke the action over there as well. There are basically two ways to gather the parameters:
Define the parameter as <managed-property> of the <managed-bean> in faces.config.xml so that JSF will set it for you.
E.g.
<h:inputText id="input" />
(which will generate <input type="text" id="input" name="input" /> in HTML, it's the name attribute which is been used as request parameter name; rightclick page in browser and view source if you're unsure)
with
<managed-property>
<property-name>input</property-name>
<value>#{param.input}</value>
</managed-property>
and
private String input; // +setter
EL supports automatic conversion to primitive types and their wrappers as well, so you could for numbers also use private Long input; instead. The caveat is however that this would throw an ugly and unhandleable exception when the value is not parseable as a number.
Or, gather them yourself by ExternalContext#getRequestParameterMap().
public class Bean {
private String input;
public Bean() {
Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
input = params.get("input");
// ...
}
}
This allows for more fine-grained conversion/validation/errorhandling.
I use JSF managed beans calling EJB methods that are provide data from database. I want to use some data already on the welcome page of the application. What is the best solution for it?
EJBs are injected into JSF managed beans and it looks like the injection is done after executing the constructor. So I am not able to call EJB methods in the constructor.
The normal place for EJB call is in the JSF action methods but how to call such a method prior to loding the first page of the application?
A possible solution would be to call the EJB method conditionally in a getter that is used on the welcome page, for example:
public List getProductList(){
if (this.productList == null)
this.productList = myEJB.getProductList();
return this.productList;
}
Is there any better solution? For example, in some config file?
You can do it in a method which is annotated with #PostConstruct. This will be executed once after the bean is constructed and all JSF managed property and resource injection is done.
#PostConstruct
public void init() {
this.productList = myEJB.getProductList();
}
if you want to make a call from xhtml view
<f:view>
<f:metadata>
<f:viewAction action="${myController.init()}" onPostback="true"/>
</f:metadata>
</f:view>
and your controller
public class MyController{
public void init(){
this.productList = myEJB.getProductList();
...