primefaces / primefaces

Ultimate Component Suite for JavaServer Faces
http://www.primefaces.org
MIT License
1.79k stars 761 forks source link

DataTable: Ajax select does not work when there are div tags within the columns #8783

Closed tomtibbetts closed 2 years ago

tomtibbetts commented 2 years ago

Describe the bug

Hi, I have a situation that when I wrap text within a column with div tags the ajax select (double and single) does not work. It will work if the first column has div tags and there are no other columns. This is very frustrating. I don't know if this happens just to this theme or if it's a general problem. I appreciate your help. Thank you. Below is my sample code.

This code will present two datatables with identical lists. The second table renders as a dialog when the 'Search' button is clicked. The ajax in the first data table works because I'm only displaying one column of data. The ajax in the dialog datatable does not because of the 'Div' tags. If I were to remove the 'Div' tags, then the ajax will work.

I'm using Harmony v4.10 and Primefaces 11.0.0.0 RC-2

[code]package com.bakerstreethhs.bshi.persistence.data.domain;

import java.util.ArrayList; import java.util.List; import java.util.Objects;

import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.Table;

import com.bakerstreethhs.bshi.persistence.data.enumerations.PriorityTypeEnum;

@Entity @Table(name = "provider") public class Provider extends BaseAudited { private Long providerId; private String firstName; private String lastName; private String middleName; private String address1; private String address2; private String address3; private String address4; private String city; private Code stateCode; private String postalCode; private Code countryCode; private String email; private String telephone1; private String telephone2; private String npi; private String license; private PriorityTypeEnum telephonePreference;

private List<Specialty> specialties = new ArrayList<Specialty>();

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getProviderId() {
    return providerId;
}
public void setProviderId(Long providerId) {
    this.providerId = providerId;
}

@Column(name = "firstName", length = 45)
public String getFirstName() {
    return firstName;
}
public void setFirstName(String firstName) {
    this.firstName = firstName;
}

@Column(name = "lastName", length = 45)
public String getLastName() {
    return lastName;
}
public void setLastName(String lastName) {
    this.lastName = lastName;
}

@Column(name = "middleName", length = 45)
public String getMiddleName() {
    return middleName;
}
public void setMiddleName(String middleName) {
    this.middleName = middleName;
}

@Column(name = "address1", length = 45)
public String getAddress1() {
    return address1;
}
public void setAddress1(String address1) {
    this.address1 = address1;
}

@Column(name = "address2", length = 45)
public String getAddress2() {
    return address2;
}
public void setAddress2(String address2) {
    this.address2 = address2;
}

@Column(name = "address3", length = 45)
public String getAddress3() {
    return address3;
}
public void setAddress3(String address3) {
    this.address3 = address3;
}

@Column(name = "address4", length = 45)
public String getAddress4() {
    return address4;
}
public void setAddress4(String address4) {
    this.address4 = address4;
}

@Column(name = "city", length = 45)
public String getCity() {
    return city;
}
public void setCity(String city) {
    this.city = city;
}

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "stateCodeId")
public Code getStateCode() {
    return stateCode;
}
public void setStateCode(Code stateCode) {
    this.stateCode = stateCode;
}

@Column(name = "postalCode", length = 45)
public String getPostalCode() {
    return postalCode;
}
public void setPostalCode(String postalCode) {
    this.postalCode = postalCode;
}

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "countryCodeId")
public Code getCountryCode() {
    return countryCode;
}
public void setCountryCode(Code countryCode) {
    this.countryCode = countryCode;
}

@Column(name = "email", length = 320)
public String getEmail() {
    return email;
}
public void setEmail(String email) {
    this.email = email;
}

@Column(name = "telephone1", length = 45)
public String getTelephone1() {
    return telephone1;
}
public void setTelephone1(String telephone1) {
    this.telephone1 = telephone1;
}

@Column(name = "telephone2", length = 45)
public String getTelephone2() {
    return telephone2;
}
public void setTelephone2(String telephone2) {
    this.telephone2 = telephone2;
}

@Column(name="telephonePreference")
@Enumerated(EnumType.STRING)
public PriorityTypeEnum getTelephonePreference() {
    return telephonePreference;
}
public void setTelephonePreference(PriorityTypeEnum telephonePreference) {
    this.telephonePreference = telephonePreference;
}

@Column(name = "npi", length = 45)
public String getNpi() {
    return npi;
}
public void setNpi(String npi) {
    this.npi = npi;
}

@Column(name = "license", length = 45)
public String getLicense() {
    return license;
}
public void setLicense(String license) {
    this.license = license;
}

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
@JoinTable(name="provider_specialty", joinColumns = @JoinColumn(name = "providerId"), inverseJoinColumns = @JoinColumn(name = "specialtyId"))
public List<Specialty> getSpecialties() {
    return specialties;
}
public void setSpecialties(List<Specialty> specialties) {
    this.specialties = specialties;
}

@Override
public int hashCode() {
    final int prime = 31;
    int result = super.hashCode();
    result = prime * result
            + Objects.hash(address1, address2, address3, address4, city, countryCode, email, firstName, lastName,
                    license, middleName, npi, postalCode, providerId, stateCode, telephone1, telephone2);
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (!super.equals(obj)) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    Provider other = (Provider) obj;
    return Objects.equals(address1, other.address1) && Objects.equals(address2, other.address2)
            && Objects.equals(address3, other.address3) && Objects.equals(address4, other.address4)
            && Objects.equals(city, other.city) && Objects.equals(countryCode, other.countryCode)
            && Objects.equals(email, other.email) && Objects.equals(firstName, other.firstName)
            && Objects.equals(lastName, other.lastName) && Objects.equals(license, other.license)
            && Objects.equals(middleName, other.middleName) && Objects.equals(npi, other.npi)
            && Objects.equals(postalCode, other.postalCode) && Objects.equals(providerId, other.providerId)
            && Objects.equals(stateCode, other.stateCode) && Objects.equals(telephone1, other.telephone1)
            && Objects.equals(telephone2, other.telephone2);
}

} [/code] [code]package com.bakerstreethhs.bshi.ui.web.reference;

import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong;

import javax.faces.context.FacesContext; import javax.faces.view.ViewScoped; import javax.inject.Named;

import org.primefaces.event.SelectEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory;

import com.bakerstreethhs.bshi.persistence.data.domain.Provider;

@Named(value = "providerSelect") @ViewScoped public class ProviderSelect implements Serializable{

Logger logger = LoggerFactory.getLogger(getClass());

private List<Provider> providerSearchResults = new ArrayList<Provider>();
private Provider selectedProvider;
private Provider selectedProviderBottom;
private AtomicLong providerId = new AtomicLong();

/**
 * 
 */
private static final long serialVersionUID = 1L;

public void preRender() {
    FacesContext facesContext = FacesContext.getCurrentInstance();

    if(facesContext.isPostback()) {
        logger.info("is postback = true");
    }
    else {
        loadProviders();
    }
}

public void onProviderSearchInitialize() {
    logger.info("onProviderSearchInitialize");
}

public void onProviderSelect(SelectEvent<Provider> event) {
    logger.info("onProviderSelect, id: " + event.getObject().getProviderId());
}

public List<Provider> getProviderSearchResults() {
    return providerSearchResults;
}

public void setProviderSearchResults(List<Provider> providerSearchResults) {
    this.providerSearchResults = providerSearchResults;
}

public Provider getSelectedProvider() {
    return selectedProvider;
}

public void setSelectedProvider(Provider selectedProvider) {
    this.selectedProvider = selectedProvider;
}

public Provider getSelectedProviderBottom() {
    return selectedProviderBottom;
}

public void setSelectedProviderBottom(Provider selectedProviderBottom) {
    this.selectedProviderBottom = selectedProviderBottom;
}

private void loadProviders() {
    providerSearchResults.add(getProvider("John", "DeBorre", "John's Back Pain Clinic", "345 Crooked St.", "Pheonix", null, "89989-", "(789) 456-1122"));
    providerSearchResults.add(getProvider("Thomas", "Tibbetts", "BSHI", "808 Baker St.", "Prosper", null, "75069-", "(612) 270-1212"));
    providerSearchResults.add(getProvider("Joan", "Baez", "Look Straigh Back Health", "907 E Marmalade St", "New Orleans", null, "78945-6689", "(456) 789-1212"));
}

private Provider getProvider(String firstName, String lastName, String address1, String address2, String city, Long stateCode, String postalCode, String telephone1) {
    Provider provider = new Provider();
    provider.setProviderId(providerId.incrementAndGet());
    provider.setFirstName(firstName);
    provider.setLastName(lastName);
    provider.setAddress1(address1);
    provider.setAddress2(address2);
    provider.setCity(city);
    provider.setPostalCode(postalCode);
    provider.setTelephone1(telephone1);

    return provider;
}

} [/code] [code]<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" template="/WEB-INF/template.xhtml">

<f:view>
    <f:metadata>
        <f:event type="preRenderView" listener="#{providerSelect.preRender()}"/>
    </f:metadata>
</f:view>

<ui:define name="title">Empty Page</ui:define>
<ui:define name="viewname">
    <li>Pages</li>
    <li>/</li>
    <li><p:link outcome="/empty">Empty</p:link></li>
</ui:define>

<ui:define name="content">
    <div class="p-grid">
        <div class="p-col-12">
            <div class="card">
                <h4>Empty Page</h4>
                <p>Use this page to start from scratch and place your custom content.</p>
                <p:commandButton value="Search" action="#{providerSelect.onProviderSearchInitialize()}" update="addProviderSearch" oncomplete="PF('addProviderSearch').show()" style="display:inline-block;margin-top:6px" icon="pi pi-home"/>
                <h:form id="id1">
                    <p:dataTable id="providerDtId" widgetVar="providerDtId" value="#{providerSelect.providerSearchResults}" var="provider"
                        showGridlines="true"
                        reflow="true"
                        stripedRows="true"
                        selectionMode="single"
                        selection="#{providerSelect.selectedProvider}"
                        rowKey="#{provider.providerId}"
                        styleClass="ui-datatable-sm"> 

                        <p:ajax event="rowDblselect" listener="#{providerSelect.onProviderSelect}" update="providerDtId" />

                        <p:column headerText="Id">
                            <h:outputText value="#{provider.providerId}" />
                        </p:column>

                        <p:column headerText="Provider" style="vertical-align: top">
                            <h:outputText value="#{provider.firstName} #{provider.lastName}" />
                            <h:outputText value="#{provider.address1}" />
                        </p:column>
                        <p:column headerText="Address" style="vertical-align: top" rendered="true">
                            <h:outputText value="#{provider.address2}" />
                            <h:outputText value="#{provider.city}, #{providerBottom.postalCode}" />
                        </p:column>
                        <p:column headerText="Contact" rendered="true">
                            <h:outputText value="#{provider.telephone1}" />
                        </p:column>
                    </p:dataTable>
                </h:form>
            </div>
        </div>
    </div>

    <p:dialog header="Search Provider" id="addProviderSearch" widgetVar="addProviderSearch" minHeight="40" width="1000" showEffect="fade" modal="true" rendered="true">

                <h:form id="datatableId">
                    <p:dataTable id="providerSearchResultsId" widgetVar="providerSearchResultsId" value="#{providerSelect.providerSearchResults}" var="providerBottom"
                        showGridlines="true"
                        reflow="true"
                        stripedRows="true"
                        selectionMode="single"
                        selection="#{providerSelect.selectedProviderBottom}"
                        rowKey="#{providerBottom.providerId}"
                        styleClass="ui-datatable-sm"> 

                        <p:ajax event="rowSelect" listener="#{providerSelect.onProviderSelect}" update="providerSearchResultsId" />

                        <p:column headerText="Id">
                            <h:outputText value="#{providerBottom.providerId}" />
                        </p:column>

                        <p:column headerText="Provider" style="vertical-align: top">
                            <div><h:outputText value="#{providerBottom.firstName} #{providerBottom.lastName}" /></div>
                            <div><h:outputText value="#{providerBottom.address1}" /></div>
                        </p:column>
                        <p:column headerText="Address" style="vertical-align: top" rendered="true">
                            <div><h:outputText value="#{providerBottom.address2}" /></div>
                            <div><h:outputText value="#{providerBottom.city}, #{providerBottom.postalCode}" /></div>
                        </p:column>
                        <p:column headerText="Contact" rendered="true">
                            <div style="height: 50;"><h:outputText value="#{providerBottom.telephone1}" /></div>
                        </p:column>

                        <f:facet name="footer">
                            <span style="float: right; display: block"> <p:commandButton value="Select"/></span>
                        </f:facet>
                    </p:dataTable>
                </h:form>
</p:dialog>                    
</ui:define>

</ui:composition>[/code]

Reproducer

Sample XHTML and beans are in the description

Expected behavior

Expected behavior is to be able to single/double click on a row to select that row even if there are div tags in the columns.

PrimeFaces edition

No response

PrimeFaces version

11.0.0.0

Theme

Harmony

JSF implementation

No response

JSF version

2.2

Browser(s)

Chrome and Microsoft Edge

melloware commented 2 years ago

@tomtibbetts Not a bug. Simply use the rowSelector attribute of the datatable like this rowSelector="td,div" that will make all cells with DIVS also selectable.

See the docs: https://primefaces.github.io/primefaces/11_0_0/#/components/datatable?id=attributes