On the Dashboard each User can see some Basic Stats. Lets take for example the "last login" Date. (But there are many more stats / values / settings to display)
The XHTML Files looks simplidfied like this:
<h:outputText value="statisticController.lastLoginDate()" />
The Bean itself uses #Inject to get the Session and therefore the current user:
#Named
#RequestScoped
public StatisticController{
#Inject
private mySessionBean mySession;
#PostConstruct
private void init(){
//load stats for mySession.currentUser;
}
}
Now, i want to generate a List where for example a certain role can view the values for ALL users. Therefore i can't use the Session Inject anymore, because the StatisticController now needs to be generated for multiple Users.
Having regular Classes this would not be a big problem - add the userEntity to the constructor. What is the "best practice" to solve this in JSF?
If i modify the StatisticController to something like this:
#Named
#RequestScoped
public StatisticController{
public void init(User user){
//load stats for user;
}
}
i would need to call init(user) manually of course. How can this be achieved from within a Iteration in the XHTML file?
I could refactor it so the valueLoading happens in the actual getter method, and iterate like this:
<ui:repeat var="user" value="#{userDataService.getAllUsers()}">
<h:outputText value="statisticController.lastLoginDate(user)" />
...
</ui:repeat>
But then i would need to load "every" value seperate, which is bad.
So a way like this would be "better":
<ui:repeat var="user" value="#{userDataService.getAllUsers()}">
statisticController.init(user);
<h:outputText value="statisticController.lastLoginDate()" />
...
</ui:repeat>
However this doesnt look very comfortable either. Further more doing things like this, will move nearly "all" Backend Stuff into the Render Response Phase, which is feeling wrong.
Any Ideas / Tipps how to solve this in a way that's not feeling "like a workaround"?
Create a new model wrapping those models.
public class UserStatistics {
private User user;
private Statistics statistics;
// ...
}
So that you can just use e.g.
public class UserStatisticsBacking {
private List<UserStatistics> list;
#EJB
private UserService userService;
#EJB
private StatisticsService statisticsService;
#PostConstruct
public void init() {
list = new ArrayList<UserStatistics>();
for (User user : userService.list()) {
list.add(new UserStatistics(user, statisticsService.get(user)));
}
}
// ...
}
(better would be to perform it in a new UserStatisticsService though)
with
<ui:repeat value="#{userStatisticsBacking.list}" var="userStatistics">
<h:outputText value="#{userStatistics.user.name}" />
<h:outputText value="#{userStatistics.statistics.lastLoginDate}" />
...
</ui:repeat>
An alternative to using a wrapped model proposed by BalusC is to store two separate lists with model data. With this approach you don't need to introduce modifications.
Following this route you'll be iterating over one list with <ui:repeat> and, ensuring equality of sizes of both lists, get second list element by index, which in turn is available via varStatus attribute that exports the iteration status variable.
<ui:param name="stats" value="#{bean.stats}/>
<ui:repeat value="#{bean.users}" var="user" varStatus="status">
<h:outputText value="#{user}/>
<h:outputText value="#{stats[status.index]}/>
</ui:include>
Population of lists may be done beforehand in PostConstruct method:
private List<User> users;
private List<Statistic> stats;
#PostConstruct
public void init() {
users = userService.list();
stats = statService.list();
}
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 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 am having a two level map Map<String,HashMap<String,String>> which i need to display using a <p:dataTable>. The code of managed bean is as follows:
#ManagedBean(name="MyBean")
public class MyBean{
private Map<String,HashMap<String,String>> twoDimentionalMap;
public void getMapData(){
twoDimentionalMap=getDataFromDataStore();
}
}
Now I am using this map in my view.xhtml file as follows:
<p:dataTable var="entrySet1" value="#{MyBean.twoDimentionalMap.entrySet()}">
<p:columns var="entrySet2" value="#{entrySet1.getValue()}">
#{entrySet2.getKey()} - #{entrySet2.getValue()}
</p:columns>
</p:dataTable>
I also tried using
<p:dataTable var="entrySet1" value="#{MyBean.twoDimentionalMap.entrySet()}">
<p:columns var="entrySet2" value="#{MyBean.twoDimentionalMap.get(entrySet1.getKey()).getValue()}">
#{entrySet2.getKey()} - #{entrySet2.getValue()}
</p:columns>
</p:dataTable>
I even tried converting the outer map to a list:
List<HashMap<String,String>> twoDimentionalMap;
However nothing is displayed on datatable. The execution shows no error but there is nothing displayed on the page.
Kindly suggest if I am doing something wrong or if <p:columns> is having any issue handling maps.
Thanks
since the keys of outer map do not have a meaning, converting the outer map to a list is correct.
but your approach to retrieve column names from xhtml does not seem valid. you need to get them independently from the current iteration variable entrySet1, otherwise you add a third dimension to the operation, which data table cannot handle.
we need to assume that all keys are same across the listed maps.
here is the code for xhtml:
<p:dataTable var="entrySet1" value="#{testMB.twoDimensionalMap}">
<p:columns var="keySet2" value="#{testMB.columnNames}">
#{keySet2} - #{entrySet1[keySet2]}
</p:columns>
</p:dataTable>
and for the bean:
#Named
#ViewScoped
public class TestMB implements Serializable {
private List<HashMap<String,String>> twoDimensionalMap;
public TestMB()
{
getMapData();
}
private void getMapData(){
//twoDimentionalMap=getDataFromDataStore();
twoDimensionalMap = new ArrayList<HashMap<String,String>>();
twoDimensionalMap.add(new HashMap<String,String>());
twoDimensionalMap.get(0).put("key0", "value00");
twoDimensionalMap.get(0).put("key1", "value01");
twoDimensionalMap.add(new HashMap<String,String>());
twoDimensionalMap.get(1).put("key0", "value10");
twoDimensionalMap.get(1).put("key1", "value11");
}
public Set<String> getColumnNames()
{
return twoDimensionalMap.size() > 0 ? twoDimensionalMap.get(0).keySet() : new HashSet<String>();
}
public List<HashMap<String, String>> getTwoDimensionalMap() {
return twoDimensionalMap;
}
}
This question already has answers here:
How to ajax-refresh dynamic include content by navigation menu? (JSF SPA)
(3 answers)
Closed 1 year ago.
I'm relatively new to JSF and trying to learn how current JSF 2 applications are designed. I've seen reference to single page applications that use ajax. Can someone fill me in on some of the techniques used and / or point me to a model or book? The books I've seen (JSF Complete Reference etc.) are good for basic tech issues but I can't find a source for current design techniques.
Thanks
Dave
In order to implement your Single Page Application, you should state which piece of your page should be rendered. This can be accomplished making use of a boolean flag such as create, edit, list, and so on. For instance, see the following (Just relevant code)
<h:body>
<h:form rendered="#{userController.stateManager.create}">
<h:panelGroup rendered="#{not empty facesContext.messageList or userController.stateManager.failure}">
<!--render error message right here-->
</h:panelGroup>
<div>
<label>#{messages['br.com.spa.domain.model.User.name']}</label>
<h:inputText value="#{user.name}"/>
</div>
<h:commandButton action="#{userController.create}">
<f:ajax execute="#form" render="#all"/>
<f:actionListener type="br.com.spa.web.faces.listener.StateManagerActionListener" />
<f:setPropertyActionListener target="#{userController.stateManager.create}" value="true"/>
<f:setPropertyActionListener target="#{userController.user}" value="#{user}" />
</h:commandButton>
</form>
</h:body>
Notice that our form will be rendered when a flag create is true - See second line above. To wrap our flags, we create a classe named StateManager as follows
/**
* I am using lombok, which takes care of generating our getters and setters. For more info, please refer http://projectlombok.org/features/index.html
*/
#Setter #Getter
public class StateManager {
private boolean create;
private boolean edit;
private boolean list;
}
Now, because we are using only a single page, we should use a ViewScoped managed bean, which keep our managed bean scoped active as long as you are on the same view - Is it a single page application, right ? So, no navigation. With this in mind, let's create our managed bean.
#ManagedBean
#ViewScoped
public class UserController implements StateManagerAwareManagedBean {
private #Inject UserService service;
private #Getter #Setter stateManager = new StateManager();
private #Getter #Setter List<User> userList = new ArrayList<User>();
private #Getter #Setter User user;
#PostConstruct
public void initialize() {
list();
}
public void create() {
service.persist(user);
stateManager.setCreate(false);
stateManager.setList(true);
stateManager.setSuccess(true);
}
public void edit() {
service.merge(user);
stateManager.setEdit(false);
stateManager.setList(true);
stateManager.setSuccess(true);
}
public void list() {
userList = service.list();
stateManager.setList(true);
}
}
For each action method, we define which piece of our page should be rendered. For instance, consider that our form was processed, covering all of JSF lyfecycle, which implies that their values was successfully converted and validated, and our action method invoked. By using as example our create action method - see above -, we set its create flag as false because our form was converted and validated, so we do not need to show it again (Unless you want). Furthermore, we set both list and success flag as true, which indicates that the list of our page should be rendered and our form was successfully processed - You could use this flag to show something like "User created" such as bellow
<h:panelGroup rendered="#{userController.stateManager.success}">
#{messages['default.created.message']}
</h:panelGroup>
Now, let's discuss which piece of our page should be rendered when it is called for the first time. Maybe you do not know but a void method annotated with #PostConstruct will be called first. So we define which piece of our page should be rendered. In our example, we call list method, which sets its list flag as true and populate a backing list.
#PostConstruct
public void initialize() {
list();
}
Finally, let's review the following order nested within h:commandButton
<h:commandButton action="#{userController.create}">
<f:ajax execute="#form" render="#all"/>
<f:actionListener type="br.com.spa.web.faces.listener.StateManagerActionListener" />
<f:setPropertyActionListener target="#{userController.stateManager.create}" value="true"/>
<f:setPropertyActionListener target="#{userController.user}" value="#{user}" />
</h:commandButton>
First of all, you should call an ActionListener - here called StateManagerActionListener - which takes care of resetting any StateManager - code bellow. It must be called first before any other setPropertyActionListener designed to control any flag because the order defined within h:commandButton is the order in which they will be called. keep this in mind.
public class StateManagerActionListener implements ActionListener {
public void processAction(ActionEvent e) throws AbortProcessingException {
Map<String,Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
for(Map.Entry<String,Object> entry: viewMap.entrySet()) {
if(entry.getValue() instanceof StateManagerAwareManagedBean) {
((StateManagerAwareManagedBean) entry.getValue()).setStateManager(new StateManager());
}
}
}
}
StateManagerAwareManagedBean - used in our ViewScoped Managed bean -, which allows that we reset any StateManager of any ManagedBean instead of resetting one by one in our ActionListener, is defined as follows
public interface StateManagerAwareManagedBean {
StateManager getStateManager();
void setStateManager(StateManager stateManager);
}
Second, after defining our ActionListener, we use a setPropertyActionListener which set the flag which controls the enclosing piece of the view as true. It is needed because our form is supposed to be not converted and validated. So, in our action method, we set this flag as false as discussed before.
A couple of notes
User is marked as a RequestScoped ManagedBean so that it can not be injected into a ViewScoped one using a ManagedProperty because its scope is shother. To overcome this issue, i set its value by using a <f:setPropertyActionListener target="#{userController.user}" value="#{user}"> - See our form
Our example use JEE features which need a proper Application Server. For more info, refer http://docs.oracle.com/javaee/6/tutorial/doc/
ManagedBean can play different roles such as a Controller, DTO and so on. When it play a role of a Controller, i prefer suffix its name with Controller. For more info, refer http://java.dzone.com/articles/making-distinctions-between
Can we add multiple rows in the database table from one JSF page in one go?
Actually i want to make an attendance page, i want to take attendance of all employees in one JSF page in one go that is when user will press save button then all employees attendance will get save in database.
Is it possible to achieve this using managed bean and JSF page? Kindly give me some idea that how it can be achieved?
I've done insert,view,update and delete for single row.
Yes, just use a <h:dataTable>. Imagine that you've a bean like this
#ManagedBean
#ViewScoped
public class EmployeeManager {
private List<Employee> employees;
#EJB
private EmployeeService employeeService;
#PostConstruct
public void init() {
employees = new ArrayList<Employee>();
employees.add(new Employee());
employees.add(new Employee());
employees.add(new Employee());
// ...
// Do whatever you find necessary. Maybe offer an `Add row` button?
}
public void save() {
employeeService.save(employees);
// ...
}
// ...
}
then you can present it as follows
<h:form>
<h:dataTable value="#{employeeManager.employees}" var="employee">
<h:column><h:inputText value="#{employee.firstname}" /></h:column>
<h:column><h:inputText value="#{employee.lastname}" /></h:column>
<h:column><h:inputText value="#{employee.email}" /></h:column>
</h:dataTable>
<h:commandButton value="Save" action="#{employeeManager.save}" />
</h:form>
See also:
The benefits and pitfalls of #ViewScoped - contains a CRUD example