Prevent other fields in loop from changing with state - javascript

What's the best way to accomplish something like this. I have a credit note that I wish to edit the unit price. I'll pull in the invoice details then the user can edit/change the unit price. When I do this, all fields are changing. Somehow I need to put this in its own state? I have no idea how.
Parent Component using React.createClass:
line_items = [
{
id: 1,
unit_amount: "250.00",
...
},
{
...,
unit_amount: "20.00",
...
}
]
//render
<LineItems items={this.props.line_items} />
Child Component using React.createClass:
getInitialState(){
return{
creditedValues: []
}
},
_valueChange(e){
this.setState({creditedValues: this.state.creditedValues.concat([e.target.value])})
},
render(){
var items = this.props.items;
if (items.length !== 0) {
var lineItems = items.map(function(l){
return(
<tr key={l.id}>
<td data-label="Payment">{l.description}</td>
<td className="credit-note-qty" data-label="Issue Date">{l.quantity}</td>
<td className="credit-note-up" data-label="Amount">{l.unit_amount}</td>
<td className="credit-note-amt" data-label="Period">
<input type="number" value={this.state.newValue} onChange={this._valueChange} />
</td>
</tr>
)
}.bind(this));
};
return(<tbody>{lineItems}</tbody>)
}
Before I changed to concat, when I change a value, all values change at the same time. How to prevent this?
Edit:
I have updated the _valueChange with:
_valueChange(i, e){
e.preventDefault();
var obj = this.props.creditNote.line_items;
var num = obj.find(p => i === p.id);
num.unit_amount = e.target.value;
this.setState({creditedValues : num});
}
Input now changes to:
<input type="number" value={this.state.newValue} onChange={this._valueChange.bind(this, l.id)} />
Now I have exactly what I want but there is a but. If the user update another input field, it replace the entire state. True, as the code I have replaces the entire state. If the user update two or more fields, how to keep the new objects?

Related

I an trying to disable and enable checkbox based on value in angular 8

Here is my code
I am having List Array like: this is getCall.
ktsessions =
[ {"presenter":"Bharath","topic":"Angular","status":"scheduled","emailId":"bharathkumar#gmail.com"}, {"presenter":"Sayyad","topic":"Angular","status":"organized","emailId":"raheemsayyad#gmail.com"},{"presenter":"Kanchan","topic":"APS","status":"scheduled","emailId":"kanchanubey#gmail.com"} ];
<tr *ngFor = "let ktsession of ktsessions >
<td ><input type="checkbox" [disabled]='disabled'></td>
</tr>
TS code:
getktsession(){
this.service.getKtsession().subscribe(data =>{
console.log('get', data);
this.ktsessions = data;
this.ktsessions.find(user => {
if(user.presenter ==="Kanchan"){
this.disabled = true
} else {
this.disabled = false;
}
});
});
}
There are several issues here.
<tr *ngFor = "let ktsession of ktsessions > - Missing closing quote after ktsessions
<td ><input type="checkbox" [disabled]='disabled'></td> - [disabled]='disabled' should be [disabled]="ktsession.disabled". Each checkbox instance needs to have its own disabled property. [disabled]='disabled' sets each checkbox's disabled property to the disabled property of the component class instance rather than the checkbox instance.
this.ktsessions.find(user => { - Array#find is not an appropriate method to use here, even though it happens to work since you're not returning anything from the callback function and it will therefore iterate the entire array. Array#find is for searching an array for an element that matches the specified criteria and returning that element, not for iterating over an array and setting properties on each element, which is what you're doing here. Array#forEach is the appropriate method for that.
this.disabled = true and this.disabled = false - these are setting the disabled property on the component class instance (which is what this refers to), but what you need to do is set the disabled property on each user instance: user.disabled = true or user.disabled = false.
So your template should look something like this:
<tr *ngFor="let ktsession of ktsessions">
<td>
<input type="checkbox" [disabled]="ktsession.disabled" /> <!-- reference "ktsession.disabled" instead of "disabled" -->
{{ ktsession.presenter }}
</td>
</tr>
And your subscribe should look something like this:
this.getKtsession().subscribe((data) => {
this.ktsessions = data;
this.ktsessions.forEach((user) => { // <-- use forEach, not find
if (user.presenter === 'Kanchan') {
user.disabled = true; // <------------ set user.disabled, not this.disabled
} else {
user.disabled = false; // <----------- set user.disabled, not this.disabled
}
});
});
Here's a StackBlitz showing it working with those changes.

React web app- unique inputs from a table of variable lengths

I am building an app to help me learn react and solidity. I have a table which lists available products on the site. I want customers to be able to input the number of items they would like to buy for each product (each row) separately.
It works if I make all of them change a single variable in my state but stylistically I would like each row to have a unique value. To solve this I created an array to hold each value ( the array is called inputs). The tough part here is that the number of products (rows in the table) can vary so I cant just hard code it.
Rendering the table:
<tbody className="has-text-black-bis">
{indices.map((a) => (
<tr className="rows">
<td ><strong>{this.state.itemNames[a]}</strong></td>
<td ><strong>{this.state.costs[a]} Wei</strong></td>
<td ><strong>{this.state.quantities[a]}</strong></td>
//This row! <td >
Qty: <input type="number" className='table-input' name="inputs" value={this.state.inputs[a]} onChange={()=>this.handleTableInput(a)} />
<button type="button" className='buy-btn' onClick={()=>this.buyItem(a)}> Buy!</button>
</td>
</tr>
))}
</tbody>
Handling the input changes normally -This did not work when passing this.state.inputs[a] as the value as other rows would change as well:
handleInputChange = (event) => {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
I thought to change the above function to something like this where a (my index) is passed into the function. The problem is that I am not sure how to access the 'event' types like in the above generic function and thus not sure how to access the value that the user has input.
handleTableInput = (ind) => {
const {inputs} = this.state;
inputs[ind] = "value from table input";
this.setState({inputs: inputs})
}
Thank you for any help!

Angular dynamic checkbox Filtering implementation

thanks in advance
my Requirement is to make a custom filter with name wise search(done) and checkboxes which filters a Table's Rows(array of objects) by matching the checkbox value with the Row['tags'] (array of strings) and returns row if the tags array consist of value in a checkbox ,
The problem is that the filters(checkbox) is obtained from DB and Dynamically populated thus I cannot use ngmodel
Any implementation ideas are highly appreciated, I've seen a lot of questions with static filters and some filters using pipes but how to handle the dynamic case
so far my implementation,
Template:
<div id="searchByTag" *ngFor="let tag of tagList">
<input
type="checkbox"
(change)="filterByTags(tag, $event)"
/>{{ tag }}
</div>
Ts:
rows=[{},{}] //from db
temp = rows // copied when getting row from db
filterByTags(FilterTag, event) {
if (event.target.checked) {
const filteredRow = this.rows.filter((obj) => {
return tag.includes(FilterTag.toLowerCase());
});
this.rows = filteredRow;
} else {
return (this.rows = this.temp);
}
}
a Row object:
{
"xx":'yyy',
....,
"tags" : [
"org",
"pcb",
]
}
other problem is that the filtering technique currently returns only one row which matches the condition (cleared), but the main thing is the dynamic implementation of tags
you can have ngModel:
if this is your checkboxes = ["org", "pcb"];
then all you need is a record to bind checkboxes values to it:
checkboxes: {[id: string]: {value: any}} = {};
for(let tag of this.tags) {
this.checkboxes[tag] = {value: false}
}
now in your template:
<input type="checkbox" *ngFor="let item of tags"
[(ngModel)]="checkboxes[item].value">
you can see this in this stackblitz:
stackblitz

Multiplying Row.values and user input values in Reactjs tables

I have a table that gets data(wages/hr & numberofPresentAttendances) from the backend database. I'm trying to create a UI inside the same table that allows the user to input values(Nos of overtime hours), which is then automatically used to compute totalWages = (wages/hr.numberofPresentAttendances.overtime).
filtered = this.state.data.map( (row, i) => {
if(row.department.toLowerCase().indexOf(this.state.departmentFilter) > -1){
return (
<tr key={"workerData"+i}>
<td>{i+1}</td>
<td>{row.name}</td>
<td>{row.numberofPresentAttendances}</td>
<td>{row.wages}</td>
<td>{row.totalWages}</td>
<td>{row.overtime}</td>
<td>overtime</td>
<td>
<input number="text" name="overtime" id="time" className="form-control"
placeholder="Enter Nos of overtime hrs..."
value={this.state.overtime}
onChange={this.handleOvertimeChange} required />
</td>
<td>{row.Tallowance}</td>
<td>
</td>
</tr>
I know this is not the right way to do this but I would like to do something like this :
constructor(props){
super(props);
this.state = {
totalWages:0,
};
this.onOvertimeChange = this.onOvertimeChange.bind(this);
}
onOvertimeChange(event){
this.setState({ totalWages: event.target.value*row.wages*row.numberofPresentAttendances });
}
If done this way, I will get several errors, one of the errors will be "Row not defined"
How do I get it to work so that it works on each row?
The function doesn't know what row is, pass it as an argument and create a closure on the onOvertimeChange function
...
onChange={this.handleOvertimeChange(row)} required />
...
and Update onOvertimeChange as below
const onOvertimeChange = (row) => (event) => {
this.setState({ totalWages: event.target.value*row.wages*row.numberofPresentAttendances });
}

How to update a particular row of a vueJs array list?

Is there a proper way to refresh one particular row from a vueJS array list without reloading all the data?
In this case, it is a phone list.
<template>
<table class="table">
<tr v-for="phone in phones">
<td #click="edit(phone.id)">
{{phone.number}}
</td>
<td class="pointerCursor" #click="edit(phone.id)">
{{phone.comment | truncate(90)}}
</td>
</tr>
</table>
</template>
<script>
export default {
data () {
return {
phones = [
{id:1, number: '001 656 88 44', comment:''},
{id:2, number: '610-910-3575 x2750', comment:'Quod odit quia'},
{id:3, number: '536.224.2639 x714', comment:'primary phone'},
{id:5, number: '0034 556 65 77', comment:'home phone phone'},
]
}
},
methods: {
edit(id) {
// update using an api that returns the updated data.
var updatedPhone = update(id)
// how to update with reloading all the phone list?
// ...
}
}
}
</script>
PS: this is a code just to demonstrate the problem.
const currentIndex = this.phones.findIndex(p => p.id === id);
this.phones.splice(currentIndex, 1, updatedPhone)
If your target environment doesn't support findIndex on arrays, you can use some other means of finding the current index. One would be pass the phone instead of it's id.
edit(phone) {
const currentIndex = this.phones.indexOf(phone);
// update using an api that returns the updated data.
var updatedPhone = update(phone.id)
this.phones.splice(currentIndex, 1, updatedPhone)
}
In both cases, Vue will detect the change and re-render for you.
You could use the built-in index generation:
<tr v-for="(phone, index) in phones">
<td #click="edit(index)">
{{phone.number}}
</td>
<td class="pointerCursor" #click="edit(index)">
{{phone.comment | truncate(90)}}
</td>
</tr>
And then in your edit function:
methods: {
edit(index) {
phone = this.phones[index];
// update using an api that returns the updated data.
var updatedPhone = update(phone.id)
// how to update with reloading all the phone list?
this.phones.splice(index, 1, updatedPhone)
}
}

Categories

Resources