generate unique array base on checkbox value - javascript

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'/>

Related

Cannot read the array property. Objects are not valid as a React child

I am following a tutorial exercise and I got the following error
Objects are not valid as a React child
I know this error is related to the object as I am trying to access the object but it needs an individual item of an object but not sure.
Why cannot the map loop over each item in the array?
Following is my code
var template = <h1>Indecision App</h1>;
var app = {
title: 'Indecision App',
subtitle: 'yo',
options: []
}
let count = 0;
function checkSubtitles (subtitle){
if(subtitle){
return <p>{subtitle}</p>
}else{
return undefined
}
}
function reset(){
count = 0;
reRenderApp();
}
function increaseCount(){
count++;
reRenderApp();
}
function onSubmitHandle(e){
e.preventDefault();
const options = e.target.elements.options;
app.options.push(options);
reRenderApp();
e.target.elements.options.value = ''
}
function removeAll(){
app.options = [];
reRenderApp();
}
function reRenderApp(){
var templateTwo = (
<div>
<h1>{app.title}</h1>
{checkSubtitles(app.subtitle)}
<p>Count: {count}</p>
<p>Array Length: {app.options.length > 0 ? app.options.length : '0 Items'}</p>
<ol>
{app.options.map((item)=>{
return <li key={item}>{item}</li>
})}
</ol>
<hr></hr>
<form onSubmit={onSubmitHandle}>
<input type="text" name="options" />
<input type="submit" value="Push to the Array" />
<input type="reset" value="Empty my list" onClick={removeAll} />
</form>
<button onClick={()=>{
increaseCount();
}}>Increase Count</button>
<button onClick={()=>{
reset();
}}>Reset Count</button>
</div>
)
ReactDOM.render(templateTwo, appRoot)
}
var appRoot = document.getElementById('app');
reRenderApp();
<body>
<div id="app"></div>
<script src="https://unpkg.com/react#16.0.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.0.0/umd/react-dom.development.js"></script>
<script src="./app.js"></script>
</body>
</html>
The main problem is, as you mentioned: Objects are not valid as a React child
But, what is happening?
If we go into:
function onSubmitHandle(e){
e.preventDefault();
// Line 1
const options = e.target.elements.options;
app.options.push(options);
reRenderApp();
// Line 2
e.target.elements.options.value = ''
}
So in Line 1, you're pushing options into the options array.
But, then in Line 2, we can notice options has an attribute (so, it's an object)
So, if you change Line 1, from:
const options = e.target.elements.options;
To this:
const options = e.target.elements.options.value;
It'd work.
Also, to check what I'm saying you have 2 options:
option 1: console.log
function onSubmitHandle(e){
e.preventDefault();
const options = e.target.elements.options;
console.log({ options })
app.options.push(options);
reRenderApp();
e.target.elements.options.value = ''
}
option 2: make that option a valid child of react with JSON.stringify()
<ol>
{app.options.map((item, index)=>{
return <li key={index}>{JSON.stringify(item)}</li>
})}
</ol>
You can do
{app.options.length && app.options.map((item)=>{
return <li key={item}>{item}</li>
})}
But you must be sure that "item" here is not an object as you can't render an object
The reason for this is that your options array is going to be filled with elements as you're pushing the input element with the name of "option" into your array - this elements are objects in JS which you can't render out as list items.
Use React State to store anything that's going to change in the UI - in this case your list of options> So rather than doing
var app = {
title: 'Indecision App',
subtitle: 'yo',
options: []
}
let count = 0;
Do:
const [options, setOptions] = React.useState([]);
const [count, setCount] = React.useState(0);
Title and subtitle are probably not going to change, so just put them in h1 & h2 elements - if they are, then use the state pattern again.
Get rid of the two inputs with types of "submit" & "reset" just use normal button elements instead.
You'll also need an onchange event on your input where the text will go in and each time the onchange event is fired (i.e, when a user types) you'll need to save the input text
const [inputText, setInputText] = React.useState('');
const handleChange = (e) => {
const {value} = e.target;
setInputText(value)
}
<input type="text" value={inputText} onChange={handleChange}/>
Then in your onHandleSubmit function, just have
const onHandleSubmit = () => {
setOptions([...options, inputText]);
setInputText('')
}
This should work

Create a Dynamic Checkbox Validation in Angular With Data from API

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.

How to search in the table between two age

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

Checkboxes not working in react js

my check boxes are not getting checked, when created dynamically. I am not able to find the problem. Though, when I hard-code the values for check box id and label for, it just works.
var category_list = this.props.categories_list.map(function(name, i) {
// debugger
return (
<div className="group-chkbx list-group-item">
<input key={i+11} type="checkbox" id={name.category_id} name="category" />
<label htmlFor={name.category_id}>{name.name}</label>
</div>
)
});
After a lot of research one of my colleague helped me out with a solution. The htmlFor and id must be same, but cannot be only numeric. The Ids that I'm using are purely numeric. When I added alphabet as a prefix, it just started working like charm. Thanks all for showing interest and helping out here.
There's nothing that would set the checked prop on them, anyway. When should they be checked?
(Also, remember that components in arrays (such as what .map returns) should have unique key props.)
If your checkboxes are not getting checked, most probably is that some other functionality is preventing it.
Here and example of how to get the checkbox values:
class WithChecks extends Component {
constructor(props){
super(props);
this.getValue = this.getValue.bind(this);
}
getValue(e){
const chk = e.target;
console.log(chk.checked);
console.log(chk.value);
}
render() {
const arr = ['a', 'b', 'c', 'd'];
return (
<div>
{
arr.map((value, index) => {
return (
<div key={index}>
<input type="checkbox"
id={'chk' + index}
onChange={this.getValue}
name="category"
value={value} />
<label htmlFor={'chk' + index}>{value}</label>
</div>
);
})
}
</div>
);
}
}
Maybe this can help to clarify.
The checked property of the input will control whether it is checked. Usually I use local state (or something from global redux state to control what is checked). Little Example:
class Something extends React.Component {
constructor(props) {
super(props);
this.state = {
checked: 0
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
// Do Stuff
}
render() {
return (
<div>
{
this.props.categories_list.map(function(name, i) {
return (
<div className="group-chkbx list-group-item" key={i}>
<input checked={i === this.state.checked} onChange={this.handleChange} type="checkbox" id={name.category_id} name="category" />
<label htmlFor={name.category_id}>{name.name}</label>
</div>
)
});
}
</div>
);
}
}

compare two arrays based on length: skip empty values

I have a form with 4 input (can be even many more) where the user can put a number or nothing. The only rule is that if you put a number in a input you cannot submit if the same number is in another input (no duplicates). You can submit with as many empty input as you want.
To validate the input I compare the length of the array of all the inputs with the same array with only unique values. If they have the same length it's ok.
I need to improve my code because now it works only if the user enters all the input fields. If some inputs are empty they are considered in the array with unique value as they all have "" as a value. So, if the user enters just one number I will get that array length is 4 and array unique is 2 but it should be 1 and 1 (skipping the blank items).
I was thinking about using splice() on arr, but is this the best way to do this validation?
**EDIT: I applied splice BUT if the array is ('1','','') my code gives me ('1','') instead of just (1) as I'd expect... ** This is because splice remove the item and change array length so that the for loop point to the wrong index.
Any idea?
HTML:
<div class="sez-form">
<fieldset>
<legend>Messaggi inclusi</legend>
<div class="percheckbox">
<input class="checkseq" type="checkbox" value="1" name="messaggio[0]">
Prova di messaggio che scorre<br>
<label>Ordine: </label>
<input class="seq" type="text" name="ordine[0]" maxlength="2" size="2">
</div>
<div class="percheckbox">
<input class="checkseq" type="checkbox" value="3" name="messaggio[1]">
Titoli di film<br>
<label>Ordine: </label>
<input class="seq" type="text" name="ordine[1]" maxlength="2" size="2">
</div>
<div class="percheckbox">
<input class="checkseq" type="checkbox" value="6" name="messaggio[2]">
Prova a testo fisso<br>
<label>Ordine: </label>
<input class="seq" type="text" name="ordine[2]" maxlength="2" size="2">
</div>
<br style="clear: both;">
</fieldset>
</div>
JAVASCRIPT:
function uniqueArray(arr) {
return $.grep(arr,function(v,k) {
return $.inArray(v,arr) === k;
});
}
$(document).ready(function() {
$('#invia').click(function(e) {
e.preventDefault();
var arr = $(".seq").map(function(){ return $(this).val(); }).toArray();
var empty = $(".seq").filter(function() {
return this.value == "";
})
for (index = 0; index < arr.length; ++index) {
if (arr[index]=='') {
new_arr = arr.splice([index],1);
}
console.log(arr);
}
if(empty.length == $('.seq').length) {
alert('Non hai scelto alcun messaggio per il workflow. Correggi per procedere.');
}
else if(uniqueArray(arr).length != $('.seq').length) {
console.log(uniqueArray(arr));
alert('Ci sono voci duplicate nella sequenza. Correggi per procedere.');
}
else if($('#dt_from').val()=='__/__/____ __:__') {
alert('Scegli data e ora di inizio validit\u00E0 per il workflow');
}
else if($('#dt_to').val()=='__/__/____ __:__') {
alert('Scegli data e ora di fine validit\u00E0 per il workflow');
}
else {
ajaxSubmit();
}
});
});
Here is another possible way to handle it. Here is the working JSFiddle. And here is the code:
$(function() {
$("#submit").click(function() {
//build a profile of the inputs
var inputs = [];
var values = [];
var dups = false; //track duplicates on pass 1
$(".seq").each(function(i, el) {
var empty = (el.value == ""); //check if empty
var exists = (!empty && $.grep(inputs, function(item, index) {
return (item.Value === el.value);
}).length > 0); //check if exists
dups = (dups || exists); //track dups
//add the new input item
var obj = {
Element: el,
Value: el.value,
Empty: empty,
Exists: exists
};
inputs.push(obj);
//conditionally add the sorting value
if (!empty && !exists)
values.push(el.value);
});
//Validate the inputs. If there are duplicates, don't submit
$(".seq").css("background-color", "white"); //clear errors
if (dups) {
$(inputs).each(function(i, el) {
if (el.Exists)
el.Element.style.backgroundColor = "red";
});
} else {
values = values.sort();
alert(values);
}
});
});
With this method, at the end you have an array - inputs - of all of the elements with their statuses so that you can provide error handling on specific fields. In my example, the error fields turn red.
At the alert, you have a sorted array of the valid values.
Maybe I don't understand what you are trying to do, but why can't you do it very simply with something like:
$('#invia').click(function(e) {
e.preventDefault();
var unique = [], nonunique = [];
$(".seq").each(function(index){
var val = $(this).val();
if (val !== "") {
if ($.inArray(val, unique) !== -1) {
nonunique.push(val);
} else {
unique.push(val);
}
}
});
// If unique and nonunique are empty, all inputs were blank
// else if nonunique is empty, inputs are valid and in unique
});
Use a hash to track values as you iterate. This example simply returns true or false, but you could also scan the entire array and return the repeated values.
function uniquifyArray(ary) {
var seen = {};
var isUnique = true;
/* iterate backwards since the array length will change as elements are removed */
for (var i=ary.length; i--;) {
/* remove blank/undefined */
if (typeof ary[i] === 'undefined' || ary[i] === '') {
ary.splice(i,1);
} else {
/* check if this value has already been seen */
if (ary[i] in seen) {
isUnique = false;
ary.splice(i,1);
} else {
seen[ary[i]]=true;
}
}
}
ary = ary.sort();
return isUnique;
}
var test = [ '1','2','','','3','4','1' ];
uniquifyArray(test); // returns false, test = [ '1','2','3','4' ]
test = [ '1','2','','' ]
uniquifyArray(test); //true, test = ['1','2']

Categories