JSF 2.0's ui:repeat tag gets the value of java bean(arraylist) as it's value property but size property doesn't. I am using the ui repeat inside of a datatable which shows statuses iteratively and ui repeat shows comments for each status. I am giving the size property of ui repeat from a java class because each status has a different number of comments. Therefore size should be decided dynamically. Here is the summary of what i've done. Model classes:
#ManagedBean
#RequestScoped
public class Comment {
private String commentAuthorName;
//getter and setter
}
This represents the Status class which has a list of comments:
#ManagedBean
#RequestScoped
public class Status {
private ArrayList<Comment> commentList;
private int numOfComments;
//getter and setter
}
This is giving an idea about StatusBean class:
#ManagedBean
#SessionScoped
public class StatusBean {
List<Status> panelList = new ArrayList<Status>();
List<Comment> commentList = new ArrayList<Comment>();
public static void process() {
panelList = StatusService.getPersonalStatus(log.getLoggeduser());//means fill list
commentList = StatusService.getPersonalComments(panelList);//gets comments via related statuses
for (int i=0; i<panelList.size(); i++) { //for each status
Status status = panelList.get(i);
for(Comment comment : commentList) { //for each comment of each status
status.setNumOfCommentsShown(1);
}
}
}
}
And view layer is sth like below. Ui repeat included in PrimeFaces DataTable to be able to show each comment for each status. I am using datatable because it has live scroll and it has to show all statuses iteratively and ui repeat looks best to show each comment for each status.
<p:dataTable liveScroll="true" value="#{StatusBean.panelList}"
var="Status" scrollable="true">
<ui:repeat var="Comment" value="#{Status.commentList}"
size="#{Status.numOfComments}"></ui:repeat>
</p:dataTable>
Debug result shows the #{Status.numOfComments} is correctly filled with expected integer but still it's not working. But if i write size=3 manually, it gives the expected result.
As far as I see there are no answer up till today, so I'll answer your question and give you some ideas on what I would have definitely changed in your stack.
Analysis of your problem
I have written a code similar to yours regarding the usage of <ui:repeat> with size attribute specified by a managed bean property and it didn't work for me either. No matter how hard I tried to set attribute's value by EL it didn't work. Moreover, it didn't work for the simplest EL like #{5} as well. I don't know where the problem stems from, but I think that the experiences ones here will enlighten us of why it is happening, will you?
Probably it is a glitch of the JSF <ui:repeat> component, then we shall give rise to an issue regarding it. If it is a feature it would be nice to understand it fully.
A working example of your code, as I understand it
Regarding the above code, there are many simple workarounds. In case you are so insistent on using the <ui:repeat> component I'll provide you with the basic working example. Your view layer is backed by a JSF managed bean and two model classes. My solution uses <ui:param> and <ui:fragment>. Here we go.
The view:
<p:dataTable value="#{statusBean.statusesList}" var="status">
<p:column headerText="Status name">
<h:outputText value="#{status.statusName}"/>
</p:column>
<p:column headerText="Status comments">
<ul>
<ui:param name="max" value="#{status.numOfComments}"/>
<ui:repeat var="comment" value="#{status.commentList}" varStatus="statusVar">
<ui:fragment rendered="#{statusVar.index lt max}">
<li>
<h:outputText value="Author: #{comment.authorName}; comment: #{comment.description}"/>
</li>
</ui:fragment>
</ui:repeat>
</ul>
</p:column>
</p:dataTable>
The model:
public class Comment {
private String authorName;
private String description;
}
with
public class Status {
private List<Comment> commentList;
private int numOfComments;
private String statusName;
}
The managed bean:
#ManagedBean
#RequestScoped
public class StatusBean {
private List<Status> statusesList;
public StatusBean() {
Status status;
List<Status> statusesList = new ArrayList<Status>();
Comment comment;
List<Comment> commentList;
for(int s = 0; s < 10; s++) {
commentList = new ArrayList<Comment>();
for(int c = 0; c < 20; c++) {
commentList.add(new Comment("User " + (s + 1) + "-" + (c + 1), "Description for comment " + (s + 1) + "-" + (c + 1)));
}
statusesList.add(new Status(commentList, (s + 1), "Status " + (s + 1)));
}
this.statusesList = statusesList;
}
}
Things I would definitely change in your code
After the code is working I would like to state some improvements I'd make.
Make your model a model: get rid of #ManagedBean and #...Scoped annotations and make them (detached) #Entity classes instead, delvered by your service bean;
Try to make the model as simple as possible, retaining the functionality of your application. My example uses only a list of statuses as data holders in your bean: you do not need a duplicate list of comments as it is already there for you in a Status object;
Do NOT use static methods inside beans, rather (pre)load nesessary data in your page action method (Servlet 3.0), or on preRenderView listener (Servlet 2.5), or in a #PostConstruct method. Make a good choice of bean scopes. Consulting an excellent overview by BalusC would be a great starting place;
Preload only the data you need from your data source with a service method call: if you are willing to show a limited amount of comments why fetch them from your datasource? Limit the result sets depending on what you need (remember, if you would like to implement 'view all' feature you may update your elements with ajax and load the rest). Making it work this way eliminates the need for usage of size="...";
Use standard UI components or the components of the component library of your choice, which is Primefaces, so try to follow Luiggi Mendoza's suggestions.
The sample kick-off example of a managed bean follows:
#ManagedBean
#RequestScoped
public class StatusBean {
private List<Status> statusesList;
#EJB
private StatusService statusService;
#ManagedProperty(value="#{user}")
private User user;
#PostConstruct
public void init() {
statusesList = statusService.getStatuses(user);
}
public List<Status> getStatusesList() {
return statusesList;
}
}
Related
Let's assume a simple Jsf example with a xhtml page, a ManagedBean, a service and an JPA entityClass. I have a lot of usecases with the following structure:
Hold an entity in my bean
Do actions on the entity
Do rendering on the updated entity
Some easy example, so everyone will understand
Entity:
public class Entity {
private long id;
private boolean value;
...
// Getter and Setter
}
Dao:
public class EntityService {
// Entity Manger em and other stuff
public void enableEntity(long id) {
Entity e = em.find(id);
e.value = true;
em.persist(e);
}
}
Managed Bean:
#ManagedBean
#RequestScoped/ViewScoped
public class EntityBean() {
#EJB
private EntityService entityService;
private Entity entity;
#PostConstruct
public void init() {
// here i fetch the data, to provide it for the getters and setters
entity = entityService.fetchEntity();
}
public void enableEntity() {
entityService.enableEntity(entity.getId);
}
// Getter and Setter
}
and finally a simple xhtml:
<html>
// bla bla bla
<h:panelGroup id="parent">
<h:panelGroup id="disabled" rendered="#{not EntityBean.entity.value}>
<p:commandButton value="action" action="#{EntityBean.enableEntity}" update="parent" />
</h:panelGroup>
<h:panelGroup id="enabled" rendered="#{EntityBean.entity.value}>
// other stuff that should become visible
</h:panelGroup>
</h:panelGroup>
</html>
What i want to achieve:
Always show the up to date entity in every request!
What i already tried
I tried with a dao-fetch in my getter. But you can read everywhere that this is bad practice, because jsf will call the getter more than once (but for now the only way i can keep them really up to date).
I tried RequestScoped Beans. But the Bean will be created before the action is done, and is not recreated on the update call and the value will be outdated (Makes sense, since this is one request, and the request starts with the click on the button).
I tried ViewScoped Beans and added an empty String return value to my method. My hope was, that this redirection will recreate the Bean after the action was processed. But this was not the case.
I tried to call the refetch function manually after every method i used. But I have some cross bean actions on the same entity (My real entities are way more complex than this example). So the different Beans do not always know, if and when the entity has changed.
My Questions:
Can this be done with any kind of Scope? Let's say that every request will fetch the data from my PostConstruct again.
There must be a better solution than the dao-fetch in the getter method
This seems to be a fundamental problem for me, because getting the up to date data is essential for my app (data is changed often).
Using Primefaces 6.1 and Wildfly 10.x
What do you think about this?
A request scoped bean which will be created for update, too and does only one fetchEntity() per request.
<f:metadata>
<f:viewAction action="#{entityBean.load()}" onPostback="true"/>
</f:metadata>
#ManagedBean
#RequestScoped
public class EntityBean() {
#EJB
private EntityService entityService;
private Entity entity = null;
public void load() {}
public Entity getEntity() {
if(entity == null) {
entity = entityService.fetchEntity();
}
return entity;
}
public void enableEntity() {
entityService.enableEntity(getEntity().getId);
}
// Getter and Setter
}
I have bilateral One to Many Relationship in a JSF + JPA application. I want to list out only filtered items from the list in the view. Doing it in the controller is difficult and is it possible to filted like below?
<p:selectOneListbox id="cmbField" value="#{investigationItemController.current}" >
<f:selectItems value="#{investigationItemController.currentInvestigation.reportItems}" var="ri" itemLabel="#{ri.name}" itemValue="#{ri}" itemRendered="#{ri.retired ne true and ri.ixItemType eq 'Value'}" />
</p:selectOneListbox>
As itemRendered is not an attribute, I tried this, but failed.
<p:selectOneListbox id="cmbField" value="#{investigationItemController.current}" >
<ui:repeat value="#{investigationItemController.currentInvestigation.reportItems}" var="ri" >
<h:panelGroup rendered="#{ri.retired ne true and ri.ixItemType eq 'Value'}" >
<f:selectItem itemLabel="#{ri.name}" itemValue="#{ri}" />
</h:panelGroup>
</ui:repeat>
</p:selectOneListbox>
Owner Entity is as below.
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class InvestigationItem extends ReportItem implements Serializable {
private static final long serialVersionUID = 1L;
#OneToMany(mappedBy = "investigationItem", cascade= CascadeType.ALL)
List<InvestigationItemValue> investigationItemValues;
public List<InvestigationItemValue> getInvestigationItemValues() {
return investigationItemValues;
}
public void setInvestigationItemValues(List<InvestigationItemValue> investigationItemValues) {
this.investigationItemValues = investigationItemValues;
}
}
The other entity is as follow
#Entity
public class InvestigationItemValue implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
private InvestigationItem investigationItem;
private String name;
//Getters and setters and some other fields
}
The important code set of the controller class is as follows
#ManagedBean
#SessionScoped
public final class InvestigationItemController implements Serializable {
private static final long serialVersionUID = 1L;
#EJB
private InvestigationItemFacade ejbFacade;
private InvestigationItem current;
private Investigation currentInvestigation;
// Other code goes here
}
How can I filter items when using f:selectItem or f:selectItems in JSF/Primefaces ?
(I will explain my real world situation why I desperately need this functionality.
I am developing a medical applications and terms are confusing. A test or an Investigation (like Full Blood Count or Urine Full Report) can have several properties. Test is represented as an Investigation Entity. A test comprised of one or more fields (like Haemoglobin, White Cell Count for FBC and Colour, Apperance for UFR). It is represented as InvestigationItem. A single InvestigationItem may have a list of possible values of which one is selected for in different occasions. They are identified as InvestigationItemValue. So one InvestigationItem has a list of InvestigationItemValue(s). But some of the InvestigationItemValues may be of string type while others are of numeric, for example. The user needs to select one InvestigationItemValue when designing a new investigation of one particular type. So filtering is needed in the view.)
The PrimeFaces <p:selectOneListbox> actually hides the <select><option> and generates a <div><ul><li> which allows you much more CSS styling freedom.
You can just make use of itemDisabled attribute and then use CSS to set disabled items to display: none.
<p:selectOneListbox ...>
<f:selectItems value="#{investigationItemController.currentInvestigation.reportItems}"
var="ri" itemLabel="#{ri.name}" itemValue="#{ri}"
itemDisabled="#{not ri.retired and ri.ixItemType eq 'Value'}" />
</p:selectOneListbox>
with this CSS
.ui-selectlistbox-item.ui-state-disabled {
display: none;
}
As to your failed attempt with <ui:repeat>; it failed because the <f:selectItem> is evaluated during view build time, not during view render time. However, the <ui:repeat> runs during view render time. During that moment the <f:selectItem> is nowhere available in the component tree. It's supposed to be attached to an UISelectOne/UISelectMany parent during view build time.
JSTL runs during view build time, like <f:selectItem>, so using JSTL <c:forEach> for the loop and <c:if> for the conditional building (not rendering!) should do it as well:
<p:selectOneListbox ...>
<c:forEach items="#{investigationItemController.currentInvestigation.reportItems}" var="ri">
<c:if test="#{not ri.retired and ri.ixItemType eq 'Value'}">
<f:selectItem itemLabel="#{ri.name}" itemValue="#{ri}" />
</c:if>
</c:forEach>
</p:selectOneListbox>
You only need to take into account that this breaks view scoped beans when using a Mojarra version older than 2.1.18.
See also:
JSTL in JSF2 Facelets... makes sense?
Versions:
NetBeans: 7.2.1
PrimeFaces: 3.5.3
GlassFish: 3.1.2
JDK 1.6
I've been trying to find related issues and have found topics that are close, but not quite what I'm looking for. I'm trying to do something similar to the p:schedule demo from PrimeFaces ShowCase where I want a dialog to appear showing the details of the event clicked.
I think the issue is coming from calling the listener method from the backing bean. When I go to type in the listener method in the p:ajax tag, NetBeans forces me to pass in a parameter like:
listener="#{cmodel.onEventSelect(e)}"
which I don't think is necessary as I don't have a value to pass in anyways.
I'm thinking either:
Something is up with NetBeans that doesn't recognize the method as a listener. (Since i keep seeing multiple examples of people calling the method without needing to pass a parameter.)
or
I'm not registering the method as a listener properly in the Model.
Also, I have directly copied and pasted the demo from the ShowCase into a project and it didn't work which is making me lean more towards an issue with NetBeans. (that is, the dialog appears but with no information on the event that was selected)
So to summarize; events are showing up as they should on the schedule itself, I just cant get the dialog to show the event details of the event that was selected.
Any help would be greatly appreciated!
View Layer:
<h:form>
<p:schedule id="nelsonsSchedule" value="#{cmodel.scheduleModel}" showHeader="true"
leftHeaderTemplate="none" rightHeaderTemplate="prev, next today"
draggable="false" timeZone="UTC" styleClass="schedule">
<p:ajax event="eventSelect" listener="#{cmodel.onEventSelect}"
update="eventDialog eventDetails" oncomplete="eventDialog.show()"/>
</p:schedule>
<p:dialog id="eventDialog" widgetVar="eventDialog" header="EventDetails">
<p:panel id="eventDetails">
<h:outputLabel value="#{cmodel.selectedEvent.title}" />
</p:panel>
</p:dialog>
</h:form>
Backing Bean:
#ManagedBean(name = "cmodel")
#SessionScoped
public class CalendarModel implements Serializable {
private ScheduleModel scheduleModel;
private List<ScheduleEvent> allScheduledGames;
private DefaultScheduleEvent gameEvent;
public ScheduleEvent selectedEvent;
List<Game> allGames;
#PersistenceContext
private EntityManager em;
public CalendarModel() {
}
#PostConstruct
public void init() {
allScheduledGames = new ArrayList<ScheduleEvent>();
allGames = new ArrayList<Game>();
allGames = em.createNamedQuery("Game.findAll").getResultList();
/*create list of games to put into the ScheduleModel*/
for (int i = 0; i < allGames.size(); i++) {
gameEvent = new DefaultScheduleEvent(allGames.get(i).getOpponent() +
"\n\n\n" + allGames.get(i).getTimeOfGame(),
allGames.get(i).getDateOfGame(),
allGames.get(i).getDateOfGame());
if(allGames.get(i).getHomeAway().equals("away")){
gameEvent.setStyleClass("away");
} else{
gameEvent.setStyleClass("home");
}
gameEvent.setData(allGames.get(i));
allScheduledGames.add(gameEvent);
}/*end for*/
scheduleModel = new DefaultScheduleModel(allScheduledGames);
}/*end init()*/
public void onEventSelect (SelectEvent e) {
selectedEvent = new DefaultScheduleEvent();
selectedEvent = (ScheduleEvent) e.getObject();
}
In case anyone runs into the same issue - It was NetBeans. I upgraded to 7.3 and don't have the problem anymore. Although another has come up where itellisense doesn't recognize a hashmap from the backing bean, but that's for another question.
What is the difference between using value and binding with JavaServer Faces, and when would you use one as opposed to the other? To make it clearer what my question is, a couple of simple examples are given here.
Normally with JSF in the XHTML code you would use "value" as here:
<h:form>
<h:inputText value="#{hello.inputText}"/>
<h:commandButton value="Click Me!" action="#{hello.action}"/>
<h:outputText value="#{hello.outputText}"/>
</h:form>
Then the bean is:
// Imports
#ManagedBean(name="hello")
#RequestScoped
public class Hello implements Serializable {
private String inputText;
private String outputText;
public void setInputText(String inputText) {
this.inputText = inputText;
}
public String getInputText() {
return inputText;
}
// Other getters and setters etc.
// Other methods etc.
public String action() {
// Do other things
return "success";
}
}
However, when using "binding", the XHTML code is:
<h:form>
<h:inputText binding="#{backing_hello.inputText}"/>
<h:commandButton value="Click Me!" action="#{backing_hello.action}"/>
<h:outputText value="Hello!" binding="#{backing_hello.outputText}"/>
</h:form>
and the correspondibg bean is called a backing bean, and is here:
// Imports
#ManagedBean(name="backing_hello")
#RequestScoped
public class Hello implements Serializable {
private HtmlInputText inputText;
private HtmlOutputText outputText;
public void setInputText(HtmlInputText inputText) {
this.inputText = inputText;
}
public HtmlInputText getInputText() {
return inputText;
}
// Other getters and setters etc.
// Other methods etc.
public String action() {
// Do other things
return "success";
}
}
What practical differences are there between the two systems, and when would you use a backing bean rather than a regular bean? Is it possible to use both?
I have been confused about this for some time, and would most appreciate having this cleared up.
value attribute represents the value of the component. It is the text that you see inside your text box when you open the page in browser.
binding attribute is used to bind your component to a bean property. For an example in your code your inputText component is bound to the bean like this.
#{backing_hello.inputText}`
It means that you can access the whole component and all its properties in your code as a UIComponent object. You can do lot of works with the component because now it is available in your java code.
For an example you can change its style like this.
public HtmlInputText getInputText() {
inputText.setStyle("color:red");
return inputText;
}
Or simply to disable the component according to a bean property
if(someBoolean) {
inputText.setDisabled(true);
}
and so on....
Sometimes we don't really need to apply the value of UIComponent to a bean property. For example you might need to access the UIComponent and work with it without applying its value to the model property. In such cases it's good to use a backing bean rather than a regular bean. On the other hand in some situations we might need to work with the values of the UIComponent without any need of programmatic access to them. In this case you can just go with the regular beans.
So, the rule is that use a backing bean only when you need programmatic access to the components declared in the view. In other cases use the regular beans.
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