How to add a style element inside a cell when the text inside changes to another value - javascript

I'm working on a ReactJS app which shows from an API a table populated of flights information. One of the columns is called status and it is changing value when the status of flight changes.
For example, we have a flight in departures and the status now is "to gate" but later is "Boarding"
What I would like to achieve is to add a style element like for To gate a color or icon but when boarding a different color and so on. So I would like to change the cell based on what will be the status value.
I don't know how to do that as I'm new to React. I was thinking on the component below on the row/cell of status maybe it is possible to add an if which changes based on the value. Don't know if it possible.
My component looks like this:
class FlightComponent extends React.Component {
render() {
const { data, activeTab } = this.props;
let columns = [
//{ Header: 'Date', accessor: 'date' },
{ Header: 'Time', accessor: 'time' },
{ Header: 'Expected', accessor: 'expected', Cell: (row) => (<div className="expected">{row.value}</div>) },
{ Header: 'Airline', accessor: 'airline', Cell: (row) => (<div className="airline-name">{row.value}</div>) },
{ Header: 'Flight No.', accessor: 'flight_no'},
];
if (activeTab == 1) {
columns.push({ Header: 'Destination', accessor: 'destination' })
} else {
columns.push({ Header: 'Arriving From', accessor: 'arriving_from' })
}
columns = columns.concat([
{ Header: 'Gate', accessor: 'gate' },
{ Header: 'Terminal', accessor: 'terminal' },
{ Header: 'Status', accessor: 'status' }
]);
return (
<div>
<ReactTable
columns={ columns }
data = {this.props.data}
/>
</div>
)
}
}

For react-table you could use Custom Cell, Header and Footer Rendering
You can use any react component or JSX to display content in column headers, cells and footers. Any component you use will be passed the following props (if available):
row - Original row from your data
original - The post-accessed values from the original row
index - The index of the row
viewIndex - the index of the row relative to the current page
level - The nesting depth (zero-indexed)
nestingPath - The nesting path of the row
aggregated - A boolean stating if the row is an aggregation row
subRows - An array of any expandable sub-rows contained in this row
You can check, here is wokring stackblitz demo.
Code Snippet
render() {
const data = [{
task: 'Demo 1',
status: 'pending'
}, {
task: 'Demo 2',
status: 'completed'
}]
const columns = [{
Header: 'Task Name',
accessor: 'task'
}, {
Header: 'Status',
accessor: 'status',
Cell: (row, original, index, viewIndex, level, nestingPath, aggregated, subRows) => {
row.styles['color'] = '#fff';
row.styles['backgroundColor'] = row.value == 'pending' ? 'red' : 'green';
return row.value.toUpperCase();
}
}];
return (
<ReactTable
data={data}
columns={columns}
defaultPageSize={5}
className="-striped -highlight"
/>
);
}

Related

How to I change my react table accesssor value based on data conditionally?

const data = {
name:"test1",
fclPrice:100,
lclPrice:null,
total:"50"
}
and my two columns:
const Datatable = [
{ Header: 'Name', accessor: 'name' },
{ Header: 'Price', accessor:'?' },
{ Header: 'Total', accessor:'total' }
];
I want accessor to take the price key which has number
For eg : if fclprice:null and lclPrice:100 it should take lclPrice and viceversa
You can a set a value of an accessor to a function which will take as a first argument the object with your data. Then, you can pass whatever condition you want to it:
const Datatable = [
{ Header: 'Price', accessor: (data)=>data.fclPrice || data.lclPrice},
];

Apply formatting to a row in ag-Grid

I am using a Ag-grid and have a requirement to set the last row (kind of total) to be bold and some background.
How can I achieve considering that we define columns ?
Also while the last row label (i.e. 1st column last row of grid) can be identified by checking params.value === 'Total', the other columns of that row can have dynamic total values (so cannot compare their actual text)
Below is how the config looks like (just for one of the columns)
[ {
headerName: '',
field: 'Status',
width: defaultWidth,
tooltipField:'status',
headerTooltip: 'Status',
sortable: false,
resizable: false,
cellStyle: params => {
if (params.value === 'Total') {
return { backgroundColor: color.orange,"font-weight":"bold" };
} else {
return null;
}
}
}, {.....}]
You can style the last row by using the Grid Option getRowStyle:
getRowStyle: (params) =>
params.node.lastChild
? { backgroundColor: 'orange', 'font-weight': 'bold' }
: null
Relevant Documentation: https://www.ag-grid.com/javascript-grid/row-styles/
See this implemented in the following sample: https://plnkr.co/edit/Vu32lW7jROiN0h5Q

How to format date in ant table?

I have a table with 6 columns, one of these columns is a date, The default display format of the date is 2020-04-30T21:30:07.000Z I want to change it to 30-04-2020.
export const columns = [
{ title: `ID`, dataIndex: `invoice_id`},
{ title: `Invoice Number`, dataIndex: `invoice_number` },
{ title: `Amount`, dataIndex: `invoice_amount`},
{ title: `Store Name`, dataIndex: `store_name`},
{ title: `Supplier Name`, dataIndex: `supplier_name`},
{ title: `Creation Date`, dataIndex: `invoice_date`,},
]
<Table bordered columns={columns} dataSource={invoices_list} />
I believe there should be a way to pass date format to the columns. I looked into the ant table documentations but didn't find a solution.
Another way to implement it is to define a 'render' for a date column like
{
title: 'Published at',
dataIndex: 'publishedAt',
key: 'publishedAt',
width: 210,
render: ((date:string) => getFullDate(date)) }
and just utilize date formatting function getFullDate. In your case it can look like
export const getFullDate = (date: string): string => {
const dateAndTime = date.split('T');
return dateAndTime[0].split('-').reverse().join('-');
};
In this case you'll also avoid using external libraries and will have a function to format the date in other places.
After looking into the antd documentation (https://ant.design/components/table/#API) it doesn't seems to have a property to handle your case. You should duplicate your column invoices_date to invoices_date_printabble which will have the good format to be printed.
invoices_list.map(el => {
let date = new Date(el.invoices_date)
el.invoices_date_printabble = date.getDay()+"/"+date.getMonth()+"/"+date.getFullYear()
})
And now your list is printable.
export const columns = [
{ title: `ID`, dataIndex: `invoice_id`},
{ title: `Invoice Number`, dataIndex: `invoice_number` },
{ title: `Amount`, dataIndex: `invoice_amount`},
{ title: `Store Name`, dataIndex: `store_name`},
{ title: `Supplier Name`, dataIndex: `supplier_name`},
{ title: `Creation Date`, dataIndex: `invoices_date_printabble`},
]
<Table bordered columns={columns} dataSource={invoices_list} />
Another way to do it using moment.js library.
First install moment.js into your project
npm i moment
Second import it to your component
import moment from 'moment';
Third change date format:
let invoices=response.data
invoices.map(el => {
let date = moment(new Date(el.invoice_date));
el.invoice_date = date.format("DD/MM/YYYY")
})
this worked for me!
export const columns = [
{
title: "ID",
key: "key",
render: (record) => {
return (
<div>
<p>{moment(record.invoice_id).format("DD-MM-YYYY")}</p>
</div>
);
},
},
]
I think the easiest way would be to use moment.js in the render method of the column options:
Your moment format of your existing date can be found in the moment documentation.
For future reference you can always use a millisecond timestamp and convert to whatever date format you want with moment.
export const columns = [
....
{ title: "Creation Date",
dataIndex: "invoices_date",
render: (invoice_date) => { return (<p>{moment(invoices_date, **"Moment Format"**).format(DD-MM-YYYY)}</p>)},
]

Dynamic lookup from React's state in material-table

I'm using the material-table component, which I fill with dynamic data coming from Redux (an array of objects), but then I do other things with that data inside my component's state. To create column dropdown filters, there's an element inside each column's array of options, lookup, that receives an object and creates the dropdown based on it's values.
I am extracting some items from my data and putting them inside an element in my component's state. This is an object, the same kind that lookup receives. The thing is that the component shows an empty dropdown, as if the object was empty, but it's not. I'm logging it in into the console and the object is filled with the data I need.
I initially thought it was a render problem, that the object is empty at the beggining, and then it's filled with data, but the component renders every time.(Yeah, React is reactive).
This is only the code needed to help me solve this problem:
Table component
import React, { Component } from "react";
import MaterialTable from "material-table";
class CustomTable extends Component {
state = {
column1: "",
column2: "",
column3: "",
column1FilterList: {}
columns: [
{
title: "Column1",
field: "column1",
editable: "onAdd",
filtering: true,
lookup: { ...this.column1FilterList }
},
{
title: "Column2",
field: "column2",
editable: "onAdd",
filtering: true,
},
{
title: "Column3",
field: "column3",
editable: "onAdd",
filtering: true
}
]
};
componentDidMount() {
this.props.fetchValues()
this.props.fetchApplications()
this.filterColumn1ExistingKeys()
}
filterColumn1ExistingKeys = () => {
return this.props.elements.map(element => {
return this.setState(prevState => ({
column1FilterList: {
...prevState.column1FilterList,
[element.name]: element.name
}
}))
})
}
render() {
return (
<div>
<MaterialTable
options={{
search: false,
actionsColumnIndex: 4,
filtering: true
}}
title="Search by"
columns={this.state.columns}
data={this.state.data}
/>
</div>
);
}
}
export default CustomTable;
The problem is how you save that data. You create a new object in the constructor with { ...this.column1FilterList }. This will create a new object which will act as the lookup object, which is filled with the initial data of column1FilterList (empty). Updating the column1FilterList later does not change that lookup object, because it is disconnected (new object). You have to update the lookup within the columns as well like this:
const filterColumn1ExistingKeys = () => {
const column1FilterList = this.state.column1FilterList;
this.props.elements.forEach(element => column1FilterList[element.name] = element.name)
this.setState({
column1FilterList,
columns: [{
title: "Column1",
field: "column1",
editable: "onAdd",
filtering: true,
lookup: { ...column1FilterList }
},
{
title: "Column2",
field: "column2",
editable: "onAdd",
filtering: true,
},
{
title: "Column3",
field: "column3",
editable: "onAdd",
filtering: true
}
]
})
}
Hope this helps. Let me know, if that works for you. If you have any questions, let me know. Happy coding.

CellValueChanged event in master detail of ag-grid

I am developing an ag-grid with master-detail set to true, the first row of master-detail has a editable cell i.e. "contractID", I want to populate other cells of the master-detail depending upon the fetched contractID.
I tried adding cellValueChanged in the columnDef and also tried to set params.api.rowModel.gridApi.cellValueChanged explicitly in onFirstDataRendered method for the detailGrid, but found no luck.
Master detail grid code:
detailCellRendererParams = {
detailGridOptions: {
columnDefs: [
{ headerName: "Contact ID", field: "contractId", editable: true, [cellValueChanged]: "cellValueChanged()" },
{ headerName: "Contact Name", field: "contractName" },
{ headerName: "Category", field: "category" }]
}
}
onFirstDataRendered(params) {
params.api.rowModel.gridApi.cellValueChanged = function (params) {
console.log("cellChanged");
}
}
In case someone still wonders, in detailGridOptions add:
onCellValueChanged: function ($event) {
// your code here
},
This will be triggered every time you change cell value.
Fixed the problem using custom cellRenderer and added an eventListener for change event. I just wanted to have this functionality on the first row of the grid, so added a condition to the rowIndex.
detailCellRendererParams = {
detailGridOptions: {
columnDefs: [{headerName: "Contact ID",field: "contractId",editable: true,
cellRenderer: (params) => {
params.eGridCell.addEventListener('change', (e) => {
this.getDetails(e.target.value);
})
},
{headerName: "Contact Name", field: "contractName"},
{headerName: "Category",field: "category"}]
}
}
getDetails(id): void {
this.apiService.getDetails(id)
.subscribe(result => {
this.gridApi.detailGridInfoMap.detail_0.api.forEachNode(node=> {
if(node.rowIndex == 0){
node.data = result;
node.gridApi.refreshCells();
}}
);
})}

Categories

Resources