React grid table with double sorting in specific columns - reactjs

I have difficulties for completing an emerging task. I must implement a table in which some columns must have two fields. These columns must have a sorting functionality through which the user will be able to sort the selected fields.
To be more accurate, see the screenshot below:
You can see the columns which contains two fields. When, the user clicks in a field, arrows must be shown and the grid will be sorted by the selected value. When the user clicks the other fields (Price for example), arrows are shown, the field becomes bold and the grid is sorted by the selected field.
I use Griddle but, how can I implement in Griddle such a functionality? Should I make my own grid from scratch?

This will sort any column if you have stored your data in the state and the data is either a string, number or boolean.
function sortFunc(a, b, key) {
if (typeof(a[key]) === 'number') {
return a[key] - b[key];
} else if (typeof(a[key]) === 'boolean') {
return a[key] ? 0 : 1;
}
const ax = [];
const bx = [];
a[key].replace(/(\d+)|(\D+)/g, (_, $1, $2) => { ax.push([$1 || Infinity, $2 || '']); });
b[key].replace(/(\d+)|(\D+)/g, (_, $1, $2) => { bx.push([$1 || Infinity, $2 || '']); });
while (ax.length && bx.length) {
const an = ax.shift();
const bn = bx.shift();
const nn = (an[0] - bn[0]) || an[1].localeCompare(bn[1]);
if (nn) return nn;
}
return ax.length - bx.length;
}
function sortByColumn(column, data) {
const isAsc = this.state.sortHeader === column ? !this.state.isAsc : true;
const sortedData = data.sort((a, b) => sortFunc(a, b, column));
if (!isAsc) {
sortedData.reverse();
}
this.setState({
data: sortedData,
sortHeader: column,
isAsc
});
}
<IconButton onClick={() => this.sortByColumn(column.key, this.state.data)} style={styles.sortButton} >
<SortIcon />
</IconButton>

Related

Limit user input in a text field using Kendo UI grid and React

So I have a datagrid, with editable rows where the user can edit and add rows.
I want the user to have a limit on each different row cell field. For example, smallNumber will have 3 and description will have 15. I am using Kendo UI and React v18.2.0
<Grid
editField={editField}
onItemChange={itemChange}
data={dataField}
key="keys"
>
<Column field="smallNumber " title="Small NR" />
<Column field="stationDescription" title="DESCRIPTION" />
</Grid>
And itemChange func is like:
const itemChange = (event) => {
const newData = dataField.map((item) =>
item.dataFieldID === event.dataItem.dataFieldID
? { ...item, [event.field || ""]: event.value, changed: true }
: item
);
setDataField(newData);
};
If I add a maxLength={3} to the column for ex, it will give me a 500 server error. I want to use the onItemChange event, and check the value lenght for that field. If it is over 5, then the values in the state wont be updated, but i dont know how to do it. And also how to do it for different fields such as SmallNumber which needs less than 3 and Description that needs less than 15.
Found the solution:
Initialized the max length for each column
const itemsLength = {
stationDescription: 225,
stationNumber: 3
}
And then changed the newData funx like this
const newData = stations.map((item) =>
item.stationID === event.dataItem.stationID
? { ...item, [event.field || ""]: event.value.length <= itemsLength[event.field] ? event.value : item[event.field], changed: true }
: item
);

sorting tasks in three column

I have a task manager in home component I'm getting all task and I put tasks in column component
const { tasks} = useTypedSelector((state) => state.task);
const new_tasks = tasks.filter((task) => task.position == 'new');
const progress_tasks = tasks.filter((task) => task.position == 'progress');
const done_tasks = tasks.filter((task) => task.position == 'done');
<div className="columns">
<Column position="new" inner_tasks={new_tasks} />
<Column position="progress" inner_tasks={progress_tasks} />
<Column position="done" inner_tasks={done_tasks} />
</div>
then in column component I'm gettin in props inner_tasks
Column = ({ position, inner_tasks })
and render tasks depends of position
{inner_tasks &&
inner_tasks.map((item) =>
item && item.position == position ? (
in total I got 3 arrays, but only one component - column.
I want to add sorting and I want to if I selected sorting in first column tasks in first column will be sorted, but in 2 and 3 column not.
I writed sorting func
const hangleChange = (sortingType, position_name) => {
debugger;
inner_tasks.filter((item) => item.position == position_name);
sorted = inner_tasks.sort((a, b) =>
a.priorityNum > b.priorityNum ? 1 : -1
);
};
sortingType will be 'by date' or 'by priority'
position_name it's just name of column for example 'new tasks' or 'done'
ok, now I have sorted array with name sorted, but how can I replace and rerander my tasks in one column and don't change my other columns. I don't know i to change this part of code
{inner_tasks &&
inner_tasks.map((item) =>
item && item.position == position ? (
I think problem is here. Help me guys!

Filter table with select options (check if column is number or null) in React

I am new at React and I am stuck.
I have a table with data from database. I want to filter that table with a select dropdown. I have mulitple "select-fields" that works fine, because the value in the dropdown matches the exact value in database. But now I just want to check if the column value is null or a number.
In my select options I just want three options (see left picture):
All (Show all result. It is working)
Missing number(is null in Database. Not working)
Has number(Not working)
So the value in the table column (see right picture) I want to filter is either a number or null.
Here is my code so far:
const [filteredData, setFilteredData] = useState([]);
//Column in table:
{
Header: () => (<div>TableHead</div>),
accessor: "accessorToDatabase",
Cell: (props) => { return <div>{props?.cell?.value}</div> }
}
// The select dropdown and the table
<Col>
<Label>Select Dropbox</Label>
<Input type="select" onChange={handleChange('id', 'description')}>
<option>All</option>
<option value="false">Missing number</option>
<option value="true">Has number</option>
</Input>
</Col>
<Table columns={columns} data={filteredData} HandleRowData={HandleRowData} />
//The filter functions
const handleChange = name => ({ target: { value } }) => {
filter[name] = (value === 'All') ? null : value
if (checkProperties(filter)) {
var filtered = state
}
else {
var filtered = handleFilter(state, filter)
}
setFilteredData(filtered)
}
const handleFilter = (arr: Object[], filters: Object) => {
const filterKeys = Object.keys(filters)
return arr.filter(eachObj => {
return filterKeys.every(eachKey => {
if (!filters[eachKey] || !filters[eachKey].length) {
return true
}
})
})
}
I have tried with something like this, for looping through all values in the column, but without no success:
state.map(x=>((
x.id> 0 ? x.id : null)))
.map is used to transform one array of values/objects to another array with the same length but with transformed values/objects. What you want to use is .filter, which removes elements from an array but maintain the element structure.
To keep the ones that does not have an id:
array.filter(x => !x.id)
To keep the ones that has an id that is a number:
array.filter(x => !isNaN(x.id))
A simple Array.prototype.filter should do the trick:
//removes all entries with an id of `0 || null || undefined`
state.filter(entry=>!!entry.id)
//removes all entries with an id of `null || undefined` but keeps the 0
state.filter(entry=>!!entry.id || entry.id === 0)
I would not recommend using isNan: as it internally tries to parse strings to check if they are numbers you might end up with some unexpected behaviors. It also goes wild with booleans, null and undefined.
See this link for more info:
https://www.w3schools.com/jsref/jsref_isnan.asp
EDIT
Rereading your question it looks like you want
all items
items with an ID of type number
items that are not numbers
Here is how you could implement that
const [items,setItems]= useState(someItems)
const [filter, setFilter]=useState('all')
function handleChange(e){
setFilter(e.target.value)
}
const filteredItems = items.filter(item=>{
if(filter === 'number')
return typeof items === 'number'
if(filter === 'no-number')
return typeof item !== 'number'
return true
})
return (
<React.Fragment>
<select onChange={handleChange}>
<option value='all'>All</option>
<option value='no-number'>Missing number</option>
<option value="number">Has number</option>
</select>
<Table columns={columns} data={filteredData} HandleRowData={HandleRowData} />
</React.Fragment>
)
Change the elements with the specific library you are using and you are good to go
I would recommend not using NaN, as it tries to parse strings. Instead, you can create an array and use the map function, in combination with the filter function.

Primereact datatable: filter null values or template values

I have a datatable in primereact with a list of customers which has a column validTo which returns a date or null. I want to filter all valid customers, so I would filter for equals null, but that doesn't work because null resets the filter.
Second Option would be to replace null with something like "-" in a template, but how do I filter the value returned by the template, as it seems, that datatable only filters the source data?
Update 1:
I got a bit further.
my column looks like this
<Column
field="giltbis"
header="giltbis"
filter={true}
filterElement={giltbisFilterElement}
filterMatchMode="custom"
filterFunction={filterGiltbis}
sortable={true}
body={dateTemplate_giltbis}
/>
And here is my filter setup:
const handleFilterClick = (value) => {
setgiltbisSelected(value);
dt.current.filter(value, "giltbis", "custom");
};
const filterGiltbis = (value) => {
if (giltbisSelected === "Gültig") {
return value == null;
} else if (giltbisSelected === "Ungültig") {
return value != null;
} else {
//how to cancel filter or show all values
}
};
const giltbisFilterElement = (
<SelectButton
style={{ width: "100%" }}
value={giltbisSelected}
options={giltbisoptions}
onChange={(e) => handleFilterClick(e.value)}
/>
);
So only one problem left. How to I cancel the filtering or show all values?
You need to implement a custom filter function. Here is an example
filterMatchMode="custom" filterFunction={customFunction}
export const customFunction = (value, filter) => {
return value.toUpperCase().indexOf(filter.toUpperCase()) >= 0
}

AngularJS/Bootstrap - DataTables - Turn off certain dropdown filters in footer

Is it possible to turn off certain dropdown filters in the footer? This is the API I'm using: https://datatables.net/examples/api/multi_filter_select.html
I don't want all columns to be filterable. Also, is it possible to have the header labels be the default in the dropdown instead of a blank?
Here is my live example: http://live.datatables.net/cufawibi/3/
A usual approach is to use a css class to filter which columns can be filterable.
You could also add the column name as selected and disabled in order to display it as the default (included an all values options to disable the filter).
initComplete: function () {
var api = this.api();
api.columns('.filtersearch').indexes().flatten().each( function ( i ) {
var column = api.column( i );
var select = $('<select></select>')
.appendTo( $(column.footer()).empty() )
.on( 'change', function () {
var val = $.fn.dataTable.util.escapeRegex(
$(this).val()
);
column
.search( val ? '^'+val+'$' : '', true, false )
.draw();
} );
select.append('<option selected disabled>"'+$(column.header()).text()+'"</option>');
select.append('<option value="">All values</option>');
column.data().unique().sort().each( function ( d, j ) {
select.append( '<option value="'+d+'">'+d+'</option>' );
} );
} );
}
UPDATE:
In order to have the class added from the controller, changed also the table head definition to
<th ng-repeat="(i, th) in head" value="{{th.id}}" class="{{th.class}}"><span>{{th.name}}</span></th>
Live example (filter only for the "Payload" column, add filtersearch class to other columns to enable filtering)
I don't think there's a clean way to do this via the API. So this solution will be hacky.
So you'd wrap your logic around an IF block to filter out the columns dropdown filters you don't want to display.
api.columns().indexes().flatten().each(function (index) {
var column, select;
// You don't want to display the first and the fourth dropdown filter
if (index !== 0 || index !== 3) {
column = api.column(i);
select = $('<select><option value=""></option></select>')
.appendTo($(column.footer()).empty())
.on('change', function () {
var val = $.fn.dataTable.util.escapeRegex($(this).val());
column.search(val ? '^' + val + '$' : '', true, false).draw();
});
column.data().unique().sort().each(function (d, j) {
select.append('<option value="' + d + '">' + d + '</option>')
});
}
});

Resources