I want to export a dataTable (with Pagination) having LazyLoad DataModel during Report Generation.
Problem :
When I export, the report is getting generated page by page from Database and then getting exported to Excel/PDF that consumes more time. I would like to get it in a single database access by skipping the page by page generation of dataset.
I'm producing my code snippet as follows:
JSF:
<p:dataTable id="dTable" var="dev" value="#{ReportAction.lazyModel}"
styleClass ="table_paginator" rowKey="#{device.macAddress}" paginatorPosition="bottom"
paginator="true" rowsPerPageTemplate="10,20,30" rows="10" lazy="true"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
emptyMessage="Select appropriate conditions and click 'Generate Report'">
<f:facet name="header">
<h:commandLink actionListener="#{ReportAction.doExport}">
<p:graphicImage value="../../../resources/images/excel.png"
alt="XLS" style="float:right;width:32px;height:32px" />
<p:dataExporter type="xls" target="dTable" fileName="#{ReportAction.fileName}"
preProcessor="#{ReportAction.preProcess}"
postProcessor="#{ReportAction.postProcessXLS}" />
</h:commandLink>
</f:facet>
<!-- All the columns in Data Table -->
</p:dataTable>
Managed Bean:
public class ReportAction {
private ConfigurationReportDataModel mediumConfigModel;
private List<FieldReportModel> configModelList;
private String fileName;
private LazyDataModel<ConfigurationReportModel> lazyModel;
private boolean export;
public ReportAction() {
configModelList = new ArrayList<ConfigurationReportModel>();
export = false;
mediumConfigModel = new ReportDataModel();
}
public void generateFieldReport() {
lazyFieldModel = new ConfigurationReportDataModel(day, fromDate,
location,store,engineer, status, toDate, export);
}
public void preProcess(Object document) {
export = true;
log.info("preProcess::Lazy model : Page Sizing");
if(lazyFieldModel != null) {
lazyFieldModel.setPageSize(1000000);
}
log.info("preProcess::Export All Details");
mediumConfigModel.setExport(true);
}
public void postProcessXLS(Object document) {
HSSFWorkbook wb = (HSSFWorkbook) document;
HSSFSheet sheet = wb.getSheetAt(0);
HSSFRow header = sheet.getRow(0);
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setFillForegroundColor(HSSFColor.GREEN.index);
cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
for (int i = 0; i < header.getPhysicalNumberOfCells(); i++) {
HSSFCell cell = header.getCell(i);
cell.setCellValue(cell.getStringCellValue().toUpperCase());
cell.setCellStyle(cellStyle);
sheet.autoSizeColumn(i);
}
export = false;
mediumConfigModel.setExport(false);
}
public List<ConfigurationReportModel> getConfigModelList() {
return configModelList;
}
public void setConfigModelList(
ArrayList<ConfigurationReportModel> configModelList) {
this.configModelList = configModelList;
}
public String getFileName() {
SimpleDateFormat formatter = new SimpleDateFormat("ddMMyyyy_HHmmss");
fileName = "Config_Report_".concat(formatter.format(new Date()));
return fileName;
}
public void setMediumConfigModel(
ConfigurationReportDataModel mediumConfigModel) {
this.mediumConfigModel = mediumConfigModel;
}
public void setConfigModelList(
List<ConfigurationReportModel> configModelList) {
this.configModelList = configModelList;
}
public LazyDataModel<ConfigurationReportModel> getLazyFieldModel() {
log.info("##########getLazyFieldModel###########");
if(export) {
log.info("getLazyFieldModel::Will get Exported........");
lazyFieldModel = new ConfigurationReportDataModel(day, fromDate,
location, store, engineer, status, toDate, true);
lazyFieldModel.load(1, 1000000000, null, null, null);
}
return lazyFieldModel;
}
public void setLazyFieldModel(
LazyDataModel<ConfigurationReportModel> lazyFieldModel) {
this.lazyFieldModel = lazyFieldModel;
}
}
DataModel:
public class ConfigurationReportDataModel extends
LazyDataModel<ConfigurationReportModel> {
private List<ConfigurationReportModel> configReport;
private boolean export;
public ConfigurationReportDataModel() {
this.export = false;
}
public List<ConfigurationReportModel> load(int first, int pageSize,
String sortField, SortOrder sortOrder, Map<String, String> filters) {
UIClient client = new UIClient();
ReportData data = null;
// ///////////////////
if(export) {
log.info("Do Export....");
first = 1;
pageSize = 1000000000;
}
deviceList = new ArrayList<DeviceGlobal>();
// Retrieves data from Database with the number of record (page size)
data = client.generateFieldReport(first, pageSize,
Integer.parseInt(location), Integer.parseInt(store),
engineer, getTimeToBeginningOfDay(), getTimeToEndofDay(),
status);
log.info("Obtained data : " + data);
if (data != null) {
log.info("Got devices : " + data.getRecords().size());
deviceList = (ArrayList<DeviceGlobal>) data.getRecords();
// ///////////////////
int record = first + 1;
ConfigurationReportModel storeModel = null;
DeviceGlobal deviceGlobal = null;
configReport = new ArrayList<ConfigurationReportModel>();
for (Iterator<DeviceGlobal> iterator = deviceList.iterator(); iterator
.hasNext();) {
deviceGlobal = (DeviceGlobal) iterator.next();
storeModel = new ConfigurationReportModel(deviceGlobal,
record++);
configReport.add(storeModel);
}
log.info("Total Config Report : " + configReport.size());
// rowCount
int dataSize = data.getReportCount();
this.setRowCount(dataSize);
log.info("Report Count: " + data.getReportCount());
if(export) {
return configReport;
}
else {
// paginate
if (dataSize > pageSize) {
try {
return configReport;
} catch (IndexOutOfBoundsException e) {
return configReport;
}
} else {
return configReport;
}
}
} else {
log.info("Got no devices");
deviceList = new ArrayList<DeviceGlobal>();
configReport = new ArrayList<ConfigurationReportModel>();
this.setRowCount(0);
return configReport;
}
}
}
Note:
There's no syntax error.
All the custom type classes are defined.
How do I get the Excel report by skipping the page by page generation of records?
You have to add pageOnly="false" to dataExporter
for zero devision issue just override an extra LaztDataModel method as given below.
#Override
public void setRowIndex(int rowIndex) {
// TODO Auto-generated method stub
if (rowIndex == -1 || getPageSize() == 0) {
super.setRowIndex(-1);
}
else
super.setRowIndex(rowIndex % getPageSize());
}
Related
I'm trying to develop a program using Primefaces that downloads all the images in a given URL. I've already successfully downloaded and it works fine but i need to add a functionality wherein i need to see the progress of all files being downloaded. Each file must have it's own progress bar. Also, one of the problems is having to create dynamic progress bars based on the files downloaded.
This is what i've come up so far.
#ManagedBean
public class CheckValidUrl {
private static final String FOLDERPATH = "C:/Users/jan.louise.h.casas/Downloads/";
private static final String IMAGE_PATTERN = "([^\\s]+(\\.(?i)(jpg|png|jpeg|ico|bmp))$)";
private Pattern pattern;
private Matcher matcher;
private String url;
private Integer progress;
private static boolean progressBarRendered;
private List<String> imagesList = new ArrayList<String>();
public List<String> getImagesList() {
return imagesList;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Integer getProgress() {
return progress;
}
public void setProgress(Integer progress) {
this.progress = progress;
}
public boolean isProgressBarRendered() {
return progressBarRendered;
}
public void cancel(){
progress = null;
}
public void onComplete() {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Progress Completed"));
}
public void validateUrl() throws IOException {
UrlValidator urlValidator = new UrlValidator();
if(!urlValidator.isValid(url)){
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error!", "Please make sure URL is valid"));
} else{
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Info", "Please wait"));
downloadImages();
}
}
public void downloadImages(){
try{
Document doc = Jsoup.connect(url).get();
Elements img = doc.getElementsByTag("img");
if(img.size() > 0){
for (Element el : img) {
String src = el.absUrl("src");
System.out.println("Image Found!");
System.out.println("src attribute is : "+src);
pattern = Pattern.compile(IMAGE_PATTERN);
if(validateImageType(src)){
imagesList.add(src);
}
}
if(!imagesList.isEmpty()){
getImages(imagesList);
} else{
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN, "Warning", "URL only has GIFs"));
}
} else{
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN, "Warning", "URL doesn't have any images"));
}
} catch(FileNotFoundException ex){
System.err.println("There was an error");
Logger.getLogger(CheckValidUrl.class.getName()).log(Level.SEVERE, null, ex);
} catch(IOException ex){
System.err.println("There was an error");
Logger.getLogger(CheckValidUrl.class.getName()).log(Level.SEVERE, null, ex);
}
}
private static void getImages(List<String> imageSources) throws FileNotFoundException, IOException {
for(String src : imageSources){
int indexname = src.lastIndexOf("/");
if (indexname == src.length()) {
src = src.substring(1, indexname);
}
indexname = src.lastIndexOf("/");
String name = src.substring(indexname, src.length());
System.out.println(name);
URL url = new URL(src);
InputStream in = url.openStream();
OutputStream out = new BufferedOutputStream(new FileOutputStream( FOLDERPATH+ name));
for (int b; (b = in.read()) != -1;) {
progressBarRendered = true;
out.write(b);
System.out.println("Downloaded image: "+src);
System.out.println("Boolean Value: "+progressBarRendered);
}
out.close();
in.close();
}
}
public boolean validateImageType(String image){
pattern = Pattern.compile(IMAGE_PATTERN);
matcher = pattern.matcher(image);
return matcher.matches();
}
}
and in my check_valid_url.xhtml
<p:messages id="messages" showDetail="true" autoUpdate="true" closable="true" />
<h:panelGrid columns="3" cellpadding="5">
<p:outputLabel for="url" value="Url:" />
<p:inputText id="url" value="#{checkValidUrl.url}" required="true" label="Url" />
<p:message for="url" display="icon" />
<p:commandButton value="Submit" update="progressBarPanel" actionListener="#{checkValidUrl.validateUrl}" onclick="PF('pbAjax').start()" icon="ui-icon-check" />
</h:panelGrid>
<h:panelGrid id="progressBarPanel">
<p:progressBar widgetVar="pbAjax" ajax="true" rendered="#{checkValidUr.progressBarRendered}" value="#{checkValidUrl.progress}" labelTemplate="{value}%" styleClass="animated" global="false">
<p:ajax event="complete" listener="#{checkValidUrl.onComplete}" update="panel"/>
</p:progressBar>
</h:panelGrid>
</p:panel>
</h:form>
I have the following p:selectOneMenu :
<p:selectOneMenu widgetVar="selectELclUpdateLcl" style="font-size:11px;"
value="#{tabparam.selectedDataEntite}" appendTo="#this"
id="selectedDataEntite" required="true" requiredMessage="Entité obligatoire"
converter="entiteConverter">
<p:ajax event="change" listener="#{tabparam.enableBloc}" update="sBloc sTLocal sEtage" />
<f:selectItem noSelectionOption="true" itemLabel="-- Entités --" value="#{null}"></f:selectItem>
<f:selectItems value="#{tabparam.listDataEntite}" var ="e"
itemLabel="#{e.nom}" itemValue="#{e}" ></f:selectItems>
</p:selectOneMenu>
When I select an item different from null, the ajax event is fired correctly. But if I choose the null one (Select "-- Entités --" ), the ajax event is not fired.
Below is my converter class:
#FacesConverter(value = "entiteConverter")
public class EntiteConverter implements Converter {
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
Data_Entite ret = null;
UIComponent src = arg1;
if (src != null) {
List<UIComponent> childs = src.getChildren();
UISelectItems itens = null;
if (childs != null) {
for (UIComponent ui : childs) {
if (ui instanceof UISelectItems) {
itens = (UISelectItems) ui;
break;
} else if (ui instanceof UISelectItem) {
UISelectItem item = (UISelectItem) ui;
try {
Data_Entite val = (Data_Entite) item.getItemValue();
if (arg2.equals("" + val.getId())) {
ret = val;
break;
}
} catch (Exception e) {
}
}
}
}
if (itens != null) {
List<Data_Entite> values = (List<Data_Entite>) itens.getValue();
if (values != null) {
for (Data_Entite val : values) {
if (arg2.equals("" + val.getId())) {
ret = val;
break;
}
}
}
}
}
return ret;
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) {
String ret = "";
if (arg2 != null && arg2 instanceof Data_Entite) {
Data_Entite m = (Data_Entite) arg2;
if (m != null) {
int id = m.getId();
if (id != 0) {
ret = Integer.toString(id);
}
}
}
return ret;
}
}
Any idea please ? I am using PF 5.1
Thanks.
I have a requirement to use same variable of a request scoped backing bean which is a List datatype for h:selectOneListbox and h:selectManyListbox. I get "ip: Validation Error: Value is not valid" error with h:selectOneListbox even after using a converter. Can some one help me resolving this ?
In page1.xhtml I've given it as:
<h:selectManyListbox id="ip" value="#{inputBean.ipAddress}" size="5">
<f:selectItems value="#{inputBean.ipAddressList}" />
</h:selectManyListbox>
In page2.xhtml I've given it as:
<h:selectOneListbox id="ip" value="#{inputBean.ipAddress}" size="5">
<f:selectItems value="#{inputBean.ipAddressList}" />
<f:converter converterId="ipAddressConverter"></f:converter>
</h:selectOneListbox>
My Input bean, a request scoped managed bean looks like this:
#ManagedBean
#RequestScoped
public class InputTablePlot implements Serializable{
private static final long serialVersionUID = 1L;
#ManagedProperty("#{database}")
private Database database;
private Connection connection;
private StringBuilder query;
private PreparedStatement pst_query;
private ResultSet rs;
private List<Long> ipAddress;
private Map<String, Long> ipAddrMenu;
public InputBean()
{
ipAddrMenu = new LinkedHashMap<String, Long>();
}
#PostConstruct
public void init()
{
ipAddrMenu.clear();
try
{
connection = database.getDbConnection();
query = new StringBuilder();
query.append("SELECT distinct source AS ipaddr FROM addrtable ORDER BY source");
pst_query = connection.prepareStatement(query.toString());
rs = pst_query.executeQuery();
while (rs.next())
{
ipAddrMenu.put(ipLongToString(rs.getLong("ipaddr")), rs.getLong("ipaddr")); // Adding
// sources
}
rs.close();
pst_query.close();
connection.close();
}
catch (SQLException e)
{
e.printStackTrace();
}
}
public List<Long> getIpAddress()
{
System.out.println("In Getter" + ipAddress);
return ipAddress;
}
public void setIpAddress(List<Long> ipAddress)
{
System.out.println("In Setter");
System.out.println(ipAddress);
this.ipAddress = ipAddress;
}
public Map<String, Long> getIpAddressList()
{
return ipAddrMenu;
}
public void setIpAddressList(Map<String, Long> ipAddressList)
{
this.ipAddrMenu = ipAddressList;
}
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((connection == null) ? 0 : connection.hashCode());
result = prime * result + ((database == null) ? 0 : database.hashCode());
result = prime * result + ((ipAddrMenu == null) ? 0 : ipAddrMenu.hashCode());
result = prime * result + ((ipAddress == null) ? 0 : ipAddress.hashCode());
return result;
}
#Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
InputBean other = (InputBean) obj;
if (connection == null)
{
if (other.connection != null)
return false;
}
else if (!connection.equals(other.connection))
return false;
if (database == null)
{
if (other.database != null)
return false;
}
else if (!m_database.equals(other.database))
return false;
if (ipAddrMenu == null)
{
if (other.ipAddrMenu != null)
return false;
}
else if (!ipAddrMenu.equals(other.ipAddrMenu))
return false;
if (ipAddress == null)
{
if (other.ipAddress != null)
return false;
}
else if (!ipAddress.equals(other.ipAddress))
return false;
return true;
}
}
Converter code:
import java.util.ArrayList;
import java.util.List;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
public class IpAddressConverter implements Converter
{
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2)
{
List<Long> ipAddr = new ArrayList<Long>();
try{
ipAddr.add(Long.valueOf(arg2));
}catch(NumberFormatException e){
e.printStackTrace();
FacesContext facesContext = FacesContext.getCurrentInstance();
FacesMessage facesMessage = new FacesMessage("Problem with conversion of ip!");
facesContext.addMessage(null, facesMessage);
}
for(int i=0; i< ipAddr.size(); i++){
System.out.println("ipAddr >>> "+ipAddr);
}
return ipAddr;
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2)
{
String val = null;
try{
Long ip = (Long) arg2;
val = ip.toString();
}catch(Exception e){
e.printStackTrace();
FacesContext facesContext = FacesContext.getCurrentInstance();
FacesMessage facesMessage = new FacesMessage("Problem with conversion of ip!");
facesContext.addMessage(null, facesMessage);
}
System.out.println("value >>> "+val);
return val;
}
}
It failed because List<Long> as returned by the converter doesn't match the individual Long item of the available items while JSF is validating the submitted (and converted!) value against the list of available items (as part of safeguard against tampered/spoofed HTTP requests wherein attacker manipulated the selected item value). The converter is supposed to return a Long. All with all, such a converter is simply insuitable for usage in an UISelectOne component.
You'd better bind the value straight to a single property. If you insist in using a List property, then just prepare it with a single empty item and specify the list item index in the value.
private List<Long> ipAddress = new ArrayList<>(1);
with
<h:selectOneListbox id="ip" value="#{inputBean.ipAddress[0]}" size="5">
<f:selectItems value="#{inputBean.ipAddressList}" />
</h:selectOneListbox>
See also:
Validation Error: Value is not valid
I am using Struts2 jquery chart plugin 3.4.0. I am getting blank chart when i use json for getting date values from the Action class.If i use simple action then same code works fine.
Here is my jsp code.
<s:url id="chartDataUrl" action="jsonChartData"/>
<sjc:chart
id="chartDate"
xaxisMode="time"
xaxisTimeformat="%m.%Y"
xaxisMin="%{minTime}"
xaxisMax="%{maxTime}"
xaxisColor="#666"
xaxisTickSize="[3, 'month']"
xaxisTickColor="#aaa"
xaxisPosition="top"
yaxisPosition="right"
yaxisTickSize="10"
cssStyle="width: 600px; height: 400px;"
>
<sjc:chartData
id="chartAjaxData1"
label="Map -Double, Double-"
href="%{chartDataUrl}" // when i remove json call then it works fine
list="dateFromMap"
reloadTopics="reloadMap"
lines="{show : true}"
/>
</sjc:chart>
struts.xml code
<action name="jsonChartData"
class="com.ebhasin.fitnessbliss.actions.GraphsAction">
<result type="json" name="success"></result>
</action>
Action class code:
public class GraphsAction extends ActionSupport {
private String currentDate;
private Map<Date, Float> dateFromMap;
HomeService homeService = new HomeService();
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
#Override
public String execute() {
System.out.println("execute");
float weight;
Date date = new Date();
Map session = ActionContext.getContext().getSession();
Integer loginId = (Integer) session.get("loginId");
if (loginId != null) {
dateFromMap = new TreeMap<Date, Float>();
List list = homeService.getWeightGraphData(loginId);
if (list.size() > 0) {
Iterator itr = list.iterator();
while (itr.hasNext()) {
UserStats userStats = (UserStats) itr.next();
weight = userStats.getWeight();
date = userStats.getCreatedDate();
//currentDate = formatter.format(date);
dateFromMap.put(date, weight);
}
} else {
// dateFromMap.put("my",2F );
}
} else {
}
return SUCCESS;
}
public String getCurrentDate() {
return currentDate;
}
public void setCurrentDate(String currentDate) {
this.currentDate = currentDate;
}
public Map<Date, Float> getDateFromMap() {
return dateFromMap;
}
public void setDateFromMap(Map<Date, Float> dateFromMap) {
this.dateFromMap = dateFromMap;
}
}
EDIT:
Please put a System.out.println("something"); on every if / else block to see exactly where are you passing;
As a note, the preferred way to get the session object is to implement SessionAware, instead of using ActionContext;
Does your JSON Action looks like this ? :
http://struts.jgeppert.com/struts2-jquery-showcase/index.action
(go to More Widgets -> Charts -> JSON Action, in the bottom right tab.
#ParentPackage(value = "showcase")
public class JsonChartData extends ActionSupport {
private static final long serialVersionUID = 6659512910595305843L;
private List<ListValue> objList;
private Map<Double, Double> doubleMap;
#Actions( {
#Action(value = "/json-chart-data", results = {
#Result(name = "success", type = "json")
})
})
public String execute()
{
objList = new ArrayList<ListValue>();
doubleMap = new TreeMap<Double, Double>();
Random generator = new Random();
for (int i = 1; i <= 24; i++)
{
doubleMap.put(Double.valueOf("" + i), generator.nextDouble() * 10.0);
}
for (int i = 1; i <= 24; i++)
{
objList.add(new ListValue("" + i, "" + generator.nextInt(30)));
}
return SUCCESS;
}
public String getJSON()
{
return execute();
}
public List<ListValue> getObjList()
{
return objList;
}
public void setObjList(List<ListValue> objList)
{
this.objList = objList;
}
public Map<Double, Double> getDoubleMap()
{
return doubleMap;
}
public void setDoubleMap(Map<Double, Double> doubleMap)
{
this.doubleMap = doubleMap;
}
}
I have a data object called DeliveryPeriod which is a container for a start and a end date (saved as String like dd.MM.yyy, comes from the database) an the id of another object called PlanningPeriod. This delivery period should be displayed in its own custom component in JSF like
<myc:deliveryPeriodComponent value="#{backendBean.deliveryPeriod}" />
I implement a class DeliveryPeriodComponent which extends UIInput and a DeliveryPeriodComponentRenderer which extendes javax.faces.renderer. The rendering works well, i see two calender elements and a SelectOneMenu to choose the planning period. But render the data is not all, I need to change the data as well. And here comes the problem, i have no idea to get the data inside my component to the backend bean. The decode() method did not know the new values and the other methods are never called. I didn't know the trick, how to connect the JSF page to the bean, from the tutorial (http://jsfatwork.irian.at, i bought the book) i had these methods like getValue() and getConverter().
Here is the code from the component:
public class DeliveryPeriodComponent extends UIInput {
public static final String COMPONENT_TYPE = "de.hacon.tps.integrator.web.component.deliveryperiod.DeliveryPeriodComponent";
enum PropertyKeys {
begin, end, planningPeriod
}
public DeliveryPeriodComponent() {
setRendererType("de.hacon.tps.integrator.web.component.deliveryperiod.DeliveryPeriodComponent");
}
public String getBegin() {
return (String) getStateHelper().eval(PropertyKeys.begin, "01.01.2012");
}
public void setBegin(String begin) {
getStateHelper().put(PropertyKeys.begin, begin);
}
public String getEnd() {
return (String) getStateHelper().eval(PropertyKeys.end, "31.12.2012");
}
public void setEnd(String end) {
getStateHelper().put(PropertyKeys.end, end);
}
public int getPlanningPeriod() {
return (Integer) getStateHelper().eval(PropertyKeys.planningPeriod, 0);
}
public void setPlanningPeriod(int planningPeriod) {
getStateHelper().put(PropertyKeys.planningPeriod, planningPeriod);
}}
And here is the renderer:
public class DeliveryPeriodComponentRenderer extends Renderer {
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
#Override
public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
DeliveryPeriodComponent comp = (DeliveryPeriodComponent) component;
String clientId = comp.getId();
try {
encodeInput(context, comp, clientId);
} catch (ParseException e) {
e.printStackTrace();
}
}
private void encodeInput(FacesContext context, DeliveryPeriodComponent comp, String clientId) throws ParseException {
comp.getChildren().clear();
DeliveryPeriod value = (DeliveryPeriod) comp.getAttributes().get("value");
List<PlanningPeriodSubset> pp = (List<PlanningPeriodSubset>) comp.getAttributes().get("periods");
HtmlPanelGrid pGrid = new HtmlPanelGrid();
pGrid.setColumns(4);
Calendar cBegin = new Calendar();
cBegin.setShowOn("button");
cBegin.setValue(sdf.parse(value.getStartDate()));
cBegin.setPattern("dd.MM.yyyy");
pGrid.getChildren().add(cBegin);
Calendar cEnd = new Calendar();
cEnd.setShowOn("button");
cEnd.setValue(sdf.parse(value.getEndDate()));
cEnd.setPattern("dd.MM.yyyy");
pGrid.getChildren().add(cEnd);
HtmlSelectOneMenu sPlanningPeriod = new HtmlSelectOneMenu();
Collection<UISelectItem> items = new ArrayList<UISelectItem>();
for (PlanningPeriodSubset op : pp) {
UISelectItem item = new UISelectItem();
item.setItemLabel(op.getName());
item.setItemValue(op.getId());
items.add(item);
}
sPlanningPeriod.getChildren().addAll(items);
sPlanningPeriod.setValue(value.getPlanningPeriodId());
pGrid.getChildren().add(sPlanningPeriod);
HtmlPanelGrid buttonPanel = new HtmlPanelGrid();
buttonPanel.setColumns(2);
Button bDelete = new Button();
bDelete.setValue(" - ");
buttonPanel.getChildren().add(bDelete);
Button bInfo = new Button();
bInfo.setValue(" i ");
buttonPanel.getChildren().add(bInfo);
pGrid.getChildren().add(buttonPanel);
comp.getChildren().add(pGrid);
}
#Override
public void decode(FacesContext context, UIComponent component) {
DeliveryPeriodComponent deliveryComponent = (DeliveryPeriodComponent) component;
DeliveryPeriod deliveryPeriod = new DeliveryPeriod();
deliveryPeriod.setStartDate(deliveryComponent.getBegin());
deliveryPeriod.setEndDate(deliveryComponent.getEnd());
deliveryPeriod.setPlanningPeriodId(deliveryComponent.getPlanningPeriod());
// Map<String, String> params = context.getExternalContext().getRequestParameterMap();
// String clientId = component.getClientId();
// String value = params.get(clientId);
// ((UIInput) component).setSubmittedValue(value);
}
#Override
public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue)
throws ConverterException {
Converter converter = getConverter(context, (DeliveryPeriodComponent) component);
if (converter != null) {
return converter.getAsObject(context, component, (String) submittedValue);
} else {
return submittedValue;
}
}
private Object getValue(FacesContext context, DeliveryPeriodComponent comp) {
Object submittedValue = comp.getSubmittedValue();
if (submittedValue != null) {
return submittedValue;
}
Object begin = comp.getBegin();
Object end = comp.getEnd();
Object planningPeriod = comp.getPlanningPeriod();
DeliveryPeriod period = new DeliveryPeriod();
period.setStartDate((String) begin);
period.setEndDate((String) end);
period.setPlanningPeriodId((Integer) planningPeriod);
Converter converter = this.getConverter(context, comp);
if (converter != null) {
return converter.getAsString(context, comp, period);
} else if (period != null) {
return period.toString();
} else {
return "";
}
}
private Converter getConverter(FacesContext context, DeliveryPeriodComponent comp) {
Converter conv = ((UIInput) comp).getConverter();
if (conv != null) {
ValueExpression exp = comp.getValueExpression("value");
if (exp == null) {
return null;
} else {
Class valueType = exp.getType(context.getELContext());
if (valueType == null) {
return null;
} else {
return context.getApplication().createConverter(valueType);
}
}
}
return null;
}}
Maybe this is a trivial problem, I am not a pro at JSF and still learning. And it is very hard to write your own components when you almost confused about the basics :-( Still learning new things every day. Thank you for your help!
(I found a lot on custom components with examples like a custum inputfields, these components works well and transfer their data. Unfortunatly I found nothing on custom components which contains more then one input field or did something different from the existing JSF elements)
In Xhtml Page Call your listner with
<h:commandButton id="add" value="Add More" styleClass="input_right_cor" type="button">
<f:ajax execute="#this" listener="#{templateSearchHandler.AddRow}" />
</h:commandButton>