When or where do entities get created?
Do they get created when the XHTML page loads and accesses the entities via the managed bean?
Or do they get automatically created in the managed bean?
Do we need to manually create it from the managed bean's constructor?
Please see the code below (some necessary code might not have been copied.)
The entity would be:
public class PersonalInfo implements Serializable {
#Size(max = 50)
#Column(name = "FIRST_NAME", length = 50)
private String firstName;
// some getters and setters
}
the web page would be:
<h:form>
<h:outputText value="first name"/>
<h:inputText value="#{personalInforController.personalInfo.firstName}" />
<h:commandButton value="hit me"
action="#{personalInforController.create}"
immediate="true"/>
</h:form>
and the backing bean would be:
#Named(value = "personalInfoController")
#SessionScoped
public class PersonalInforController {
#EJB
PersonalInfoFacade ejbFacade;
PersonalInfo personalInfo;
String defaultPage = "index";
public String create() {
try {
ejbFacade.create(personalInfo);
return "prepareCreate";
} catch (Exception e) {
return "success";
}
}
}
In the example code given, the create action indeed doesn't seem to be able to work. The entity must be created by the backing bean before that.
If it's a simple entity, either the constructor or an #PostConstruct method would work. For instance:
#Named(value = "personalInfoController")
#SessionScoped
public class PersonalInforController {
#EJB
PersonalInfoFacade ejbFacade;
PersonalInfo personalInfo;
String defaultPage = "index";
#PostConstruct
public void init() {
personalInfo = new PersonalInfo();
}
public String create() {
try {
ejbFacade.create(personalInfo);
return "prepareCreate";
} catch (Exception e) {
return "success";
}
}
Some notes about the code. It's highly suspicious, and most likely plain wrong, to declare your bean to be #SessionScoped. If personalInfo is being edited in two tabs or windows you'll be in a world of hurt. I suggest making your bean #ViewScoped (for CDI, there's a separate extension made by the Seam3 that enables this, if you can't/won't use this extension consider using #ManagedBean instead of #Named).
Also, you might want to declare your instance variables to be private and give ejbFacade a better name (e.g. personalInfoFacade). I also doubt whether immediate is necessary on the commandButton, and since the outputText is obviously a label for the given inputText, you might want to consider using outputLabel and the for attribute.
Related
I have started learning JSF, but sadly most tutorials out there present only a log in or a register section.
Can you point me to some more in depth examples? One thing I'm interested in is a page presenting a list of products. I'm on page home and I press on page products so that I can see the latest products added. And every time I visit the page, the product list will be created from the latest entries in the database. How can I handle this?
One way to solve this would be to create a session scoped managed bean in which I would place different entities updated through other managed beans. I found this kind of approach in some tutorials, but it seems quite difficult and clumsy.
Which would be the best approach to solve a thing like this? What is the correct usage of session scope in two-page master-detail user interface?
What is the correct usage of session scope
Use it for session scoped data only, nothing else. For example, the logged-in user, its settings, the chosen language, etcetera.
See also:
How to choose the right bean scope?
And every time I visit the page, the product list will be created from the latest entries in the database. How can I handle this?
Typically you use the request or view scope for it. Loading of the list should happen in a #PostConstruct method. If the page doesn't contain any <h:form>, then the request scope is fine. A view scoped bean would behave like a request scoped when there's no <h:form> anyway.
All "view product" and "edit product" links/buttons which just retrieve information (i.e. idempotent) whould be just plain GET <h:link> / <h:button> wherein you pass the entity identifier as a request parameter by <f:param>.
All "delete product" and "save product" links/buttons which will manipulate information (i.e. non-idempotent) should perform POST by <h:commandLink>/<h:commandButton> (you don't want them to be bookmarkable/searchbot-indexable!). This in turn requires a <h:form>. In order to preserve the data for validations and ajax requests (so that you don't need to reload/preinitialize the entity on every request), the bean should preferably be view scoped.
Note that you should basically have a separate bean for each view and also note that those beans doesn't necessarily need to reference each other.
So, given this "product" entity:
#Entity
public class Product {
#Id
private Long id;
private String name;
private String description;
// ...
}
And this "product service" EJB:
#Stateless
public class ProductService {
#PersistenceContext
private EntityManager em;
public Product find(Long id) {
return em.find(Product.class, id);
}
public List<Product> list() {
return em.createQuery("SELECT p FROM Product p", Product.class).getResultList();
}
public void create(Product product) {
em.persist(product);
}
public void update(Product product) {
em.merge(product);
}
public void delete(Product product) {
em.remove(em.contains(product) ? product : em.merge(product));
}
// ...
}
You can have this "view products" on /products.xhtml:
<h:dataTable value="#{viewProducts.products}" var="product">
<h:column>#{product.id}</h:column>
<h:column>#{product.name}</h:column>
<h:column>#{product.description}</h:column>
<h:column>
<h:link value="Edit" outcome="/products/edit">
<f:param name="id" value="#{product.id}" />
</h:link>
</h:column>
</h:dataTable>
#Named
#RequestScoped
public class ViewProducts {
private List<Product> products; // +getter
#EJB
private ProductService productService;
#PostConstruct
public void init() {
products = productService.list();
}
// ...
}
And you can have this "edit product" on /products/edit.xhtml:
<f:metadata>
<f:viewParam name="id" value="#{editProduct.product}"
converter="#{productConverter}" converterMessage="Unknown product, please use a link from within the system."
required="true" requiredMessage="Bad request, please use a link from within the system."
/>
</f:metadata>
<h:messages />
<h:form rendered="#{not empty editProduct.product}>
<h:inputText value="#{editProduct.product.name}" />
<h:inputTextarea value="#{editProduct.product.description}" />
...
<h:commandButton value="save" action="#{editProduct.save}" />
</h:form>
#Named
#ViewScoped
public class EditProduct {
private Product product; // +getter +setter
#EJB
private ProductService productService;
public String save() {
productService.update(product);
return "/products?faces-redirect=true";
}
// ...
}
And this converter for <f:viewParam> of "edit product":
#Named
#RequestScoped
public class ProductConverter implements Converter {
#EJB
private ProductService productService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
try {
Long id = Long.valueOf(value);
return productService.find(id);
} catch (NumberFormatException e) {
throw new ConverterException("The value is not a valid Product ID: " + value, e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
}
if (value instanceof Product) {
Long id = ((Product) value).getId();
return (id != null) ? String.valueOf(id) : null;
} else {
throw new ConverterException("The value is not a valid Product instance: " + value);
}
}
}
You can even use a generic converter, this is explained in Implement converters for entities with Java Generics.
See also:
How to navigate in JSF? How to make URL reflect current page (and not previous one)
JSF Controller, Service and DAO
JSF Service Layer
How to inject #EJB, #PersistenceContext, #Inject, #Autowired, etc in #FacesConverter?
Communication in JSF 2.0 - Contains several examples/hints
As a small improvement to what BalusC recommended, sometimes you can remove the required / requiredMessage part from the <f:viewParam> of your "details" screen and instead use the conditional rendering of the editing form (as BalusC did) with a reverse condition for recommending a specific link for the "list/master" screen or, even use a viewAction that would test the param and force a redirect to that list.
I have a Stripes form using an Action bean.
I can store the data without any problem out of the form, but I did not find out how to prepopulate the form's data.
I have simplified my example to show what's not working.
My code under #Before is without any effect: the data dont go into the form.
How should I manage this approach?
myform.jsp:
...
<stripes:form beanclass="UserActionBean">
...
<stripes:text name="user" />
<stripes:submit name="store" value="Save" />
...
</stripes:form>
...
ActionBean:
public class UserActionBean implements ActionBean {
private String user;
#Before
public void init() {
user = "myuser";
}
#DefaultHandler
public Resolution store() {
...
}
}
You will need public getters and setters for Stripes to be able to read the value.
Although usually I would use the #DefaultHandler for the get, and then create a new handler for the save e.g. #HandlesEvent(value = "save") public Resolution save () {...
This might be crude, but I used a getter like Scary Wombat said and initialized the variable in the ActionBean. Or you could set its value in your handler that does the initial page display.
#Validate(required = true, on = {
"proceed"
})
private String tumblrUrl= "xyz.tumblr.com";
...
public String getTumblrUrl() {
return this.tumblrUrl;
}
public void setTumblrUrl(final String tumblrUrl) {
this.tumblrUrl = tumblrUrl;
}
I am setting a value in the login-bean when the user logs-in
using
--Login-bean
setMailNickname(mailnickname);
And in the info page I am able to retrieve the same value using
--InfoPage
<h:inputText value="#{login.mailNickname}" />
Now on the info page I am taking some more info from the user and saving all of them in the database, As one of the value is from the login form bean it is prepopulating on the Info-page but how to assign the same value to the info-page bean variable-
InfoBean.mailNickname
so that it can be saved into the database with the other fields which the user provides.
How can I assign the value to the infopage bean variable?
You can inject LoginBean into InfoBean, so that you can have a reference to its fields. As I understand one of your beans is at least #SessionScoped. You didn't provide that information, so I'd go as far as to guess that LoginBean is used for logging into your application or something like that and InfoBean to hold that information for future reference. Following that chain of thinking your InfoBean would look like that:
#ManagedBean
#SessionScoped
public class InfoBean {
private String mailNickname;
public String getMailNickname() {
return mailNickname;
}
public void setMailNickname(final String mailNickname) {
this.mailNickname = mailNickname;
}
// other fields, methods...
}
and your LoginBean then would hold reference to session-scoped InfoBean:
#ManagedBean
#ViewScoped
public class LoginBean {
private String mailNickname;
public String getMailNickname() {
return mailNickname;
}
public void setMailNickname(final String mailNickname) {
this.mailNickname = mailNickname;
}
#ManagedProperty("#{infoBean}")
private InfoBean infoBean;
public void setInfoBean(final InfoBean infoBean) {
this.infoBean = infoBean;
}
/** Method you use for populating */
public void populateInput(final String mailNicknameFromDb) {
setMailNickname(mailNicknameFromDb);
infoBean.setMailNickname(mailNicknameFromDb);
}
}
Remember about getters and setters for those fields as well as setter for managedProperty and be careful about scopes (you can't for example inject request-scoped bean into application-scoped bean, because the request-scoped one would probably not exist yet when creating application-scoped one).
I have the following model code, which I am supposed to use.
public class Client extends User {
private String userName;
public Client(String firstName, String lastName, String userName){
super(firstName, lastName);
this.userName = userName;
}
//getters and setters
}
public abstract class User {
String firstName;
String lastName;
//getters and setters
}
Now I have created the following bean:
#ManagedBean(name = "client")
#SessionScoped
public class ClientBean implements Serializable {
private final long serialVersionUID = 1L;
private Client client;
public Client getClient(){
return client;
}
public void setClient(Client client){
this.client = client;
}
}
Now I want to set the clients' firstName using this bean in an xhtml page:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<head>
<title>Register as a client</title>
</head>
<body>
<h:form>
First Name:<h:inputText value="#{???}"></h:inputText>
<br/>
<h:commandButton value="Register" action="registered?faces-redirect=true"/>
</h:form>
</body>
</html>
Now my question is: How do I access the clients' firstName? Am I supposed to create a new bean that represents user as well and extend it in ClientBean? (If so, what's the use of having model code at all? I would have double code everywhere?) Or is there any other simpler way to implement this in JSF 2.0?
You will need the following for the page to display the last name correctly.
-- The class User must have a constructor as below along with getters and setters for firstname and lastname.
public User (String firstName, String lastName)
-- Public getter and setter method for username in Client class.
-- In the ClientBean class, I would suggest you change the name to clientBean. Also, change the getter and setter method to public instead of private. You would need to create an object of type client and initialize the client object to some value if you need to display it on the screen. In the code provided, you are not creating an object or giving any value to any of the name properties.
-- In the JSF page, you can access the values using "#{clientBean.client.firstName}"
Make getter and setter public
public Client getClient(){
return client;
}
public void setClient(Client client){
this.client = client;
}
If your Client is null, simply instantiate it.
private Client client = new Client();
You would take this approach with a managed bean and a pojo if you want to for instance persist the values in a database, or do some other magic stuff, like calling a webservice.
To access the first name you write #{client.client.firstName} Indeed it looks a bit awesome, so I suggest give the managed bean another name.
You could create an instance of the pojo in the managed bean:
public class ClientBean implements Serializable {
private Client client = new Client();
...
}
You could also include first name and last name in the managed bean directly, this would make sense in case you are creating the pojo during some action to save the values.
JSF doesn't press you in a corset, instead you can choose the way that fits you.
How can I dynamically change managed bean of "value" attribute? For example, I have h:inputText and, depending on typed-in text, managed bean must be #{studentBean.login} or #{lecturerBean.login}. In a simplified form:
<h:inputText id="loginField" value="#{'nameofbean'.login}" />
I tried to embed another el-expression instead of 'nameofbean':
value="#{{userBean.specifyLogin()}.login}"
but it doesn't worked out.
Polymorphism should rather be done in the model, not in the view.
E.g.
<h:inputText value="#{person.login}" />
with
public interface Person {
public void login();
}
and
public class Student implements Person {
public void login() {
// ...
}
}
and
public class Lecturer implements Person {
public void login() {
// ...
}
}
and finally in the managed bean
private Person person;
public String login() {
if (isStudent) person = new Student(); // Rather use factory.
// ...
if (isLecturer) person = new Lecturer(); // Rather use factory.
// ...
person.login();
// ...
return "home";
}
Otherwise you have to change the view everytime when you add/remove a different type of Person. This is not right.
Another way:
<h:inputText id="loginField1" value="#{bean1.login}" rendered="someCondition1"/>
<h:inputText id="loginField2" value="#{bean2.login}" rendered="someCondition2"/>