I have a table that display employees information. I want to add a search to display all employees between spicfic number that i put in min textbox and max textbox. Here is my table code.
constructor() {
super();
this.state = {
employees: []
};
}
componentDidMount() {
this.employeesTracker = Tracker.autorun(() => {
const employees = Employees.find().fetch();
this.setState({ employees });
});
}
renderEmployeesListItems() {
return this.state.employees.map(employee => {
return (
<tr key={employee._id}>
<td>{employee.name}</td>
<td>{employee.email}</td>
<td>{employee.age}</td>
<td>{employee.gender}</td>
<td>{employee.city}</td>
</tr>
);
});
}
here where I render my app:
render() {
return (
<div>
<input type="text" id="min" name="min"/>
<input type="text" id="max" name="max"/>
<button onClick={this.ageFilter.bind(this)}>filter</button>
<table id="myTable">
<tbody>
{this.renderEmployeesListItems()}
</tbody>
</table>
</div>
);
}
instead of || (OR) you need to use && (AND) operator
...
var age = parseInt(td.innerHTML)
if (age > filterMin && age < filterMax) {
...
} else {
...
}
Also don't why are you accessing data through document.. This is not the way to do document update in react.
You could have this filtered Data separately as a state variable, filteredEmployee or such. Then the filter function will be,
ageFilter = () => {
const { employees, min, max } = this.state;
const filteredEmployees = employees.filter(employee => min < employee.age && employee.age < max);
this.setState({ filteredEmployees })
}
you'll need to add logic to clear the filter too, if you need that. use this filteredEmployees in renderEmployeesListItems function instead of employees
Related
I want to fill the inputs value of a form with default values once the modal is opened
I did it with pure javascript using document.getElementById(textId).value='some value as follow:
for(var i=0; i<details_data.length;i++){
let textId='text'+i;
let amountId='amount'+i;
document.getElementById(textId).value=details_data[i].text
}
This worked fine. but I want to know how to do it with React since I don't believe this is a best practice.
what i tried is to set the input value like this:
<input name='text' id={textId} value={el.text} onChange={details_handler.bind(index)}/>
But this woudn't let me change the value of the input. it just set the default value, and when i type in the input the value woudn't change as I want.
This is my code
const [details_data,set_details_data]=useState([
{'text':'','amount':''}
])
// called this function on `onChange` and store the data in `details_data`
function details_handler(e){
let name=e.target.name;
let value=e.target.value;
details_data[this][name]=value;
set_details_data(details_data);
}
JSX:
(In my case user can add inputs as many as he wants,That's why I put the inputs in a the mapping loop)
{
details_data.map((el,index) => {
let textId='text'+index;
let amountId='amount'+index;
return (
<div key={index}>
<input name='text' id={textId} value={el.text} onChange={details_handler.bind(index)}/>
<input name='amount' id={amountId} onChange={details_handler.bind(index)}/>
</div>
);
})
}
useEffect(() => {
if(detailsProps) {
set_details_data(detailsProps);
}
}, [detailsProps])
where your detailsProps (data from the api) will look something like this
detailsProps = [
{'text':'text1','amount':'100'},
{'text':'text2','amount':'200'}
]
onChange Function
const details_handler = (event, index) => {
const items = [...details_data];
items[index][event.target.name] = event.target.value;
set_details_data(items);
}
your view
{
details_data.map((el,index) => {
return (
<div key={index}>
<input name='text' value={el.text} onChange={(e) => details_handler(e, index)}/>
<input name='amount' value={el.amount} onChange={(e) => details_handler(e, index)}/>
</div>
);
})
}
So I have a function that displays a list from an API:
displayEventTicketDetails() {
this.Service
.getEventTicketDetails().subscribe((data: any) => {
this.eventTicketDetails = data.map(ticket => ticket.ticket_name);
console.log(this.eventTicketDetails);
});
}
This is the result from the function above:
["Regular", "VIP", "Table", "Testing", "Cabana"]
Here is how the form array is declared:
ngOnInit() {
this.composeMessage = this.fb.group({
ticket: new FormArray([])
});
Then I use this function below to track if the check boxes are checked
onChange(event: any, isChecked: boolean){
const control = <FormArray>this.composeMessage.controls.ticket;
if(isChecked){
control.push(new FormControl(event))
} else{
const index = control.controls.findIndex(x => x.value === event);
control.removeAt(index)
}
}
Then finally in my ts file, here is my onsubmit function that submits the data on the form:
submitComposeMessage() {
this.submitted = true;
if (this.composeMessage.invalid) {
return;
}
const ticket = this.f.ticket.value;
this.Service
.createMessage(
ticket)
.subscribe(
(res: any) => {
if (res.err) {
this.toaster.errorToastr(res.message, null, { toastTimeout: 5000 });
return false;
}
this.toaster.successToastr(res.message, null, { toastTimeout: 5000 });
console.log("Message successfully created");
},
err => {
console.log(err);
}
);
}
So in my Html file here is my input field:
<ng-container *ngFor="let event of eventTicketDetails; let i = index" >
<label class="w-checkbox checkbox-field" >
<input
type="checkbox"
id="{{i}}"
name="checkbox-9"
class="w-checkbox-input checkbox"
(change)="onChange(event, $event.target.checked)"
[checked]="composeMessage.controls.ticket.value.indexOf(event)>=0">
<span class="no-margin w-form-label">{{event}}</span>
</label>
</ng-container>
With that loop, I'm able to get this result
So, I need help with two things:
1). I want all the checkbox to be checked by default when the page loads at first instance.
2). I want to validate the checkbox to ensure at least one checkbox is checked on submission.
I'll appreciate any help I can get.
If you want to only show validation message after submit, I would suggest the following, where we instead iterate the formarray in template, initially set all checked (as that is what you wish). We would listen to valueChanges of the formarray, but filter out as long as form is not submitted. We would introduce a new variable, for example isEmpty, which based on we would show/hide validation message. So all in all....
TS:
isEmpty = false;
submitted = false;
constructor(private fb: FormBuilder) {
const ctrls = this.eventTicketDetails.map(control => this.fb.control(true));
this.composeMessage = this.fb.group({
ticket: this.fb.array(ctrls)
});
this.tickets.valueChanges.pipe(
filter(() => !!this.submitted)
).subscribe((value) => {
value.some(x => x === true) ? this.isEmpty = false : this.isEmpty = true;
})
}
get tickets() {
return this.composeMessage.get("ticket") as FormArray;
}
onSubmit() {
this.submitted = true;
const selectedTickets = this.tickets.value
.map((checked, i) => (checked ? this.eventTicketDetails[i] : null))
.filter(value => !!value);
selectedTickets.length ? this.isEmpty = false : this.isEmpty = true
}
HTML:
<label formArrayName="ticket" *ngFor="let t of tickets.controls; index as i">
<input type="checkbox" [formControlName]="i">
{{eventTicketDetails[i]}}
</label>
<small *ngIf="isEmpty">Choose at least one checkbox</small>
STACKBLITZ
change Id to something like this
id="ticket{{i}}"
In this method write like this and call displayEventTicketDetails on ngOnit. This will check all the values:
displayEventTicketDetails() {
this.Service
.getEventTicketDetails().subscribe((data: any) => {
this.eventTicketDetails = data.map(ticket =>ticket.ticket_name);
setTimeout(() => {
for(var i= 0;i < this.evenTicketDetails.length ; i++){
var id = "ticket" + i;
(<HTMLInputElement>document.getElementById(id)).checked = true;
console.log(this.eventTicketDetails);
}, 500);
});
}
2. In submit method write something like this
submitComposeMessage() {
for(var i= 0;i < this.evenTicketDetails.length ; i++){
var id = "ticket" + i;
var resval = (<HTMLInputElement>document.getElementById(id)).value;
if(resval){
// this will check atleast one value is checked and if it true we are coming
out of the loop and performing other operations..
i = this.evenTicketDetails.length;
}
else{
// show error message or block from going forward..
}
});
}
This will solve your issues.
I created a table that contain employees information. I want to filter the table by gender and age. I create the filter for the gender, but the filter for the age. I had hardtime to find a solution for it. For example the filter i want to do is to find employees between two number, like all employees between age 23 and 30. here is my code.
export default class EmployeeList extends React.Component {
constructor() {
super();
this.state = {
employees: []
};
}
componentDidMount() {
this.employeesTracker = Tracker.autorun(() => {
// Meteor.subscribe('employees');
const employees = Employees.find().fetch();
this.setState({ employees });
});
}
componentWillUnmount() {
this.employeesTracker.stop();
}
renderEmployeesListItems() {
return this.state.employees.map(employee => {
return (
<tr key={employee._id}>
<td>{employee.name}</td>
<td>{employee.email}</td>
<td>{employee.age}</td>
<td>{employee.gender}</td>
<td>{employee.city}</td>
<td><Link className="link button" to={`/employee-detail/${employee._id}`}>EDIT</Link></td>
<td><button className="button pointer" onClick={() => Employees.remove({_id: employee._id})}>DELETE</button></td>
</tr>
);
});
}
// --------------------------
//Gender Filter
myFunction() {
var input, filter, table, tr, td, i;
input = document.getElementById("genName").value;
filter = input;
table = document.getElementById("myTable");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[3];
if (td) {
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
render() {
return (
<div>
<select id="genName" onChange={this.myFunction.bind(this)}>
<option value="">All Genders</option>
<option value="M">Male</option>
<option value="F">Female</option>
</select>
<p>Minimum age:</p>
<input type="text" id="min" name="min"/>
<p>Maximum age:</p>
<input type="text" id="max" name="max"/>
<table id="myTable" className="employeeTable">
<tbody>
<tr>
<th>NAME</th>
<th>EMAIL</th>
<th>AGE</th>
<th>GENDER</th>
<th>CITY</th>
<th></th>
<th></th>
</tr>
{this.renderEmployeesListItems()}
</tbody>
</table>
</div>
);
}
}
To do this kind of filtering when user inputs data, you should use React State, and the best way to integrate it today is using hooks.
In the state, you will not store employees as you did (which is only the result of a minimongo query to fetch the whole database), but employeesToDisplay. This will avoid the Html parsing in myFunction.
After some linting using state, your code should look like something like this :
//Gender Filter
myFunction() {
var filter = document.getElementById("genName").value;
// best is to get the input from context (look into `this` with console.log here)
// assuming that filter is exactly what you are looking for in your database :
this.setState({
employeesToDisplay: Employees.find({gender: filter}).fetch()
})
}
Since state is reactive, your list will be updated as the gender is selected, with a rendering of the database elements that correspond to the query.
To do a query on the age of employees, refer to the mongoDB documentation on queries to build a request like :
Employees.find({age: {$gte: minAge, $lte: maxAge}}).fetch() that you will use to store the data to display in the state
I have a simple countdown component where a user inputs two times and it counts down the seconds between them. The start, stop, and reset work. Except, when I reset the countdown and input two new times (without refreshing the page), I am hit with this error:
TypeError: _this3.start is not a function
> 108 | <button onClick={(e) => this.start()}>Start</button>
Below is my code:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(){
super();
this.start = this.start.bind(this);
this.toTimestamp = this.toTimestamp.bind(this);
this.getDifference = this.getDifference.bind(this);
this.state = {
input1: '',
input2: '',
countdown: null
}
}
input1ContentChange(e){
const text = e.target.value;
this.setState(()=>{
return {input1: text};
})
}
input2ContentChange(e){
const text = e.target.value;
this.setState(()=>{
return {input2: text};
})
}
toTimestamp(input){
let time = input.split(':');
let seconds = ((+time[0]) * 60 * 60) + ((+time[1]) * 60) + (+time[2]);
return seconds;
}
getDifference(input1, input2){
let difference = (this.toTimestamp(input2))- (this.toTimestamp(input1));
if(this.toTimestamp(input2) < this.toTimestamp(input1)){
alert("please input a later time in Time 2");
}
this.setState({
countdown: difference
})
}
start() {
if(this.state.input1 === '' && this.state.input2 === ''){
alert('please choose 2 times');
}
this.getDifference(this.state.input1, this.state.input2);
this.start = setInterval((e) => {
this.setState((prevState) => {
return {countdown: prevState.countdown - 1};
});
if(this.state.countdown <= 0){
clearInterval(this.start);
}
}, 1000);
}
stop(){
clearInterval(this.start);
}
reset(){
clearInterval(this.start);
this.setState((prevState) => {
return {countdown: null, input1: '', input2:''}
})
}
render() {
return (
<div className="App">
<h1>Countdown Timer</h1>
<p>Please choose two different times below</p>
<div className="input1">
<label>
Time 1:
<input type="time"
step="1"
min= "12:00"
max= "18:00"
value={this.state.input1}
onChange={(e)=> this.input1ContentChange(e)}/>
</label>
</div>
<div className="input2">
<label>
Time 2:
<input type="time"
step="1"
min="12:00"
max="18:00"
value={this.state.input2}
onChange={(e)=> this.input2ContentChange(e)}/>
</label>
</div>
<button onClick={(e) => this.start()}>Start</button>
<button onClick={(e) => this.stop()}>Stop</button>
<button onClick={(e) => this.reset()}>Reset</button>
<h3>{this.state.countdown}</h3>
</div>
);
}
}
export default App;
The error is happening at the start function for restarting the countdown. When I check in my chrome tools using the React extension, the state is managed fine. It seems "this" is getting lost.
You're modifying your class function. When your app loads, your class has a start method, but inside that method you do:
this.start = setInterval(...)
setInterval does not return a function, but an id that you can use later to clear the interval. Even if it did return a function, you probably don't want to be modifying your class methods at runtime.
I would suggest using another variable name:
this.intervalId = setInterval(...)
I have few checkbox, what I want is to build a unique array list base on the checkbox value, if the checkbox is checked, push it to the list, if the checked checkbox is already in the list, remove it from the list.
http://jsbin.com/bojinudelo/edit?js,console,output
What's the problem of my code below?
generateFilter = (e) => {
let temp = [];
temp = this.state.arr;
if(temp.indexOf(this.state.arr) > -1){
delete temp[e.target.name]
}else{
temp.push(e.target.name);
}
this.setState({arr:temp})
console.log(this.state.arr)
}
render() {
return (
<div>
<input onChange={this.generateFilter} type="checkbox" name="apple"/>
<input onChange={this.generateFilter} type="checkbox" name="samsung"/>
</div>
);
}
Reason is, you are checking the index of wrong item, Instead of checking the index of this.state.arr, check the index of e.target.value, as well as deleting in a wrong way.
temp is an array not an Object, so instead of using delete, you need to use the splice and pass the index of item.
Use this function:
generateFilter = (e) => {
let temp = this.state.arr.slice(),
index = temp.indexOf(e.target.name);
if(index != -1){
temp.splice(index, 1);
}else{
temp.push(e.target.name);
}
this.setState({arr:temp})
console.log(temp)
}
Check the working code: http://jsbin.com/tatakamobu/1/edit?js,console,output
Run this snippet:
class HelloWorldComponent extends React.Component {
constructor(props){
super(props);
this.state = {
arr: []
}
}
generateFilter = (e) => {
let temp = this.state.arr.slice(),
index = temp.indexOf(e.target.name);
if(index != -1){
temp.splice(index, 1);
}else{
temp.push(e.target.name);
}
this.setState({arr:temp})
console.log(temp)
}
render() {
return (
<div>
<input onChange={this.generateFilter} type="checkbox" name="apple"/>
<input onChange={this.generateFilter} type="checkbox" name="samsung"/>
</div>
);
}
}
ReactDOM.render(
<HelloWorldComponent />,
document.getElementById('react_example')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='react_example'/>