I think I am missing some React or JavaScript knowledge to solve what I want to do.
Background:
My problem:
I want the table to be filtered on load. But I can't figure out how to trigger the filter once when everything is loaded.
The column headers code:
// ***Generate the column structure necessary for ReactTable
generateColumnHeaders(api_part_labels) {
let label_array = []
for (var key in api_part_labels) {
const hasHeader = api_part_labels[key].includes(">")
if (hasHeader) {
label_array.push([api_part_labels[key].split(">")[0], api_part_labels[key].split(">")[1], key]);
} else {
label_array.push([api_part_labels[key], api_part_labels[key], key]);
}
}
let mainHeaders = label_array.map((x) => x[0]);
mainHeaders = Array.from(new Set(mainHeaders));
let output = [];
console.log(this.state.parsed);
for (var i in mainHeaders) {
let labels = label_array.filter(x => x[0] === mainHeaders[i]);
if (labels.length === 1) {
let param = labels[0][2];
output.push({
Header: labels[0][1],
accessor: param,
id: param,
width: this.calculateColumnWidth(param, labels[0][1]),
});
} else {
let columns = [];
for (var i_label in labels) {
var label = labels[i_label];
let param = label[2];
columns.push({
Header: label[1],
accessor: param,
id: param,
width: this.calculateColumnWidth(param, label[1]),
filteredValue: this.state.parsed[param],
Filter: ({ filter, onChange }) => {
return (
<input
onKeyPress={event => {
if (event.keyCode === 13 || event.which === 13) {
let value = event.target.value;
onChange(value);
// update the parsed state with the event filter value
this.updateParsed(param, value);
// update 1) the query string, and 2) the URL
let stringified = queryString.stringify(this.state.parsed);
this.props.location.search = stringified;
this.props.history.push(`?${stringified}`);
}
}}
defaultValue={this.state.parsed[param]}
/>
)},
});
}
output.push({ Header: labels[0][0], columns: columns });
}
}
return output;
};
My React Table:
return (
<ReactTable
// data and columns
data = { this.parts }
noDataText = "Whoops! No data available!"
columns = { this.part_columns }
// appearance
defaultPageSize = {20}
className = "-striped -highlight"
style = {{ height: "800px" }}
// sorting and filtering
defaultSorted = {[ { id: "part_number", desc: false } ]}
filterable
defaultFilterMethod = {(filter, row) =>
String(row[filter.id]).toLowerCase().includes(filter.value.toLowerCase())}
/>
)
I think I am missing some React or JavaScript knowledge to solve what I want to do.
Background:
My problem:
I want the table to be filtered on load. But I can't figure out how to trigger the filter once when everything is loaded.
The column headers code:
// ***Generate the column structure necessary for ReactTable
generateColumnHeaders(api_part_labels) {
let label_array = []
for (var key in api_part_labels) {
const hasHeader = api_part_labels[key].includes(">")
if (hasHeader) {
label_array.push([api_part_labels[key].split(">")[0], api_part_labels[key].split(">")[1], key]);
} else {
label_array.push([api_part_labels[key], api_part_labels[key], key]);
}
}
let mainHeaders = label_array.map((x) => x[0]);
mainHeaders = Array.from(new Set(mainHeaders));
let output = [];
console.log(this.state.parsed);
for (var i in mainHeaders) {
let labels = label_array.filter(x => x[0] === mainHeaders[i]);
if (labels.length === 1) {
let param = labels[0][2];
output.push({
Header: labels[0][1],
accessor: param,
id: param,
width: this.calculateColumnWidth(param, labels[0][1]),
});
} else {
let columns = [];
for (var i_label in labels) {
var label = labels[i_label];
let param = label[2];
columns.push({
Header: label[1],
accessor: param,
id: param,
width: this.calculateColumnWidth(param, label[1]),
filteredValue: this.state.parsed[param],
Filter: ({ filter, onChange }) => {
return (
<input
onKeyPress={event => {
if (event.keyCode === 13 || event.which === 13) {
let value = event.target.value;
onChange(value);
// update the parsed state with the event filter value
this.updateParsed(param, value);
// update 1) the query string, and 2) the URL
let stringified = queryString.stringify(this.state.parsed);
this.props.location.search = stringified;
this.props.history.push(`?${stringified}`);
}
}}
defaultValue={this.state.parsed[param]}
/>
)},
});
}
output.push({ Header: labels[0][0], columns: columns });
}
}
return output;
};
My React Table:
return (
<ReactTable
// data and columns
data = { this.parts }
noDataText = "Whoops! No data available!"
columns = { this.part_columns }
// appearance
defaultPageSize = {20}
className = "-striped -highlight"
style = {{ height: "800px" }}
// sorting and filtering
defaultSorted = {[ { id: "part_number", desc: false } ]}
filterable
defaultFilterMethod = {(filter, row) =>
String(row[filter.id]).toLowerCase().includes(filter.value.toLowerCase())}
/>
)
I think you need(ed) the defaultFiltered
prop on ReactTable:
defaultFiltered={[
{
id: 'reporting_year',
value: new Date().getFullYear(),
},
]}
I also wanted to customize the edit control in the filter box to be a drop-down containing the unique values in the column. As the documentation is pretty weak (or I'm slow and/or it's late), I'll include the column definition here too:
{
Header: 'Report Yr',
accessor: 'reporting_year',
Filter: ({ filter, onChange }) => {
let found = {}
let options = this.props.dashboard
// remove duplicates
.filter(function(row) {
if (!found[row.reporting_year]) {
found[row.reporting_year] = true
return true
}
return false
})
// sort
.sort((a, b) => (a.reporting_year > b.reporting_year ? 1 : b.reporting_year > a.reporting_year ? -1 : 0))
// return option list
.map(function(row) {
return (
<option value={row.reporting_year} key={row.reporting_year}>
{row.reporting_year}
</option>
)
})
return (
<select
onChange={event => {
onChange(event.target.value)
}}
style={{ width: '100%' }}
value={filter ? filter.value : 'all'}>
{options}
</select>
)
},
},
The default filter textbox is replaced with a drop-down with all the values in the data, defaulted to the defaultFiltered
setting (in this case the current year).
Note that if you do not want the All option, this presentation isn't great, as the column can only ever contain a single value so is a waste of horizontal screen space.
To avoid repeating code for each column I created a higher order function. This is pretty handy - just set the Filter
and filterMethod
props on any column to switch the text filter to a drop-down containing all unique values.
class MyCmp extends Component {
...
filterFn = accessor => ({ filter, onChange }) => {
let found = {}
let options = this.props.dashboard
.filter(function(row) {
if (row[accessor] && !found[row[accessor]]) {
found[row[accessor]] = true
return true
}
return false
})
.sort((a, b) => (a[accessor] > b[accessor] ? 1 : b[accessor] > a[accessor] ? -1 : 0))
.map(function(row) {
return (
<option value={row[accessor]} key={row[accessor]}>
{row[accessor]}
</option>
)
})
return (
<select
onChange={event => {
onChange(event.target.value)
}}
style={{ width: '100%' }}
value={filter ? filter.value : 'all'}>
<option value='all' key={'all'}>
All
</option>
{options}
</select>
)
}
filterMethod = (filter, row, column) => {
if (filter.value === 'all') {
return true
}
const id = filter.pivotId || filter.id
return row[id] !== undefined ? String(row[id]) === String(filter.value) : true
}
Then to add a drop-down to a column, just add the Filter
and filterMethod
properties:
columns = [
{
Header: 'CSP Code',
accessor: 'csp_code',
Filter: this.filterFn('csp_code'),
filterMethod: this.filterMethod,
},
{
Header: 'Report Yr',
accessor: 'reporting_year',
Filter: this.filterFn('reporting_year'),
filterMethod: this.filterMethod,
},
...
Note that this ignores empty cells - they only show when All is selected, and can't be filtered on. Also, it doesn't differentiate between '12' and 12, for example. It also ignores custom cells and filters on the underlying value - some of my cells are icons but the drop-downs display and filter on the underlying data values (this is desired behaviour for me, and acts as a kind of key to the icons).
This example has some handy code samples and techniques.
You need to call a function to filter your table's data inside a ponentDidMount()
method.
If you don't know what poenetDidMount()
method is, read this guide about Lifecycle Events in React, this is one of the core concepts to understand in React to achieve some actions based on the document's triggered event.
As said @Elharony, you need to set filtered by query items in ponentDidMount()
method -- i've done code for you here, see ments too, please.
Hope this helps!
Take a look at this example. You'd need to set filtered={filteredData}
on your page load and may need to handle onFilteredChange
as explained in the FAQ
If you intend to mix external sorting/filtering with user driven input from the table, you also need to consider handling onFilteredChange and onSortedChange. Your code will need to be intelligent around which input (the programmatic or user) takes precedence).
Try to render your table after data was loaded. You can add simple loader which is default switch on, load your data and after successful load switch loader off. And render table only when loader is switch off.