DataTable column headers not displaying in nested table

UI Components for React
Post Reply
hwpeden
Posts: 5
Joined: 05 Aug 2019, 17:29

08 Aug 2019, 17:01

I have a datatable that has expandable rows with two datatable elements in them. For the life of me I can't get the column headers to show up in the nested tables.


Image https://imgur.com/f6Tk99v



I can't see anything that is preventing my column headers from showing up. I was able to duplicate the same effect by pasting the example datatable to the expansion template (see below).

Has anyone ever seen this happen before?

Thanks.


Here's the row detail component:

Code: Select all

import React from 'react'
import { apiCall } from '../../common/api'
import { debugLog, errorTypes } from '../../common/global'
import {ProgressBar} from 'primereact/progressbar'
// import { showGrowl, growlTypes } from '../../common/Growls'
import {Growl} from 'primereact/growl'
import {DataTable} from 'primereact/datatable'
import { Column } from 'primereact/column';



export default class JobDetail extends React.Component{
    constructor() {
        super()
        this.state = {
           apiCallComplete: false,  
           project: {},
           detailRows: [],
           tasks: [],

        }
        this.fetchData = this.fetchData.bind(this)
    }

    fetchData() {
        const {job, token} = this.props


        // get project detail
        apiCall('fetchTable', 'returnTable', `view_project_detail where "ProjectID" = ${job.ProjectID}`, token)
        .then( payload => {
            debugLog("Jobs.js", "Fetch project detail", payload)
            let projectDetailDataTableRows = []

            if (payload.data.rows[0].ProjectID === job.ProjectID) {
                for (let [key, value] of Object.entries(payload.data.rows[0])) {
                    if (key === 'lastchecktime') {
                        
                    }
                    switch(key){
                        case 'lastchecktime':
                            if (value) {
                                const date = new Date(value)
                                value = date.toString()
                            }    
                            break
                        case 'Complete' :
                            (value) ? value = 'Yes' : value = 'No'
                            break
                        default :
                            break
                            
                    }
                    projectDetailDataTableRows.push({projectAttribute: key, projectValue: value})
                }
                this.setState({project: payload.data.rows[0], detailRows: projectDetailDataTableRows})
            }
        })
        .catch( error => {
            // errors are handled in api.js
        //    if (error === errorTypes.ERROR_TOKEN_EXPIRED )
        //        showGrowl(growlTypes.GROWL_TOKEN_EXPIRED, this.growl)
        })

        // get task detail
        apiCall('fetchTable', 'returnTable', `view_tasks where "ProjectID" = ${job.ProjectID}`, token)
        .then( payload => {
            debugLog('JobDetail.js', 'fetch task detail ', payload)
            this.setState({ apiCallComplete: true , tasks: payload.data.rows })

        })
        .catch( error => {})
    }


    componentDidMount() {
        this.fetchData()
    }
    render() {
        const {job} = this.props
        // const { project } = this.state
        return(
            <div >
                {
                    this.state.apiCallComplete && 
                <div className="p-grid p-fluid" style={{padding:'2em 1em 1em 1em'}}>
                    <div>
                        <div className="p-col-12" style={{}}>
                            <h1>{`${job.ClientName} - ${job.ProjectName}`}</h1> 
                        </div>
                       <div className='p-col-12 '>
                       <div className="p-grid p-fluid">
                            <div className="p-md-4">
                            {/**stateKey={`projectDetail${project.id}`} stateStorage="local" */}
                                <DataTable value={this.state.detailRows} >
                                    <Column field="projectAttribute" header="projectAttribute"/> 
                                    <Column field="projectValue" header="projectValue" /> 
                                </DataTable>
                            </div>
                            <div className="p-md-8">
                                <DataTable header="Task Detail"  value={this.state.tasks} ref={el => this.dt = el} responsive={true}>
                                    <Column field="Task" header={"Task"}/>
                                    <Column field="Description" header="Description"/>
                                    <Column field="employee" header="Employee"/>
                                    <Column field="PctTimeRqd" header="% Time Required"/>
                                    <Column field="Hours" header="Hours"/>
                                    <Column field="StartDate" header="Start Date"/>
                                    <Column field="Complete" header="Complete"/>
                                    <Column 
                                    field={"ProjectName"} 
                                    columnKey="ProjectName"
                                    header={"Job Name"} 
                                    sortable={true}
                                    
                                />
                                </DataTable>
                            </div>
                        </div>
                       </div> 
                    </div>
                    </div>
                }
                {
                    !this.state.apiCallComplete && 
                    <ProgressBar mode="indeterminate"/>
                }
                
            </div>
            )
    }
    
}





Here's my parent component:

Code: Select all

import React, {Component} from 'react';
import {DataTable} from 'primereact/datatable'
import {Column} from 'primereact/column'
import { apiCall } from '../../common/api'
import { withCookies } from 'react-cookie'
import { debugLog, handleError } from '../../common/global'
import { ProgressBar } from 'primereact/progressbar'
import {MultiSelect} from 'primereact/multiselect'
import {Button} from 'primereact/button'
import JobDetail from './JobDetail'
import { showGrowl, growlTypes } from '../../common/Growls'
import { errorTypes } from '../../common/global'
import {Growl} from 'primereact/growl'
// import util from 'util'

class Jobs extends Component {
    constructor() {
        super()
        this.state = {
            expandedRows : null,
            offices: null, // selected offices from filter
            clients: null, // selected clients from filter
            jobs: null, // selected jobs from filter
            officeChoices: [
                {label: 'CO', value: 'CO'},
                {label: 'JAX', value: 'JAX'},
                {label: 'MEM', value: 'MEM'},
                {label: 'ROS', value: 'ROS'},
                {label: 'TOR', value: 'TOR'},
                {label: 'RE', value: 'RE'}, 
            ], 
            jobChoices: [],
            clientChoices: [],

        }
        this.fetchTable = this.fetchTable.bind(this)
        this.onOfficeChange = this.onOfficeChange.bind(this)
        this.onJobChange = this.onJobChange.bind(this)
        this.onClientChange = this.onClientChange.bind(this)
        this.export = this.export.bind(this)
    }

    rowExpansionTemplate = data => {
        const { cookies } = this.props
        return(
        <JobDetail 
            token={cookies.get('token')}
            job={data}
        />)
    }

    // format Project Start Date Column
    dateTemplate = (rowData, column) => {
        const date = new Date(rowData.ProjectStartDate) 
       return <span>{date.toDateString()}</span>
    }

    export() {
        this.dt.exportCSV()
    }


    fetchTable() {
        // get table from database and update component state
        const {cookies} = this.props
        apiCall("fetchTable", "returnTable", "view_projects", cookies.get('token'))
        .then(payload => {
            debugLog("Jobs.js","fetch jobs view ",payload)
            const {data} = payload 


            // generate columns from query data
            // if (data.fields) {
            //     let cols = data.fields.map( col => {
            //         return <Column 
            //                     key={col.columnID} 
            //                     field={col.name} 
            //                     header={col.name} 
            //                     sortable={true}
            //                 />
            //     })

                // this.setState({cols})
                // generate rows from query data
                if (data.rows) {
                    const jobList = data.rows.map( row => {
                        return {label: row.ProjectName, value: row.ProjectName}
                    })
                    this.setState({ 
                        rows: data.rows, 
                        // apiCallComplete: true,
                        jobChoices: jobList,
                    })
                }
            // }
        })
        // query client mult-select list
        .then(() => {
            apiCall("fetchTable", "returnTable", "view_client_select", cookies.get('token'))
            .then(payload => {
                debugLog("Jobs.js", "api call client select view", payload)
                const {data} = payload

                const clientList = data.rows.map( row => {
                    return( {label: row.ClientName, value: row.ClientName })
                })
                this.setState({clientChoices: clientList, apiCallComplete: true})
            })
            .catch( error => console.error(error))
        })
        .catch(error => {
            handleError(error, "Jobs.js")
            if (error === errorTypes.ERROR_TOKEN_EXPIRED)
                showGrowl(growlTypes.GROWL_TOKEN_EXPIRED, this.growl)
        })
    }

    componentDidMount() {
       // call fetchTable() when component mounts for initial table load
       this.fetchTable() 

       // update filter state from cookies 
       const { cookies } = this.props
       const officeSelection = cookies.get('jobs_officeSelection')
       const jobSelection = cookies.get('jobs_jobSelection')
       const clientSelection = cookies.get('jobs_clientSelection')
       this.setState({
           offices: officeSelection,
           clients: clientSelection,
           jobs: jobSelection,
    })
    }

    onOfficeChange(event) {
        // handle office filter selection
        this.dt.filter(event.value, 'LocationCode', 'in')
        this.setState({offices: event.value})

        // save filter to cookies
        const { cookies } = this.props
        cookies.set('jobs_officeSelection', event.value, { path: '/'})
    }
    onClientChange(event) {
        // handle filter selection
        this.dt.filter(event.value, 'ClientName', 'in')
        this.setState({clients: event.value})

        // save filter to cookies
        const { cookies } = this.props
        cookies.set('jobs_clientSelection', event.value, { path: '/'})
    }   
    onJobChange(event) {
        // handle filter selection
        this.dt.filter(event.value, 'ProjectName', 'in')
        this.setState({jobs: event.value})

        // save filter to cookies
        const { cookies } = this.props
        cookies.set('jobs_jobSelection', event.value, { path: '/'})
    }
    render() {
        let officeFilter =  <MultiSelect 
                                style={{width: '100%'}} 
                                value={this.state.offices} 
                                options={this.state.officeChoices} 
                                onChange={this.onOfficeChange} 
                            />

        let clientFilter = <MultiSelect 
                                style={{width: '100%'}} 
                                value={this.state.clients} 
                                options={this.state.clientChoices} 
                                onChange={this.onClientChange} 
                                filter={true}
                                
                            />
        let jobFilter = <MultiSelect 
                                style={{width: '100%'}} 
                                value={this.state.jobs} 
                                options={this.state.jobChoices} 
                                onChange={this.onJobChange} 
                                filter={true} 
                            />
        let header = <div style={{textAlign:'left'}}><Button type="button" icon="pi pi-external-link" alt="Export current table state." iconPos="left" label="CSV" onClick={this.export}></Button></div>;
        return (
            <div className="p-grid">
                <Growl life={3000} ref={el => this.growl = el} style={{marginTop: 50}}/>
                <div className="p-col-12">
                    <div className="card">
                        <h1>Jobs</h1>
                        {
                            // show loading bar while data loads
                            !this.state.apiCallComplete &&
                            <ProgressBar mode="indeterminate"/>
                        }
                        { this.state.apiCallComplete  && 
                            <DataTable
                                ref={el => this.dt = el }
                                value={this.state.rows}
                                header={header}
                                expandedRows={this.state.expandedRows}
                                onRowToggle={ e => this.setState({ expandedRows: e.data })}
                                responsive={true}
                                reorderableColumns={true}
                                rowExpansionTemplate={this.rowExpansionTemplate}
                                dataKey="ProjectID"
                                globalFilter={this.state.globalFilter}
                                stateKey="tablestate-jobs1"
                                stateStorage="local" // session storage seems to be the only option that works properly
                            >
                                <Column key={'expander'} expander={true} style={{width: '3em'}}/>               
                                <Column 
                                    field={"ProjectName"} 
                                    columnKey="ProjectName"
                                    header={"Job Name"} 
                                    sortable={true}
                                    filter={true}    
                                    filterElement={jobFilter}
                                    
                                />
                                <Column 
                                    field={"ClientName"} 
                                    columnKey="ClientName"
                                    header={"Client"} 
                                    sortable={true} 
                                    filter={true}    
                                    filterElement={clientFilter}
                                    
                                />
                                <Column 
                                    field={"LocationCode"} 
                                    columnKey="LocationCode"
                                    header={"Office"} 
                                    sortable={true}                                     
                                    filter={true}
                                    filterElement={officeFilter}
                                />
                                <Column 
                                    field={"JobNumber"} 
                                    columnKey="JobNumber"
                                    header={"Job Number"} 
                                    sortable={true} 
                                    filter={true}    
                                />
                                <Column 
                                    field={"ProjectStartDate"} 
                                    columnKey="ProjectStartDate"
                                    header={"Start Date"} 
                                    sortable={true} 
                                    body={this.dateTemplate} 
                                    filter={true}    
                                />
                            </DataTable>
                        }
                    </div>
                </div>
            </div>
        );
    }
}

export const JobsWithCookies = withCookies(Jobs)








I have tried pasting the example datatable component as the expanded row detail with the same results
Image https://imgur.com/qEb8CRc

Code: Select all

    rowExpansionTemplate = data => {
        return(
            <DataTable value={this.state.cars}>
                <Column field="vin" header="Vin" />
                <Column field="year" header="Year" />
                <Column field="brand" header="Brand" />
                <Column field="color" header="Color" />
            </DataTable>
            )

hwpeden
Posts: 5
Joined: 05 Aug 2019, 17:29

14 Aug 2019, 18:11

It looks like the <th> that gets rendered by the <DataTable> is inheriting the css from the t-body of the parent table. They are hiding the column name from each of the body cells, thus hiding the column names in the nested datatable.

Code: Select all

	.p-datatable-responsive .p-datatable-tbody>tr>td .p-column-title {
		display: none;
	}

aragorn
Posts: 3761
Joined: 29 Jun 2013, 12:38

10 Sep 2019, 12:22

Hi,

This issue is fixed for the next version. But, the expanded table doesn't support responsive feature. Please create a github issue for this.

Best Regards

hwpeden
Posts: 5
Joined: 05 Aug 2019, 17:29

10 Sep 2019, 15:21

aragorn wrote:
10 Sep 2019, 12:22
Hi,

This issue is fixed for the next version. But, the expanded table doesn't support responsive feature. Please create a github issue for this.

Best Regards
Create an issue specifically for responsive nested table support?

aragorn
Posts: 3761
Joined: 29 Jun 2013, 12:38

11 Sep 2019, 08:27

https://github.com/primefaces/primereact/issues

You can use this link to create a github ticket.

Thanks a lot!

heindiez
Posts: 6
Joined: 18 Sep 2019, 10:51

19 Sep 2019, 02:12

I am experiencing the same issue. The nested DataTable under a expandata DataTable row is not showing Header names. Is there a posible workaround to fix this?

hwpeden
Posts: 5
Joined: 05 Aug 2019, 17:29

19 Sep 2019, 20:48

This is how I worked around the issue:

I created a dataview to simulate a grid. See below:

It's basically a flexgrid for the column headers and then a dataview that has a matching flexgrid for the rows. This is my workaround until they release the update.

From render():

Code: Select all

                    <div>
                    <div className="p-grid">
                        <div className="p-md-2">Task</div> 
                        <div className="p-md-2">Job</div> 
                        <div className="p-md-1">% Time Required</div> 
                        <div className="p-md-1">Budgeted Hours</div> 
                        <div className="p-md-1">Start Date</div> 
                        <div className="p-md-1">Task Weeks</div> 
                        <div className="p-md-2">End Date</div> 
                        <div className="p-md-1">Complete</div> 
                        <div className="p-md-1"></div> 
                    </div>
                        <DataView 
                            value={this.state.viewData} 
                            layout={this.state.layout} 
                            itemTemplate={this.itemTemplate} 
                        />
                    </div>



Item Template:

Code: Select all

    itemTemplate(row) {
        const startDate = new Date(row.StartDate)
        const endDate = new Date(row.EndDate)
        const { type } = this.props

        switch (type) {
            case 'Staffing' :
                return(
                    <div className="p-grid p-flex">
                        <div className="p-md-2">{row.Task}</div> 
                        <div className="p-md-2">{row.Project}</div> 
                        <div className="p-md-1">{(row.PctTimeRqd*100).toPrecision(3)}</div> 
                        <div className="p-md-1">{row.Hours}</div> 
                        <div className="p-md-1">{startDate.toDateString()}</div> 
                        <div className="p-md-1">{row.TaskWeeks.toPrecision(3)}</div> 
                        <div className="p-md-2">{endDate.toDateString()}</div> 
                        <div className="p-md-1">{(row.Complete) ? "Yes" : "No"}</div> 
                        <div className="p-md-1">
                            <Button icon="pi pi-pencil" onClick={() => this.handleShowPopUp(row)}></Button> 
                        </div> 
                    </div>
                )
            case 'Jobs' :
                return(
                    <div className="p-grid p-flex">
                        <div className="p-md-2">{row.Task}</div> 
                        <div className="p-md-2">{row.employee}</div> 
                        <div className="p-md-1">{(row.PctTimeRqd*100).toPrecision(3)}</div> 
                        <div className="p-md-1">{row.Hours}</div> 
                        <div className="p-md-1">{startDate.toDateString()}</div> 
                        <div className="p-md-1">{row.TaskWeeks.toPrecision(3)}</div> 
                        <div className="p-md-2">{endDate.toDateString()}</div> 
                        <div className="p-md-1">{(row.Complete) ? "Yes" : "No"}</div> 
                        <div className="p-md-1">
                            <Button icon="pi pi-pencil" onClick={() => this.handleShowPopUp(row)}></Button> 
                        </div> 
                    </div>
                )
            default :
                    return(<div></div>)
        }
}

heindiez
Posts: 6
Joined: 18 Sep 2019, 10:51

20 Sep 2019, 08:41

thanks for the reply.

I was also able to fix this by adding an exception to the header under the expanded row.
I added this line of code to the css file of the component.

Code: Select all

.p-datatable-responsive .p-datatable-tbody>tr>td .p-datatable-thead .p-column-title {
    display: block;
}
I hope it helps.

hwpeden
Posts: 5
Joined: 05 Aug 2019, 17:29

20 Sep 2019, 21:41

That does help. I try to stay away from css at all costs haha. Your solution looks extremely straightforward though.

Post Reply

Return to “PrimeReact”

  • Information
  • Who is online

    Users browsing this forum: No registered users and 1 guest