0

I have a page (page1.xhtml) with a tabView and a dataTable within one of the tabs. The dataTableuses lazy loading and should provide multisort mode. Therefore I use a List<SortMeta> for the sortBy attribute of dataTable as found here (Initial sortorder for PrimeFaces datatable with multisort).

The List<SortMeta> is created in getPreSortOrder() but findComponent() for clientId "myForm:tabs:data:colName" always returns null!

In another constellation (page2.xhtml) where I have the dataTable not in a tabView findComponent() always returns the correct column component!

So the problem seems to be the combination with tabView?!

Any hints welcome - Thank you!

page.xhtml:

<html>
<f:metadata>
  <f:viewAction action="#{model.loadData}"/>
</f:metadata>
<ui:composition template="/WEB-INF/template.xhtml">
  <ui:define name="content">
    <h:form id="myForm">
    <p:panelGrid>...</p:panelGrid>
      <p:tabView id="tabs">
        <p:tab id="tab1">...</p:tab>
        <p:tab id="tab2">...</p:tab>
        <p:tab id="tab3">
          <p:dataTable id="data" lazy="true" 
                       value="#{model.personTableModel}" var="item" 
                       sortMode="multiple" sortBy="#{model.tableMode.preSortOrder}">
            <p:column id="colName" sortBy="#{item.name}"> // <-- findComponent for "myForm:tabs:data:colName" always returns null !!!
              <h:outputText value="#{item.name}"/>
            </p:column>
            <p:column id="colAddress" sortBy="#{item.address}">
              <h:outputText value="#{item.address}"/>
            </p:column>
          </p:dataTable>
        </p:tab>
      </p:tabView>
    </h:form>
   </ui:define>
  </ui:composition>
</html>

page2.xhtml:

<html>
<f:metadata>
  <f:viewAction action="#{model.loadData}"/>
</f:metadata>
<ui:composition template="/WEB-INF/template.xhtml">
  <ui:define name="content">
    <h:form id="myForm">
    <p:panelGrid>...</p:panelGrid>
    <p:outputPanel id="tables">
      <p:fieldset>
        <p:dataTable id="data" lazy="true" 
                     value="#{model.personTableModel}" var="item" 
                     sortMode="multiple" sortBy="#{model.tableMode.preSortOrder}">
            <p:column id="colName" sortBy="#{item.name}"> // <-- findComponent for "myForm:data:colName" always component
              <h:outputText value="#{item.name}"/>
            </p:column>
            <p:column id="colAddress" sortBy="#{item.address}">
              <h:outputText value="#{item.address}"/>
            </p:column>
          </p:dataTable>
        <p:fieldset>
      </p:outputPanel>
    </h:form>
   </ui:define>
  </ui:composition>
</html>

Model.java:

@Named // javax.inject.Named
@ViewScoped // javax.faces.view.ViewScoped
public class Model implements Serializable {

  private static final String COL_NAME_CLIENT_ID = "myForm:tabs:data:colName";
  @Inject PersonTableDataModel personTableDataModel; // with getter & setter

  public void loadData() {
    List<SortMeta> preSortOrder = getPreSortOrder(COL_NAME_CLIENT_ID, "name", SortOrder.ASCENDING);
    personTableDataModel.setPreSortOrder(preSortOrder);
  }

  private List<SortMeta> getPreSortOrder(String columnId, String sortField, SortOrder sortOrder) {
    UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
    UIComponent column = viewRoot.findComponent(columnId); // <-- ALWAYS RETURNS NULL
    if (Objects.isNull(column)) {
      return Collections.emptyList();
    }

    List<SortMeta> preSortOrder = new ArrayList<>();

    SortMeta sm = new SortMeta();
    sm.setSortBy((UIColumn) column);
    sm.setSortField(sortField);
    sm.setSortOrder(sortOrder);
    preSortOrder.add(sm);

    return preSortOrder;
  }
}

PersonTableDataModel.java:

public class PersonTableDataModel extends TableModel<Person> {
}

TableModel.java:

public class TableModel<T> extends LazyDataModel<T> {
  private List<SortMeta> preSortOrder; // with getter & setter
}

I am using Primefaces 6.1 on Wildfly 10.0.0.Final

EDIT:

I added a TabChange event listener changeTab() and traversed the UIComponents and at the end the correct clientId is written to the output?!

Model.java:

public void changeTab(TabChangeEvent event) {
    TabView tabView = (TabView) event.getComponent();
    logger.entry(tabView.getActiveIndex());
    Tab tabProzesse = tabView.findTab("myForm:tabs:tab3");
    if (Objects.nonNull(tabProzesse)) {
        List<UIComponent> childs = tabProzesse.getChildren();
        Optional<UIComponent> c = childs.stream().filter(child -> child.getClientId().equals("myForm:tabs:data")).findFirst();
        if (c.isPresent() && c.get() instanceof DataTable) {
            DataTable t = (DataTable) c.get();
            Optional<UIColumn> optColName = t.getColumns().stream().filter(col -> col.getClientId().contains("colName")).findFirst();
            optColName.ifPresent(colName -> {
                logger.debugf("colName.clientId=%s", colName.getClientId()); // <-- output colName.clientId=myForm:tabs:data:colName
            });
        }

    }
}
raho
  • 129
  • 4
  • 18
  • Do a 'view source' of the client-side html. Look what the client-id of the column actually is. Isn't there an id of the tab in there as well? (tab1, tab2, tab3)...? – Kukeltje Oct 30 '17 at 11:21
  • the generated html source looks like this `Name` - so there is no clientId for the tabs so far! in https://stackoverflow.com/questions/8634156/how-to-find-out-client-id-of-component-for-ajax-update-render-cannot-find-compo @BalusC wrote that ` is NOT a NamingContainer – raho Oct 30 '17 at 12:14
  • What if you iterate in the same way in the `getPreSortOrder` – Kukeltje Oct 30 '17 at 19:13
  • sure I can do that, but I wanted to understand the reason why it does not work as excpected or what I'm doing wrong?! – raho Oct 31 '17 at 06:57
  • Maybe nothing. But knowing the outcome helps narrowing down the problem – Kukeltje Oct 31 '17 at 08:22
  • is it worth to create an issue in the primefaces issue tracker? – raho Oct 31 '17 at 09:51

1 Answers1

0

I fixed it by this work-around:

  • added tabChange event listener to tabView and update datatable data
  • added binding to initial sort column with id colName
  • set preSortOrder for tableModel in onTabChange listener

page.xhtml:

<p:tabView id="tabs">
  <p:ajax event="tabChange" listener="#{model.onTabChange}" update="data"/>
  <p:tab id="tab1">...</p:tab>
  <p:tab id="tab2">...</p:tab>
  <p:tab id="tab3">
    <p:dataTable id="data" lazy="true" 
                 value="#{model.personTableModel}" var="item" 
                 sortMode="multiple" 
                 sortBy="#{model.personTableModel.preSortOrder}">
      <p:column id="colName" sortBy="#{item.name}" binding="#{mode.colName}"> 
      </p:column>
    </p:dataTable>
  </p:tab>
</p:tabView>

Model.java:

private UIComponent colName;

public UIComponent getColName() {
    return colName;
}

public void setColName(UIComponent colNameProzess) {
    this.colName = colName;
}

public void onTabChange(TabChangeEvent event) {
    TabView tabView = (TabView) event.getComponent();
    UIComponent uiComponent = findComponent(tabView, colName.getId());
    if (Objects.nonNull(uiComponent) && uiComponent instanceof org.primefaces.component.api.UIColumn) {
      List<SortMeta> preSortOrder = getPreSortOrder(uiComponent, "name", SortOrder.ASCENDING);
      personTableDataModel.setPreSortOrder(preSortOrder);
    } 
}

private List<SortMeta> getPreSortOrder(UIColumn column, String sortField, SortOrder sortOrder) {
    List<SortMeta> preSortOrder = new ArrayList<>();

    SortMeta sm = new SortMeta();
    sm.setSortBy(column);
    sm.setSortField(sortField);
    sm.setSortOrder(sortOrder);
    preSortOrder.add(sm);

    return preSortOrder;
}

private UIComponent findComponent(UIComponent uiComponent, String id) {
    FacesContext context = FacesContext.getCurrentInstance();
    UIComponent base = Objects.nonNull(uiComponent) ? uiComponent : context.getViewRoot();
    final UIComponent[] found = new UIComponent[1];
    base.visitTree(VisitContext.createVisitContext(context), (context1, component) -> {
        if (component.getId().equals(id)) {
            found[0] = component;
            return VisitResult.COMPLETE;
        }
        return VisitResult.ACCEPT;
    });

    return found[0];
}
raho
  • 129
  • 4
  • 18