How can I integrate searching functionality on table? - javascript

At the moment, all the available flights that was received from API are successfully loaded on the page. However, I would like to enable the end user to search specific flight, let's say, by flight number and departure date. How can I integrate this searching functionality in the existing codes?
FlightPage.js
render() {
return (
<>
<h2>Flights</h2>
{this.props.loading ? (
<div>Loading...</div>
) : (
<FlightList flights={this.props.flights} />
)}
</>
);
}
}
As you can see the bellow code, I have used table to present the results.I would like to show only one result or blank table when searching is applied. Can you help me to achieve this?
FlightList.js
const FlightList = ({ flights }) => (
<table className="table">
<thead>
<tr>
<th />
<th>Date</th>
<th>Provider</th>
<th>Dest</th>
</tr>
</thead>
<tbody>
{flights.map((f, i) => {
return (
<tr key={i}>
<td>
<input type="checkbox" name="flightListCheckbox" />
</td>
<td>{f.date}</td>
<td>{f.pnr}</td>
<td>{f.flightNumber}</td>
</tr>
);
})}
</tbody>
</table>
);

You could use filter to create a searching functionality like
I would at first add an input where I can insert my filter values
FlightPage.js
handleInput: (event) => {
const { name, value } = event.target
this.setState({ [name]: value })
}
render () {
const { filter } = this.state
return (
<>
<input onChange=(this.handleInput) value={filter} name='filter' />
<FlightList flights={this.props.flights} filterValues={filter} />
</>
)
}
Then I would use my state to filter my Object like
FlightList.js
const FlightList = ({ flights, filterValue }) => {
const filterdFlights = flights.filter(flight => Object.values(flight).includes(filterValue))
return (
<table className="table">
<thead>
<tr>
<th />
<th>Date</th>
<th>Provider</th>
<th>Dest</th>
</tr>
</thead>
<tbody>
{filterdFlights.map((f, i) => {
return (
<tr key={i}>
<td>
<input type="checkbox" name="flightListCheckbox" />
</td>
<td>{f.date}</td>
<td>{f.pnr}</td>
<td>{f.flightNumber}</td>
</tr>
);
})}
</tbody>
</table>
)};

You need an input for search and filter flights by value of input. Try this
class FlightPage extends React.Component {
state = {
keyword: '',
}
...
getFlights = () => {
const { keyword } = this.state
const { flights } = this.props
return flights.filter(flight => flight.name.includes(keyword)) // name or something else
}
onInputChange = e => {
this.setState({ keyword: e.target.value })
}
render () {
return (
<>
<input onChange=(this.onInputChange) value={this.state.keyword} />
<FlightList flights={this.getFlights()} />
</>
)
}
}

You can filter your flights array using flights.filter or sort it using flights.sort.

You could try to use jquery datatable. It adds a lot of funcionality to tables easy to implement.
DataTable doc

Related

useEffect resets the numStudents state after check out

So I created a app takes attendance of students. but when i try to check out useEffect resets the state of numStudents after update.
the code is :
const Attendance = () => {
// Declare state variables
const [students, setStudents] = useState([]);
const [numStudents, setNumStudents] = useState(0);
// Function to add a student to the attendance list
const addStudent = (rollNumber, name) => {
setStudents(
students.concat({
rollNumber,
name,
checkInTime: new Date().toLocaleString(),
})
);
};
// Function to check a student out
const checkOut = (rollNumber) => {
setStudents(
students.map((student) => {
if (student.rollNumber === rollNumber) {
return { ...student, checkOutTime: new Date().toLocaleString() };
}
return student;
})
);
setNumStudents(students.length-1);
};
// Use effect hook to update the number of students when the 'students' state variable changes
useEffect(() => {
setNumStudents(students.length);
}, [students]);
return (
<div>
{/* Form to input student roll number and name */}
<form
onSubmit={(e) => {
e.preventDefault();
addStudent(e.target.rollNumber.value, e.target.name.value);
e.target.rollNumber.value = "";
e.target.name.value = "";
}}
>
<div className="container">
<h1>Student Attendance</h1>
<label>
Roll Number
<input type="number" name="rollNumber" />
</label>
<br />
<label>
Name
<input type="text" name="name" />
</label>
<br />
<button type="submit">Check In</button>
</div>
</form>
{/* Display number of students present */}
<p>There are currently {numStudents} students in the school.</p>
{/* Table to display list of students and their check in/out times */}
<br />
<table className="fl-table">
<thead>
<tr>
<th>Roll Number</th>
<th>Name</th>
<th>Check In Time</th>
<th>Check Out Time</th>
</tr>
</thead>
<tbody>
{students.map((student) => (
<tr key={student.rollNumber}>
<td>{student.rollNumber}</td>
<td>{student.name}</td>
<td>{student.checkInTime.toString()}</td>
<td>
{student.checkOutTime ? (student.checkOutTime.toString()) : (
<button
key={student.rollNumber} onClick={() => checkOut(student.rollNumber)}>
Check Out
</button>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default Attendance;
I tried using setNumStudents(students.length-1) in checkOut function but it updates and then useEffect is called that resets it again . How do i make it not reset ? i am new to react Hooks so plz help!!!
Why do you need an effect here at all? I mean you already updating numStudents in checkout handler.
Ideally useEffect should be used to hook up your app to external library or make a http request(like fetching students from server)
When you checkout student you responding to event fired by user, and should update state variables in event handler.
// Function to add a student to the attendance list
const addStudent = (rollNumber, name) => {
setStudents(
students.concat({
rollNumber,
name,
checkInTime: new Date().toLocaleString(),
})
);
setNumStudents(numStudents + 1)
};
// Function to check a student out
const checkOut = (rollNumber) => {
setStudents(
students.map((student) => {
if (student.rollNumber === rollNumber) {
return { ...student, checkOutTime: new Date().toLocaleString() };
}
return student;
})
);
setNumStudents(numStudents - 1);
};

Prevent React table from scrolling to top on update

I have a react table that contains information in my Electron application. However, whenever data in the table is updated or a button is clicked, the table scrolls to the top, frustrating users.
Example code is as follows:
const tableContent = listItem.map((item: any, index: number) => {
return (
<Tr key={index.toString()} className="section">
<Td>{item.<item1>}</Td>
<Td>
<Icon
onClick={() => exampleFunction()}
/>
</Td>
</Tr>
);
});
return (
<Div className="round-card page-content-table table-responsive padding-20">
{<Table className="table table-striped">
<Thead>
<Tr>
<Th>TH1</Th>...
<Th>TH2</Th>
</Tr>
</Thead>
{<Tbody>{tableContent}</Tbody>}
</Table>}
</Div>)
How can I avoid these scroll jumps in the element during updates?
Update:
I was able to get the scroll position to save however, when the table updates, the scroll is stuck to the previous point, making it impossible for users to scroll when the table is updating. Any way to avoid this?
const [scrollPostion, setScrollPosition] = useState(
parseInt(localStorage.getItem('scrollPos')) || 0
);
const TableRef = useRef(null);
const scrollEvent = (e) => {
setScrollPosition(e.target.scrollTop);
localStorage.setItem('scrollPos', scrollPostion.toString());
};
React.useEffect(() => {
localStorage.setItem('scrollPos', scrollPostion.toString());
}, [scrollPostion]);
For anyone who runs into this issue in the future, I solved by moving the table into new component and putting it in the div
const myTable = () => {
const tableContent = listItem.map((item: any, index: number) => {
return (
<Tr key={index.toString()} className="section">
<Td>{item.<item1>}</Td>
<Td>
<Icon
onClick={() => exampleFunction()}
/>
</Td>
</Tr>
);
};
return (
<Table className="table table-striped">
<Thead>
<Tr>
<Th>TH1</Th>...
<Th>TH2</Th>
</Tr>
</Thead>
{<Tbody>{tableContent}</Tbody>}
</Table>}
)
}
const pageContent = () = {
return (
<Div className="round-card page-content-table table-responsive padding-20">
<myTable></myTable>
</Div>)
)
}

How to deal with async function in react?

I have a body object with different types of elements: (strings, number, objects...).
I need to show the body in a table.
In order to do it, I need to print in one table the elements that aren't objects, and in another table the elements that are objects.
So I am calling the function to create an array with object elements (arrObj) and another array with the non object elements (arrSimple).
The problem is that when I go through the arrSimple array to print the elements in a table, this array is empty.
Could anyone guide me on how can I resolve this async problem?
const DetailResult = props => {
...
const arrSimple = []
const arrObj = []
function organizeArray() {
for (const prop in body) {
if (typeof (body[prop]) != 'object') {
arrSimple[prop] = (body[prop])
} else if (typeof (body[prop]) == 'object') {
arrObj[prop] = (body[prop])
}
}
}
function renderGeneralData() {
organizeArray()
arrSimple.map((key, i) => {
<tr key={i}>
<td width="25%">{key}</td>
<td>{(arrSimple[key])}</td>
</tr>
})
}
return (
<div>
<table className='table table-striped'>
<tbody>
<tr>
<th>General Data</th>
</tr>
<tr>
{renderGeneralData()}
</tr>
</tbody>
</table>
</div>
)
}
export default DetailResult;
The body object comes from the app component.
class App extends Component {
constructor() {
super()
this.state = {
dataTable: {
transactionID: '',
maxRows: 10,
currentPage: 0,
list: {
headerList: [],
body: []
}
}
}
this.search = this.search.bind(this)
}
search() {
axios.get(URL)
.then(resp => this.setState({
dataTable: Object.assign(this.state.dataTable, {
list: [
{headerList: ['App name', 'Date', 'Bio data', 'Is verified', 'Actions']},
{body: resp.data},
],
}),
}))
.catch(function (error) {
console.log(error);
})
}
I have a component that contains a search field to make a request
const SearchComponent = props => {
const renderDetailResult =
<DetailResult list={props.dtObject.list}
search={props.search}
/>
return (
<div role='form' className='searchID'>
<ContentHeader title={props.componentHeaderTitle} />
<Grid cols='12 9 10'>
<input id="cpf" className='w-25 form-control'
placeholder='Type the ID'
/>
</Grid>
<Grid cols='12 3 2'>
<IconButton style='primary' icon='search'
onClick={props.search}>
</IconButton>
</Grid>
<Grid cols='12'>
{renderDetailResult}
</Grid>
</div>
)
}
export default SearchComponent
The reason why nothing appears is that you are calling a function that returns nothing, so there isn't anything to render.
You need to return the .map and return the elements you want.
function renderGeneralData() {
organizeArray()
// added return
return arrSimple.map((key, i) => (
<tr key={i}>
<td width="25%">{key}</td>
<td>{(arrSimple[key])}</td>
</tr>
))
}
Observation
You are rendering <tr> inside <tr>. I recommend removing the
return arrSimple.map((key, i) => (
//returning tr
<tr key={i}>
<td width="25%">{key}</td>
<td>{(arrSimple[key])}</td>
</tr>
))
<tr>
// returned tr inside another tr
{renderGeneralData()}
</tr>
I'm not sure how you want to display your data, but I recommend removing one of the tr tag.
Quick tip
If you want to remove the tr that is inside .map you should use React.Fragment
return arrSimple.map((key, i) => (
<React.Fragment key={i}>
<td width="25%">{key}</td>
<td>{(arrSimple[key])}</td>
</React.Fragment>
))
Edit:
I also noticed something weird in your code in this part
arrSimple.map((key, i) => (
<tr key={i}>
<td width="25%">{key}</td>
<td>{(arrSimple[key])}</td>
</tr>
))
In this part of the code, key will be an element of arrSimple. If you do arrSimple[key] it will probably return undefined. Here is an example
arr = ['hey', 'this', 'is', 'bad', '!']
console.log(arr.map((key, i) => arr[key]))

multiple sort table using reactjs

Any clue why this code won't be able to do sorting properly base on columns?
sort(key){
this.setState({
[`toggle-${key}`]: !this.state[`toggle-${key}`],
data: sortBy(this.state.data, [key], this.state[`toggle-${key}`]).map(v => v)
})
}
render() {
return (
<div style={styles}>
<table>
<thead>
{Object.keys(this.state.data[0]).map(v => {
return(
<th onClick={()=>this.sort(v)}>
{v.toUpperCase()}
</th>
)
})}
</thead>
<tbody>
{this.state.data.map(v=>{
return(
<tr>
<td>{v.id}</td>
<td>{v.name}</td>
</tr>
)
})}
</tbody>
</table>
</div>
);
}
The toggling of the state seems to be correct but the reflection is only happening for the first time.
https://codesandbox.io/s/zqno7m7j4p
Lodash's _.sortBy() doesn't have the ability to select descending or ascending. Use _.orderBy() instead (sandbox):
sort(key) {
const columnState = !this.state[`toggle-${key}`];
this.setState({
[`toggle-${key}`]: columnState,
data: orderBy(
this.state.data,
[key],
columnState ? 'desc' : 'asc'
)
});
}

Trying to get the row information on clicking a row on table in reactJS

I am building a simple app in ReactJS that works with a JSON array by calling a certain API. I am then populating the results of the array in a table. What I now want is to click on any row in the table and get those values to pass into some other component. I am wondering how to get the row information using onClick.
Here is my code.
class ParentComponent extends Component {
constructor(props){
super(props);
this.state = {data: []};
}
componentDidMount() {
fetch('http://hostname:xxxx/yyyy/zzzz')
.then(function(response) {
return response.json();
})
.then(items=>this.setState({data: items}));
}
fetchAccountDetails () {
}
render(){
var newdata = this.state.data;
return (
<table className="m-table">
<thead>
<tr>
<th>AccountName</th>
<th>ContractValue</th>
</tr>
</thead>
<tbody>
{
newdata.map(function(account, index){
return (
<tr key={index} data-item={account} onClick={this.fetchAccountDetails()}>
<td data-title="Account">{account.accountname}</td>
<td data-title="Value">{account.negotiatedcontractvalue}</td>
</tr>
)
}
)
}
</tbody>
</table>
);
}
}
export default ParentComponent;
Pass the index of the state element and retrieve from the state array. Also it is not required to copy state to another variable before mapping, you can do it with state itself
render(){
return (
<table className="m-table">
<thead>
<tr>
<th>AccountName</th>
<th>ContractValue</th>
</tr>
</thead>
<tbody>
{
this.state.data.map((account, index) => {
return (
<tr key={index} data-item={account} onClick={() => this.fetchAccountDetails(index)}>
<td data-title="Account">{account.accountname}</td>
<td data-title="Value">{account.negotiatedcontractvalue}</td>
</tr>
)
}
)
}
</tbody>
</table>
);
}
}
fetchAccountDetails(index) {
var values = this.state.data[index];
console.log(values);
}
fetch the value using
fetchAccountDetails (event) {
Account:event.target.value
this.getData(Account);
}
getData: function(var account)
this.setState({
state: account
});
Now you have your data set as state and you can use it anywhere you want yo

Categories

Resources