6 Replies Latest reply on Mar 4, 2009 2:09 AM by nille

    a4j:support in t:dataTable / t:columns

    nille

      Hi everybody,

      sorry if there's somewhere the answer for this, but I did some research and wasn't able to find it.

      I do have a t:dataTable and want to to use the a4j:support tag for each table cell created by t:columns. Code looks like

      <t:dataTable id="data" styleClass="standardTable"
       headerClass="standardTable_Header" footerClass="standardTable_Header"
       rowClasses="standardTable_Row1,standardTable_Row2"
       columnClasses="standardTable_Column" var="project"
       sortColumn="#{WorkloadBean.sortColumn}"
       sortAscending="#{WorkloadBean.sortAscending}"
       value="#{WorkloadBean.workloadDataModel}">
       <t:column width="150" sortable="true">
       <f:facet name="header">
       <h:outputText value="#{msg.employee_TableCustomer}" />
       </f:facet>
       <h:outputText value="#{project.counterparty.name}" />
       </t:column>
       <t:column width="150" sortable="true" defaultSorted="true">
       <f:facet name="header">
       <h:outputText value="#{msg.employee_TableProject}" />
       </f:facet>
       <h:outputText value="#{project.name}" />
       </t:column>
       <t:columns value="#{WorkloadBean.columnDataModel}" var="column">
       <f:facet name="header">
       <h:panelGroup>
       <h:outputText
       value="#{column.calendarWeek} - #{column.calendarYear}" />
       </h:panelGroup>
       </f:facet>
       <div align="right"><h:outputText
       value="#{WorkloadBean.columnValue}">
       <f:convertNumber type="number" minFractionDigits="2" />
       <a4j:support ajaxSingle="true" event="onmouseover"
       reRender="detailPanel" action="#{WorkloadBean.doit}">
       </a4j:support>
       </h:outputText></div>
       </t:columns>
       </t:dataTable>
      

      But the method is never called. I found the following issue tracker entry: https://ajax4jsf.dev.java.net/issues/show_bug.cgi?id=38, but didn't get it.

      Thanks in advance,
      cheers
      Nils

        • 1. Re: a4j:support in t:dataTable / t:columns
          nbelaevski

          Hi Nils,

          Code seems to be ok. QA team will check the problem in a next few days.

          • 2. Re: a4j:support in t:dataTable / t:columns
            nille

            Hi,

            sorry, I forgot to mention that the 't'-Tags are Tomahawk tags.

            Regards,
            nille

            • 3. Re: a4j:support in t:dataTable / t:columns
              tkuprevich

              Hi, could you please upload your backing bean code, WorkloadBean? Thanks!

              • 4. Re: a4j:support in t:dataTable / t:columns
                nille

                Hi,

                sorry that I'm answering that late.

                So, this is the backing bean code (quite a lot).

                This is the code of the abstract bean the backing bean extends...

                public abstract class AbstractTableDisplayingBean {
                
                 protected DataModel mColumns;
                 protected DataModel mWorkloadDataModel;
                 protected Map<Integer, Double > mValueMap = new HashMap<Integer, Double >();
                 protected int monthsToShow = 3;
                 protected Date calendarStart;
                 protected abstract Double getColumnValue();
                 protected abstract void fillRows();
                 protected abstract void fillColumns();
                
                 /* This method returns the columns of the model,
                 * e.g.: KW1, KW2, KW3,... */
                 public DataModel getColumnDataModel()
                 {
                 return mColumns;
                 }
                 /* This method return the rows of the model,
                 * e.g. Project P, Project D,... */
                 public DataModel getWorkloadDataModel()
                 {
                 return mWorkloadDataModel;
                 }
                }


                and this from the backing bean
                **
                 * This bean is used to display an overview using the CalendarBean
                 * and a list of the users TimeIntervals.
                 *
                 */
                public class EmployeeCalendarWorkloadBean extends AbstractTableDisplayingBean {
                 private String selectedProjectName = "initial";
                 private TimeInterval selectedTimeInterval = null;
                 private List<List<String>> listWithRows;
                 private List<String> listWithColumns;
                
                 private static final int FILTER_ALL_CUSTOMERS = -1;
                 private LoginController loginController;
                
                 private List<CalendarItem> calItems;
                 private List<TimeInterval> tIntervals;
                 private List<SelectItem> customerFilterList = new ArrayList<SelectItem>();
                
                 private ArrayList<Integer> selectedCustomerFilterListItem = new ArrayList<Integer>();
                
                 private EntityManager entityManager;
                 private EntityManagerFactory emf;
                
                 private int sumTableRows = 1;
                 private boolean alreadySet = false;
                
                 private String employeeId;
                 private String imitatingEmployeeId;
                
                 private String sortColumn = null;
                 private boolean sortAscending = true;
                
                 DecimalFormat df = new DecimalFormat( "0.00" );
                 SimpleDateFormat sdf_year = new SimpleDateFormat();
                
                
                 /**
                 * The constructor of the EmployeeCalendarWorkloadBean
                 */
                 public EmployeeCalendarWorkloadBean() {
                 /* This is used later for calendar calculations */
                 sdf_year.applyPattern("yyyy");
                 /* Set filter to display all projects/customers */
                 selectedCustomerFilterListItem.add(FILTER_ALL_CUSTOMERS);
                 init();
                 }
                
                 /**
                 * The init method of the EmployeeCalendarWorkloadBean. This method
                 * fills the rows and columns with data, which can be displayed on a JSp.
                 * Rows hold CalendarItems (from a CalendarBean), columns hold projects.
                 */
                 private void init() {
                 log.debug(this.getClass().getName() + "init");
                 Map<String, Object> map = FacesContext.getCurrentInstance()
                 .getExternalContext().getSessionMap();
                 if (map != null) {
                 loginController = (LoginController) map.get("LoginBean");
                 if (loginController != null) {
                 checkForChangedEmployeeIds();
                 if(alreadySet == false) {
                 //calBean = new CalendarBean();
                 calBean.goToday();
                 }
                 /*
                 * If ColumnModel is empty, fill with CalendarItems (KW1,
                 * KW2,...)
                 */
                 if (mColumns == null) {
                 fillColumns();
                 }
                 /* if mWorkloadDataModel is empty, fill with */
                 if (mWorkloadDataModel == null) {
                 fillRows();
                 }
                
                 }
                 }
                 }
                
                 /**
                 * This method does the actual check for the current employee. If a manager has logged on,
                 * he can impersonate a 'normal' employee. To actually see the employees TimeIntervals etc.
                 * the init method must be called again. The need for this is checked here.
                 */
                 private void checkForChangedEmployeeIds() {
                 /* Check here if current employeeId is unequal than the
                 * employeeId set in the LoginController. This can only happen if
                 * current Employee is TeamLeader of employee identified by employeeId */
                 imitatingEmployeeId = loginController.getImitatingEmployeeId();
                 if( (imitatingEmployeeId != null) && (imitatingEmployeeId.equals(loginController.getEmployeeId()) == true)) {
                 /* We are who we are: We are not imitating a different user (which _is_ allowed
                 * to Manager [-> TeamLeader, GroupLeader] ) */
                 employeeId = imitatingEmployeeId;
                 }else if( (imitatingEmployeeId != null) && (imitatingEmployeeId.equals(loginController.getEmployeeId()) == false) ) {
                 /* We are acting as a different user*/
                 employeeId = imitatingEmployeeId;
                 }else {
                 /* No employeeId is set. We assume that we are acting as ourselves */
                 employeeId = loginController.getEmployeeId();
                 }
                 }
                
                
                 /**
                 * Although this is a 'getter' this method does more than just
                 * return a value. Depending on the current row and column this methd calculates
                 * the average amount of hours. This is done by getting all TimeIntervals belonging
                 * to the project, calculating the number of week of the current TimeInterval and
                 * finally calculate the average afforded hours.
                 *
                 * @TODO Change the for-loop from all TimeIntevals to the one of the project (row)! Than the if-statement is not needed, too.
                 * @return String - the average number of afforded hours for the project and calendar week
                 */
                 /* This model returns the acutal values */
                 public Double getColumnValue() {
                 /* We need this employee to get the FTE used to calculate the amount
                 * of hours for team wide TimeIntervals */
                 emf = (EntityManagerFactory)ServiceFinder.findBean("entityManagerFactory");
                 entityManager = emf.createEntityManager();
                 Employee tmpEmployee = Employee.getEmployeeByEmpId(entityManager, employeeId);
                 if(entityManager.isOpen() == true) {
                 entityManager.close();
                 }
                
                 DataModel wloadDataModel = getWorkloadDataModel();
                 if (wloadDataModel.isRowAvailable()) {
                 Project row = (Project) wloadDataModel.getRowData();
                 DataModel columnDataModel = getColumnDataModel();
                 if (columnDataModel.isRowAvailable()) {
                 CalendarItem column = (CalendarItem)columnDataModel.getRowData();
                 /* get correct value from TimeInterval, meaning
                 * hours in calendar week = timeInterval.affordedHours / (timeInterval.untilWeekOfYear - timeInterval.untilWeekOfYear)
                 *
                 * For this we step through the timeInterval until we get a match
                 * for a column (calendarWeek) within the timeInterval
                 * */
                 /**
                 * TODO Change the for-loop! Is it necessary to loop through ALL TimeIntevals?
                 * Or is it sufficient to loop through all TimeIntervals belonging to the
                 * project (row)
                 * TODO Can this be done? org.hibernate.LazyInitializationException happens because
                 * session is already closed and we use LAZY fetching! See TeamViewBean Javadoc for more info
                 */
                 double summedUpResult = 0;
                 log.info("\t\t*** Now KW: " + column.getCalendarWeek() + " / " + column.getCalendarYear() + "***");
                 for(TimeInterval ti : tIntervals) {
                 /* This is for debug only */
                 if(log.isDebugEnabled()) {
                 debugGetColumn(ti, row, column);
                 }
                 if(ti.getProject().getId() == row.getId()) {
                 /* Used to calculate the hours per calendar week */
                 int ti_amount_of_cw = 0;
                 double result = 0;
                 /* We must check if the calendar reaches over a year break.
                 * If that's the case, we split the calendar week in calendar weeks
                 * before new year and after new year */
                
                 /* get number of weeks*/
                 ti_amount_of_cw = CalendarOperations.calculateNumOfWeeks(ti.getFrom(), ti.getUntil());
                 if(ti.getEntireTeam().equalsIgnoreCase("TRUE")) {
                 result = ( (double)((ti.getAffordedHours() / ti_amount_of_cw ) ) ) * tmpEmployee.getOmegaFte();
                 }else{
                 result = (double)((ti.getAffordedHours() / ti_amount_of_cw ) );
                 }
                
                
                 GregorianCalendar gc_from = CalendarOperations.calculateDateForFirstDayOfWeek(ti.getFrom());
                 gc_from.setFirstDayOfWeek(GregorianCalendar.MONDAY);
                 GregorianCalendar gc_until = CalendarOperations.calculateDateForLastDayOfWeek(ti.getUntil());
                 gc_until.setFirstDayOfWeek(GregorianCalendar.MONDAY);
                 GregorianCalendar gccolumn = CalendarOperations.calculateDateForFirstDayOfWeek(column.getCalendarDate());
                 gccolumn.setFirstDayOfWeek(GregorianCalendar.MONDAY);
                 log.info("\t\t*** " + gc_from.getTime().toString() + " < " + gccolumn.getTime().toString() + " < " + gc_until.getTime().toString());
                 if( (gccolumn.before(gc_from)) || (gccolumn.after(gc_until)) ) {
                 continue;
                 }else{
                // System.out.println("MATCH");
                 if(mValueMap.containsKey(column.getCalendarWeek())) {
                 mValueMap.put(column.getCalendarWeek(), result + (Double)mValueMap.get(column.getCalendarWeek()));
                 }else{
                 mValueMap.put(column.getCalendarWeek(), result);
                 }
                 /* Here we just sum up all proper results. Meaning all results belonging
                 * to the one ProjectID. Because it may happen, that there multiple TimeIntervals entries
                 * for one project in one and the same calendar week. */
                 if(result > 0)
                 summedUpResult += result;
                 }
                 }
                 }
                 /* After iterating through all TimeIntervals we have the summed up
                 * average hours for that one calendar week (column) and project (row)
                 * and return it if > 0 else we return '-' at the end if the method */
                 if(summedUpResult > 0){
                 return roundToTwoDecimals(summedUpResult);
                 }
                 }else{
                 log.debug("Column with index : " + columnDataModel.getRowIndex() + " is not available");
                 }
                 }
                 return 0.0;
                 }
                
                 /** This method just returns values calculated before in method 'getColumnValue'.
                 * That means, that method 'getColumnValue' has to be called before!
                 * @return - String the sum of the average afforded hours of all projects in this calendar week
                 * */
                 public Double getColumnSum() {
                 DataModel columnDataModel = getColumnDataModel();
                 if (columnDataModel.isRowAvailable()) {
                 CalendarItem column = (CalendarItem)columnDataModel.getRowData();
                 /**
                 * TODO specify the match more precisely: calendarWeek and Year
                 */
                 if(mValueMap.containsKey(column.getCalendarWeek())) {
                 return roundToTwoDecimals((Double)mValueMap.get(column.getCalendarWeek()));
                 }
                 }
                 return 0.0;
                 }
                
                 /**
                 * This method just fills the columns with calendar items provided by the CalendarBean
                 */
                 protected final void fillColumns() {
                 calItems = calBean.getCalendarItems();
                 mColumns = new ListDataModel(calItems);
                 }
                
                 /**
                 * This method fills the rows with projects which fit in the time span of the calendar items (columns)
                 * provided by the CalendarBean. This method checks if the TimeInterval of a project is before or after the time span
                 * and continues in this case. Otherwise the TimeInterval of the project is added to the rows.
                 */
                 protected final void fillRows() {
                 emf = (EntityManagerFactory)ServiceFinder.findBean("entityManagerFactory");
                 entityManager = emf.createEntityManager();
                 Employee tmpEmployee = Employee.getEmployeeByEmpId(entityManager, employeeId);
                
                 List<Project> tmpList = new ArrayList<Project>();
                 tIntervals = new ArrayList<TimeInterval>();
                 tIntervals.addAll(tmpEmployee.getTimeIntervalList());
                 /* Now add TeamItems (time recording entries belonging to the entire team)
                 * from the superior (DL or TeamLeader)*/
                 /* Now we have to add the items from the DL or TeamLeader. Items that do belong to the entire Team,
                 * e.g X-mas, company holidays, etc
                 * For that we first try to find the appropriate superior (DL or TeamLeader) of the current employee */
                 List<DeliveryLead> dLList = DeliveryLead.getAllDeliveryLeads(entityManager);
                 for(DeliveryLead dl : dLList) {
                 if(dl.hasTeamMember(entityManager, employeeId) == true) {
                 tIntervals.addAll(dl.getTeamTimeIntervals());
                 }
                 }
                 dLList = null;
                 List<TeamLeader> teamLeaderList = TeamLeader.getAllTeamLeader(entityManager);
                 for(TeamLeader tl : teamLeaderList) {
                 if(tl.hasTeamMember(entityManager, employeeId) == true) {
                 tIntervals.addAll(tl.getTeamTimeIntervals());
                 }
                 }
                 teamLeaderList = null;
                 customerFilterList.clear();
                
                 for(TimeInterval t: tIntervals) {
                 if(log.isDebugEnabled()) {
                 debugFillRows(t);
                 }
                 /* Just check the calendar objects. Are they in the same time span or are the overlapping? If no: just continue*/
                 GregorianCalendar gc_from = CalendarOperations.calculateDateForFirstDayOfWeek(t.getFrom());
                 //System.out.println("For date from : " + ti.getFrom() + " we calculated starting date: " + gc_from.getTime());
                 GregorianCalendar gc_until = CalendarOperations.calculateDateForLastDayOfWeek(t.getUntil());
                 //System.out.println("For date until : " + ti.getUntil() + " we calculated starting date: " + gc_until.getTime());
                 GregorianCalendar gc_calBeanstart = CalendarOperations.calculateDateForFirstDayOfWeek(calBean.getCalendarItems().get(0).getCalendarDate());
                 GregorianCalendar gc_calBeanend = CalendarOperations.calculateDateForFirstDayOfWeek(calBean.getCalendarItems().get(calBean.getCalendarItems().size()-1).getCalendarDate());
                 if( (gc_until.before(gc_calBeanstart)) || (gc_from.after(gc_calBeanend)) ) {
                 continue;
                 }
                
                 /* We have to check if the project is already in the list.
                 * This can happen because there can be more than one TimeInterval
                 * for e project
                 * */
                 if(tmpList.contains(t.getProject()) == false) {
                 /* and we check if the filter is set to ALL or to the specific customer */
                 //if( ( selectedCustomerFilterListItem.contains(new Integer(FILTER_ALL_CUSTOMERS))) ||
                 if( ( selectedCustomerFilterListItem.contains(Integer.valueOf(FILTER_ALL_CUSTOMERS))) ||
                 ( selectedCustomerFilterListItem.contains(
                 //new Integer(t.getProject().getCounterparty().getId())
                 Integer.valueOf(t.getProject().getCounterparty().getId())
                 ) == true)
                 ) {
                 tmpList.add(t.getProject());
                 }
                 }else{
                 log.debug("Project: " + t.getProject().toString() + " with T-ID: " + t.getId() + " is already in list for rows!");
                 }
                
                 /* To prevent duplicates in the filter list, we check the existing list first */
                 if(customerFilterListContains(t.getProject().getCounterparty().getId()) == false ) {
                 customerFilterList.add(new SelectItem(t.getProject().getCounterparty().getId(),t.getProject().getCounterparty().getName()));
                 log.debug("PUT: " + t.getProject().getCounterparty().getName() + " into customerList");
                 }
                
                 }
                 mWorkloadDataModel = new ListDataModel(tmpList);
                
                 if(entityManager.isOpen()) {
                 log.debug("EmployeeCalendarWorkloadBean closing em...");
                 entityManager.close();
                 }
                 }
                
                
                 /**
                 * This method is used to filter the rows. The list of selected customers
                 * is filled via the getter but we need this method to apply the filter
                 * because scope is request scope
                 */
                 public void setCustomerFilter() {
                 log.debug(this.getClass().getName() + "changeCustomerFilter");
                 log.debug("SelectedCustomer is: " + selectedCustomerFilterListItem);
                 if(monthsToShow != calBean.getMonthsToShow()) {
                 calBean.setMonthsToShow(monthsToShow);
                 calBean.repopulatCalendarItemsList();
                 }
                 Calendar cal = Calendar.getInstance();
                 cal.setTime(calendarStart);
                 calBean.setCurrentFirstDayOfMonth(cal);
                 calBean.repopulatCalendarItemsList();
                 fillColumns();
                 fillRows();
                 }
                
                 /**
                 * This is just a helper method to check if an Customer is already
                 * in a list. Used by the customer filter.
                 * @param customerID - ID of a customer
                 * @return boolean- - true if customer is already in list, ohterwise false
                 */
                 private boolean customerFilterListContains(int customerID) {
                 for(int i = 0; i<customerFilterList.size();i++) {
                 SelectItem tmpSI = customerFilterList.get(i);
                 Integer tmpSI_ID = (Integer)tmpSI.getValue();
                 if(tmpSI_ID.intValue() == customerID) {
                 return true;
                 }
                 }
                 return false;
                 }
                
                 /**
                 * This method has to be used to validate the list box for the customer filter. Because
                 * the same HTML/JSP web site is loaded again after changing the customer in the filter list box
                 * the originally entries within the list box are not longer valid. Without this method the JSF-engine
                 * states an validation exception and displays the site before choosing a value of the list box. by using this
                 * method, we update the columns and rows and by doing this also the content of the list box (in the method fillRows).
                 * No validation exception happens because the originally content of the list box is no longer used.
                 * @param context - the FacesContext
                 * @param toValidate - the component which should be validated
                 * @param value - the value transmitted from the component, here customer ID
                 */
                 public void validateSelectBox(FacesContext context, UIComponent toValidate, Object value) {
                 fillColumns();
                 fillRows();
                 if(log.isDebugEnabled()) {
                 debugValidateSelectBox((Object[])value);
                 }
                 ((UIInput)toValidate).setValid(true);
                 }
                
                 /*--------------------------------*/
                 /* Setter and getter do come here */
                 /*--------------------------------*/
                 public LoginController getLoginController() {
                 return loginController;
                 }
                
                 public void setLoginController(LoginController loginController) {
                 this.loginController = loginController;
                 }
                
                 /*....*/
                
                 public void doit() {
                 System.out.println("In doit");
                 System.out.println("in calculateprojectDetails: ");
                 DataModel wloadDataModel = getWorkloadDataModel();
                 if (wloadDataModel.isRowAvailable()) {
                 System.out.println("in calculateprojectDetails: rowAvailable");
                 Project row = (Project) wloadDataModel.getRowData();
                 DataModel columnDataModel = getColumnDataModel();
                 if (columnDataModel.isRowAvailable()) {
                 System.out.println("in calculateprojectDetails: columnAvailable");
                 CalendarItem column = (CalendarItem)columnDataModel.getRowData();
                 selectedProjectName = "! " + row.getName() + " :: " + column.getCalendarWeek() + " OLD: " + selectedProjectName;
                 System.out.println("in calculateprojectDetails: " + selectedProjectName);
                
                 for(TimeInterval ti : tIntervals) {
                 GregorianCalendar gc_from = CalendarOperations.calculateDateForFirstDayOfWeek(ti.getFrom());
                 gc_from.setFirstDayOfWeek(GregorianCalendar.MONDAY);
                 GregorianCalendar gc_until = CalendarOperations.calculateDateForLastDayOfWeek(ti.getUntil());
                 gc_until.setFirstDayOfWeek(GregorianCalendar.MONDAY);
                 GregorianCalendar gccolumn = CalendarOperations.calculateDateForFirstDayOfWeek(column.getCalendarDate());
                 gccolumn.setFirstDayOfWeek(GregorianCalendar.MONDAY);
                 if(ti.getProject().getId() == row.getId()){
                 if( (gccolumn.before(gc_from)) || (gccolumn.after(gc_until)) ) {
                 continue;
                 }else{
                 selectedTimeInterval = ti;
                 }
                 }
                 }
                 }
                 }
                 }
                }
                


                • 5. Re: a4j:support in t:dataTable / t:columns
                  adubovsky

                  Hello nille,

                  I have reproduced your problem. A4j:support with ajaxSingle=true inside t:columns does not call action and actionListener. And this is problem in tomahawk colums. You could use rich:columns instead of t:columns and all will be OK.

                  • 6. Re: a4j:support in t:dataTable / t:columns
                    nille

                    Hi adubovsky,

                    thanks for your effort. I decided to switch to richfaces datatable (and eliminate Tomahawk where possible) to prevent further irritations using two frameworks.

                    Thanks a lot and kind regards,
                    Nille