I have a need to continuously update a page with new data produced by managedbeans. Managedbean creates a new List of values for every few minutes and UI must create a OutputText for each of the value in the newly created List by managedbean. Catch is, the UI should retain the outputTexts and add new one's for every request, it should not refresh/remove old outputTexts.
My code is like below - updates the same outputText fields for each ajax call, which I need to change like mentioned above.
<c:forEach var="data" items="#{myBean.dataList}">
<p:fieldset legend="#{data}" toggleable="true" toggleSpeed="500">
<h:panelGrid columns="2" cellpadding="5" border="0">
<p:scrollPanel style="height:300px">
<p:poll interval="2" listener="#{myBean.getDataList}" update="field1 field2"/>
<h:outputText value="#{data.field1}" id="field1"/>
<h:outputText value="#{data.field2}" id="field2"/>
</p:scrollPanel>
</h:panelGrid>
</p:fieldset>
</c:forEach>
so for each ajax call, there may be few data items produced by managedbeans and for each of those item there should be a new outputText field.
You can use datalist for <h:outputText value="#{data.field1}" id="field1"/>
Make a list and use them as outputtext, yes you need some css formatting because datalist used default css. sample code
<p:poll interval="3" listener="#{yourbean.yourmethod}" update="<updatecomponet>" />
<p:dataList value="#{yourbean.tempList}" var="s" id="updatecomponet">
<p:outputLabel value="#{s}"></p:outputLabel>
</p:dataList>
Note: You need to add value in list when poll call listener. it is working in my project, let me know for anything else. Hope this will help.
Maybe not the perfect solution, but you can use a DataTable with lazy loading, the poll component can update the datatable page and you don't need to update a huge amount of components.
Edit:
xhtml
<p:dataTable var="v"
value="#{myBean.getLazyList}"
scrollRows="1"
liveScroll="true"
rows="20"
scrollHeight="300"
lazy="true"
scrollable="true"
id="tsUpdates">
<p:column>
<h:outputText value="#{v.fields1}" />
</p:column>
</p:dataTable>
Bean
public class Bean {
private LazyDataModel<MyClass> lazyList;
#Named("myBean")
#ViewScoped
#PostConstruct
public void init() {
lazyList = new LazyMyClassModel(); //where myList is your data source
}
//getters and setters
}
LazyModel
public class LazyMyClassModel extends LazyDataModel<MyClass>{
private Integer findAllCount;
public LazyMyClassModel(){
}
#Override
public List<MyClass> load(int first, int pageSize, String sortField, SortOrder sortOrder,Map<String, String> filters){
List<MyClass> myList = //your live source of data
List<MyClass> data = new ArrayList<MyClass>();
data = getSubList(MyList, first, pageSize)
if (findAllCount == null) {
findAllCount = myList.size
this.setRowCount(findAllCount);
}
return data
}
public List<MyClass> getSubList(List<MyClass> list, int fromIndex, int toIndex) {
int size = list.size();
if (fromIndex >= size || toIndex <= 0 || fromIndex >= toIndex) {
return Collections.emptyList();
}
fromIndex = Math.max(0, fromIndex);
toIndex = Math.min(size, toIndex);
return list.subList(fromIndex, toIndex);
}
}
Related
I have a datatable on jsf page with next columns: product name, price and input field for product amount. By click on the button I should load elements from datatable to collection where product amount is more than 0(method addCups).
How to create it?
Thank you.
Managed Bean
#ManagedBean
#SessionScoped
public class OrderBean extends SpringBeanAutowiringSupport {
#Autowired
private OrderDAO orderDAO;
#Autowired
private OrderPositionDAO orderPositionDAO;
private Map<Long, Integer> selectedItems = new HashMap<>();
private Integer quantityOfCups;
public Map<Long, Integer> getSelectedItems() {
return selectedItems;
}
public void setSelectedItems(Map<Long, Integer> selectedItems) {
this.selectedItems = selectedItems;
}
public Integer getQuantityOfCups() {
return quantityOfCups;
}
public void setQuantityOfCups(Integer quantityOfCups) {
this.quantityOfCups = quantityOfCups;
}
public void addCups(Long id, Integer numOfCups){
if(numOfCups > 0){
selectedItems.put(id, numOfCups);
}
if(numOfCups == 0){
selectedItems.remove(id);
}
System.out.println(selectedItems);
}
JSF page
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:form>
<p:dataTable value="#{coffeeBean.allCoffee}" var="coffee">
<p:column>
<h:outputText value="#{coffee.coffeeName}"/>
</p:column>
<p:column>
<h:outputText value="#{coffee.costForCup}"/>
</p:column>
<p:column>
<p:inputText value="#{orderBean.quantityOfCups}"/>
</p:column>
</p:dataTable>
<h:commandButton value="add cups" action="#{orderBean.addCups(coffee.id, orderBean.quantityOfCups)}"/>
</h:form>
There are number of ways to achieve this but, with minimal modifications of your posted code, you can achieve it on following way:
add one hidden data table column with your coffee ID and define widgetVar property of data table ,
use JavaScript method to iterate through data table values and pick up coffee ID-quantity pairs,
use p:remoteCommand to send array of coffee cups as JSON string to backing bean
inside bean method parse passed JSON string and add/remove coffee cups to/from your order
Your xhtml page h:form would look like this
<h:form>
<p:dataTable widgetVar="dataTableWidget" value="#{coffeeBean.allCoffee}" var="cofee">
<p:column style="display:none">
<h:outputText value="#{coffee.id}"/>
</p:column>
<p:column headerText="name">
<h:outputText value="#{coffee.coffeeName}"/>
</p:column>
<p:column headerText="price">
<h:outputText value="#{coffee.costForCup}"/>
</p:column>
<p:column headerText="quantity">
<p:inputText/>
</p:column>
</p:dataTable>
<p:commandButton value="Add cups" type="button" onclick="collectOrderFromTable()"/>
<p:remoteCommand name="sendToOrderBean" actionListener="#{orderBean.addCups}"/>
</h:form>
with added java script
<script>
//<![CDATA[
function collectOrderFromTable() {
var coffeeCups = [];
//get table rows
var tableRows = PF('dataTableWidget').tbody[0].childNodes;
//loop through rows
for (i = 0; i < tableRows.length; i++) {
//get cells of current row
var cells = tableRows[i].cells;
//get value of hidden ID column
var id = cells[0].innerText;
//get quantity from input field
var quantity = cells[3].firstChild.value;
//add new item to array
coffeeCups.push({id: id, quantity: quantity});
}
console.log("Coffee cups", JSON.stringify(coffeeCups));
sendToOrderBean([{name: 'coffeeCups', value: JSON.stringify(coffeeCups)}]);
}
//]]>
</script>
Method addCups in your OrderBean would be like this:
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
//...
//...
public void addCups() {
//get string passed from xhtml page
String values = (String) FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("coffeCups");
//parse JSON string to List
List<CoffeeQuantity> coffeCups = (ArrayList<CoffeeQuantity>) new Gson().fromJson(values, new TypeToken<ArrayList<CoffeeQuantity>>() {}.getType());
for (CoffeeQuantity cq : coffeCups) {
System.out.println("Coffee id: " + cq.getId() + ", Quantity:" + cq.getQuantity());
if (cq.getQuantity()>0){
selectedItems.put(cq.getId(), cq.getQuantity());
}else{
selectedItems.remove(cq.getId());
}
}
}
where CoffeeQuantity is helper class to make JSON string parsing easier
public class CoffeQuantity {
int id;
Integer quantity;
//getters and setters...
}
I am not able to remove an item from a arraylist.
What i tried is pasted below. Cannot seem to figure out what is wrong in below code.
The xhtml is
<p:dataTable id="emaildt" styleClass="hide-column-names"
style="width:70%" rowIndexVar="rowIndex"
value="#{myBean.emailAddressList}" var="email">
<p:column style="width:15%">
<p:inputText id="email" value="#{email.email}"
validatorMessage="Invalid email" onblur="TrimString(this)">
<f:validateRegex
pattern="^(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+#((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}$" />
<p:ajax event="blur"
global="false" listener="#{myBean.checkEmailExist(email)}" />
</p:inputText>
</p:column>
<p:column style="width:13%">
<p:spacer width="10" rendered="#{myBean.emailAddressList.size()>'1'}"/>
<p:commandLink id="delete" immediate="true"
actionListener="#{myBean.removeEmailFromList}"
update="emaildt" process="emaildt"
rendered="#{myBean.emailAddressList.size()>'1' && false}">
<h:graphicImage value="../images/button_remove.gif" />
<f:param name="rowToRemove" value="#{rowIndex}" />
</p:commandLink>
<p:spacer width="5" rendered="#{rowIndex == myBean.emailAddressList.size()-1}"/>
<p:commandLink id="addemailbtn" immediate="true"
actionListener="#{myBean.addEmail}"
style="float:right;" update="emaildt" process="#this emaildt"
rendered="#{rowIndex == myBean.emailAddressList.size()-1}">
<h:graphicImage value="../images/addbutton.jpg" />
</p:commandLink>
</p:column>
</p:dataTable>
Create a model class EmailAddressModel with string email and its getters and setters.
MyBean code is
List<emailAddressList> emailAddressList; //make its getters and setters
#PostConstruct
public void fillPage() {
emailAddressList = new ArrayList<EmailAddressModel>();
emailAddressList.add(new EmailAddressModel());
}
public void addEmail() {
try
{
EmailAddressModel emailAddress = new EmailAddressModel();
emailAddressList.add(emailAddress);
}
catch(IndexOutOfBoundsException I) {
log.warn("FL Warning", I);
}
}
public void removeEmailFromList() {
String rowIndex = null;
try {
rowIndex = FacesContext.getCurrentInstance().getExternalContext()
.getRequestParameterMap().get("rowToRemove");
int index = Integer.parseInt(rowIndex);
emailAddressList.remove(index);
} catch (Exception e) {
log.warn("FL Warning", e);
}
}
Issue is -> after we add and enter the values to first and second remove, click on delete of first row, then the second row gets deleted. On debugging, the first row is gets removed in backend arraylist but in front end, the second row gets deleted.
Hope that the above information is helpful.
Check the value of what's passed to rowIndex, as Boris the Spider mentioned, Java is zero indexed.
It's likely you're returning a rowIndex of 1, when the first item is indexed at 0.
To test, In your try-catch block you could use:
int index = Integer.parseInt(rowIndex) - 1;
Got the solution added atrribute resetValues="true" to p:commandLink.
Thank you all helping me..:)
My primefaces pickList source and target values do not change, I have followed the example in primefaces showcase
and also looked at several posts here but still am not able to solve the problem. I am using a list from the database to populate the source as follows:
private DualListModel<Course> courseModel;
public CourseBean() {
List<Course> target = new ArrayList<Course>();
List<Course> source = new ArrayList<Course>();
courseModel = new DualListModel<Course>(source, target);
}
...
//this DualListModel getter also populates the source with values from db
public DualListModel<Course> getCourseModel() {
courseModel.setSource(getCourseList());
return courseModel;
}
My converter is
import org.omnifaces.converter.SelectItemsConverter;
#FacesConverter("courseConverter")
public class CourseConverter extends SelectItemsConverter {
#Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
Integer id = (value instanceof Course) ? ((Course) value).getId()
: null;
return (id != null) ? String.valueOf(id) : null;
}
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String value) {
Course course = new Course();
course.setId(Integer.parseInt(value));
return course;
}
}
and finally my xhtml page is:
<h:form>
<p:pickList id="coursesOffered"
value="#{courseView.courseModel}" var="course"
itemValue="#{course}" itemLabel="#{course.courseTitle}"
converter="courseConverter" showSourceFilter="true"
showTargetFilter="true" filterMatchMode="contains">
<f:facet name="targetCaption">Selected</f:facet>
<f:facet name="sourceCaption">All Courses</f:facet>
<p:ajax event="transfer" listener="#{courseView.onTransfer}" />
<p:column style="width:10%">
#{course.courseCode}:
</p:column>
<p:column style="width:90%">
#{course.courseTitle}
</p:column>
</p:pickList>
<p:commandButton id="pojoSubmit" value="Submit"
update="displayPlayers" oncomplete="playerDialog.show()"
style="margin-top:5px" />
<p:dialog showEffect="fade" hideEffect="fade" widgetVar="playerDialog">
<h:panelGrid id="displayPlayers" columns="2">
<h:outputText value="Source: " style="font-weight:bold" />
<ui:repeat value="#{courseView.courseModel.source}"
var="course">
<h:outputText value="#{course.courseTitle}"
style="margin-right:5px" />
</ui:repeat>
<h:outputText value="Target: " style="font-weight:bold" />
<ui:repeat value="#{courseView.courseModel.target}"
var="course">
<h:outputText value="#{course.courseTitle}"
style="margin-right:5px" />
</ui:repeat>
</h:panelGrid>
</p:dialog>
</h:form>
The pickList is displayed correctly with source populated with values from database, however, upon clicking the button, the dialog just displays the original source list values and an empty target list even after transferring items on the interface. What am I missing?
I see some problems with your code. In the getter, you are reconstructing the DualList from the database, reseting any changes you've done so far.
try making you getter something like this:
public DualListModel<Course> getCourseModel() {
return this.courseModel;
}
Construct and load your list from the database in a method annotated #PostConstruct rather than in the constructor+getter.
public CourseBean() {}
#PostConstruct
public void init() {
List<Course> target = new ArrayList<Course>();
courseModel.setSource(getCourseList());
courseModel = new DualListModel<Course>(source, target);
}
Also annotate your bean #ViewScoped, so you dont construct a new bean with empty target-list on every request
#ManagedBean(name="courseView")
#ViewScoped
public class CourseBean {
}
Finally you also need a setter:
public void setCourseModel(DualListModel<Course> courseModel) {
this.courseModel = courseModel;
}
I have not really looked into the converter, when I did a picklist lasttime i took the converter included in primefaces showcase (Never tried the omnifaces one). Here is a link to the source: http://code.google.com/p/ind/source/browse/indicadorCensoJSF/src/ve/gob/ine/censo/beans/convert/PrimeFacesPickListConverter.java?spec=svn154&r=154
Complementary to the accepted answer, I had a problem with the Omnifaces list converter. Everything worked, except that the already picked items weren't being updated and "lost", it is, they were there in the view but the only items processed were the picked in the view, not the ones that were already set as targets in the bean, so, instead of having a target list with the picked items plus the ones that were set at the dualList constructor, the target list had just the ones picked at the view, not the previously set items.
As a solution, I also implemented a custom picklist converter for the primefaces component. Now everything works like a charm.
I'm not answering specifically the actual question because it has an accepted answer already (and i don't have the points to make a comment). I'm just complementing, as some people may have problems using primefaces 2.3 and omnifaces 3.1. I did search a lot about the problem I was facing, and did not found at least a hint of what was happening.
This site: picklist example has a tutorial on how to make the converter, if someone has doubts. ACV's answer is also valid for the converter, although it was already implemented by Silverfish, who asked the question.
To work with objects, you need a converter. This is the best answer how to implement it: https://stackoverflow.com/a/6625392/912829
I would like to pass some attributes to actionListener method.
My implementation is like...
<c:forEach items="${customerProductsBean.userProductList}" var="userProduct">
<p:panel toggleable="#{true}" toggleSpeed="500" header="#{userProduct.product}" >
// Some Code... Data Table and Tree Table
<f:facet name="options">
<p:menu>
<p:menuitem value="ProductSetup" actionListener="#{customerProductsBean.getProductSetupData}" >
<f:attribute name="userIdParam" value="#{data.userId}"/>
<f:attribute name="geCustomerIdParam" value="#{data.geCustomerId}"/>
<f:attribute name="acpProductParam" value="#{data.acpProduct}"/>
</p:menuitem>
<p:menuitem value="Remove Product" url="#" onclick=""/>
</p:menu>
</f:facet>
</p:panel>
</c:forEach>
And in Java Action Listener
public void getProductSetupData(ActionEvent actionEvent) {
try {
String userIdParam =
(String)actionEvent.getComponent().getAttributes().get("userIdParam");
String geCustomerIdParam =
(String)actionEvent.getComponent().getAttributes().get("geCustomerIdParam");
String acpProductParam =
(String)actionEvent.getComponent().getAttributes().get("acpProductParam");
} catch(Exception e) {
// Exception
}
}
I tried it using <f:attribute> and <f:param> but was not able to get the value in Java.
In java It shows null for each value.
This won't work if #{data} refers to the iterating variable of an iterating JSF component such as <h:dataTable var>. The <f:attribute> is set during JSF view build time, not during JSF view render time. However, the <h:dataTable var> is not available during view build time, it is only available during view render time.
If your environment supports EL 2.2, do instead
<p:menuitem ... actionListener="#{customerProductsBean.getProductSetupData(data)}" />
with
public void getProductSetupData(Data data) {
// ...
}
Or if your environment doesn't, do instead
public void getProductSetupData(ActionEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
Data data = context.getApplication().evaluateExpressionGet(context, "#{data}", Data.class);
// ...
}
I have a simply facelet which display a list of products in tabular format. In the last column of each row, there is a checkbox used to mark the product for deletion. Until now i have to put a selectBooleanCheckBox on each row and have a "mark for deletion" property in the Product entity but i think it's ugly because i have some presentation stuff in my model bean.
Is there anyway to have a h:selectManyCheckBox which has its f:selectItem distribute on each row of the dataTable ?
Thank you
The t:selectManyCheckbox layout="spread" is an excellent suggestion.
As an alternative, you can also just bind the h:selectBooleanCheckbox component to a Map<Long, Boolean> property where Long represents the entity ID (or whatever identifier which you can use to identify the row) and Boolean represents the checked state.
E.g.
public class Bean {
private List<Entity> entities;
private Map<Long, Boolean> checked = new HashMap<Long, Boolean>();
public void submit() {
for (Entity entity : entities) {
if (checked.get(entity.getId())) {
// Entity is checked. Do your thing here.
}
}
}
// ...
}
with
<h:dataTable value="#{bean.entities}" var="entity">
<h:column>
<h:selectBooleanCheckbox value="#{bean.checked[entity.id]}" />
</h:column>
...
</h:dataTable>
<h:commandButton value="submit" action="#{bean.submit}" />
The Map<Long, Boolean> will be automagically filled with the ID of all entities as map keys and the checkbox value is set as map value associated with the entity ID as key.
See also:
Using datatables - Select multiple rows
You can, using MyFaces Tomahawk's <t:selectManyCheckbox> with layout="spread"
In my application, i have used below set of code to get multiple checkbox list to be displayed vertically with scrollbar:
<style type="text/css">
#productCategoryId label {
float: inherit;
font-size: 10px!important;
font-weight: normal;
}
#productCategoryId table.formTable th, table.formTable td {
padding: 0px 0px 0 0;
}
</style>
<div style="width:200px;height: 280px;overflow-y:scroll;overflow-x:hidden;border:1px solid #999;" max-height=280px>
<h:selectManyCheckbox id="productCategoryId" layout="pageDirection" style="width:200px" styleClass="changeId">
<f:selectItem itemValue="-1000" itemLabel="ALL" />
<f:selectItems value="#{lookup.list['RSM_LOOKUP']['PRODUCT_CATEGORY']}"/>
</h:selectManyCheckbox >
</div>
The best way to use selectManyCheckbox and dataTable is...
=== Page.xhtml ===
<ice:selectManyCheckbox id="idSelectManyCheckbox" layout="spread"
value="#{MyBean.selectedsValuesCheckbox}" >
<f:selectItems value="#{MyBean.selectItemsCheck}"/>
</ice:selectManyCheckbox>
<ice:dataTable varStatus="rowVar"
value="#{MyBean.listOfMyObjects}" var="anyNameVar">
<ice:column>
<ice:checkbox for="idSelectManyCheckbox" index="#{rowVar.index}" />
</ice:column>
<ice:column>
<ice:outputText value="#{anyNameVar.property1}" />
</ice:column>
<!-- ... more columns .. -->
</ice:dataTable>
=== MyBean.java ===
private List<MyObject> listOfMyObjects = new ArrayList<MyObject>(3);
private List<String> selectedsValuesCheckbox = new ArrayList<String>(2);
private SelectItem[] selectItemsCheck = new SelectItem[3];
private handleSelectItemsCheck(){
int idx = 0;
selectedsValuesCheckbox.add("1");
selectedsValuesCheckbox.add("3");
for (MyObject myObject : listOfMyObjects) {
selectItemsCheck[idx++] =
new SelectItem(myObject.property1, myObject.property2); // value and label
}
}
// Gets and sets
================================================================
*you must use layout="spread" in that situation.
*in the table the checkboxs 1 and 3 will be selected. because "selectedsValuesCheckbox"