Single row value change is reflected in all the drawers - reactjs

I have two drawer components and inside those drawer user can select maximum 3 rows. Each row has a Accessory Qty property which cannot be greater than Max Qty.
Two Drawers, each with clickHandler which triggers inner row sections
Each row has Accessory Qty and Max Qty. as mentioned above.
Selecting all the rows and clicking Associate to all which adds the selected rows in the other drawers. You can see in the breadcrumbs that currently I'm in SET F. Now after going back to Image one, when I click the 2nd drawer which is SET H I can see that the selected Items are added here.
Now if I change any value in any of the rows of SET H the same change is reflected in the SET F
This is the whole problem, I want the changes not to reflect in other drawers.
For this data is getting fetched from the API. I'm setting a copy of the data in the backupdata. Then with the index and values I'm changing the values in the corresponding rows.
handleQtyChange
const handleQtyChange = (event, i) => {
console.log("Set Index QTY", index, i);
const backupdata = [...billOfMaterials];
var data = event.target.value;
if (backupdata[index].accessory_details[i].accessory[0].code !== "") {
var accesory_code =
backupdata[index].accessory_details[i].accessory[0].code;
var max_qty = backupdata[index].accessory_details[i].max_qty;
var updated_qty = event.target.value;
if (parseFloat(updated_qty) > parseFloat(max_qty)) {
setMessageDialog({
isOpen: true,
message: "Qty must be less than or equal to Max qty",
severity: "error",
});
} else {
if (data == "" || (!isNaN(+data) && parseFloat(data) >= 0)) {
console.log("AccessoryChildIndex", i);
console.log("Max qty", accessoryListWithMaxQty);
console.log("Set Index", index);
setMessageDialog({
isOpen: false,
message: "",
severity: "error",
});
var accessory_total_qty_index = backupdata[
index
].accessory_total_qty.findIndex(
(x) =>
x.code == backupdata[index].accessory_details[i].accessory[0].code
);
console.log("accessory_total_qty Index", accessory_total_qty_index);
backupdata[index].accessory_total_qty[accessory_total_qty_index].qty =
updated_qty;
backupdata[index].accessory_details[i].accessory_qty = updated_qty;
backupdata[index].accessory_details[i].total = (
parseFloat(
event.target.value == undefined || event.target.value == ""
? 0
: event.target.value
) * parseFloat(backupdata[index].accessory_details[i].rate)
).toFixed(2);
backupdata[index].grand_total = grandTotal();
setBillOfMaterials(backupdata);
if (!checkBomAccessoryWiseTotalQty(accesory_code, max_qty)) {
setMessageDialog({
isOpen: true,
message: "Qty limit exceed max qty would be " + max_qty,
severity: "error",
});
backupdata[index].accessory_total_qty[
accessory_total_qty_index
].qty = 0;
backupdata[index].accessory_details[i].accessory_qty = 0;
backupdata[index].accessory_details[i].total = 0;
backupdata[index].grand_total = grandTotal();
setBillOfMaterials(backupdata);
}
console.log("Change BOM", billOfMaterials);
}
}
}
};
handleAssociateToAll
const handleAssociateToAll = () => {
//var data = [...billOfMaterials];
var backupdata = [...billOfMaterials];
//var backupdata = JSON.parse(JSON.stringify([...billOfMaterials]));
var selectedData = backupdata[index].accessory_details.filter((el) => {
return el.is_selected == true;
});
console.log(selectedData);
console.log(index);
backupdata.map((el, i) => {
if (index != i) {
console.log(selectedData);
selectedData.map((dt) => {
dt.accessory_qty = "0";
dt.is_selected = false;
let checkAccessory = backupdata[i].accessory_details.find((data) => {
return (
data.accessory[0].code != "" &&
data.accessory[0].code == dt.accessory[0].code
);
});
console.log("Check", checkAccessory);
if (checkAccessory == undefined) {
backupdata[i].accessory_details = [
dt,
...backupdata[i].accessory_details,
];
}
});
backupdata[index].accessory_total_qty &&
backupdata[index].accessory_total_qty.map((qty) => {
qty.qty = "0";
let checkTotalQty = backupdata[i].accessory_total_qty.find(
(data) => {
return data.code == qty.code;
}
);
if (checkTotalQty == undefined) {
backupdata[i].accessory_total_qty = [
...backupdata[i].accessory_total_qty,
qty,
];
}
});
}
});
setBillOfMaterials(backupdata);
checkAllSelected();
};
console.log("bill", billOfMaterials);
I guess I've explained well. So how do I keep the other drawer's data unchanged and change only the selected drawers rows.
The api response
After clicking Associate to all:
The overall bill, the selected rows are added in accessory_total_qty:

Related

Can you help me create a Google Apps Script that will return values from a database to an on-sheet form?

My issue is returning the values saved in a database to the form in each cell in the range. The saveToDB script below is able to save the values from the form to the database, but I don't know how to retrieve those values. Hope you could help me. Thanks so much!
Form: sample form
Database: database sample
function saveToDB(){
range = ["C2","C4","C6"]
var newRange = range.map(f => formSheet.getRange(f).getValue())
dbSheet.appendRow(newRange)
}
function loadToForm(){
range = ["C2","C4","C6"]
var dbArray = dbSheet.getRange(2,1,dbSheet.getLastRow(),dbSheet.getLastColumn()).getValues()
var newArray = dbArray.filter(function(row){
if(row[0] === "Fred" && row[0] !== -1){
return row !== ""
}
})
//Don't know how to return each value to each cell in the range
//Update - this is the code that did it
range.map((f,i) => formSheet.getRange(f).setValue(newArray[0][i]))
}
function saveToDB() {
const ss = SpreadsheetApp.getActive();
const fsh = ss.getSheetByName('Form Sheet Name');
const dsh = ss.getSheetByName('Database Sheet Name')
let arr = ["C2", "C4", "C6"].map(e => fsh.getRange(e).getValue());
dsh.appendRow(arr);
}
function loadToForm() {
const range = ["C2", "C4", "C6"];
const ss = SpreadsheetApp.getActive();
const dsh = ss.getSheetByName('Database Sheet Name');
const fsh = ss.getSheetByName('Form Sheet Name');
let r = SpreadsheetApp.getUi().prompt('Row Number','Enter Row Number', SpreadsheetApp.getUi().ButtonSet.OK_CANCEL);
if(r.getSelectedButton() == SpreadsheetApp.getUi().Button.OK) {
let vs = dsh.getRange(row, 1, 1, dsh.getLastColumn()).getValues()[0];
range.forEach((e,i) => { fsh.getRange(e).setValue(vs[i])
});
}
}
All you have to do is use the setValue() function and iterate through each cell like so:
function loadToForm(){
range = ["C2","C4","C6"]
var dbArray = dbSheet.getRange(2,1,dbSheet.getLastRow(),dbSheet.getLastColumn()).getValues()
var newArray = dbArray.filter(function(row){
if(row[0] === "Fred" && row[0] !== -1){
return row !== ""
}
})
//Remove useless extra dimension from array
newArray = newArray[0];
//Solution 1 using RangeList
var ranges = formSheet.getRangeList(range).getRanges();
for(var i in newArray){
ranges[i].setValue(newArray[i]);
}
//Solution 2 not using RangeList
for(var i in newArray){
formSheet.getRange(range[i]).setValue(newArray[i]);
}
}

How do I structure my React Component? (Too bloated)

I'm making a card game and I have a parent component which holds all my methods. The methods are my "logic" and it determines how to set the randomly generated hand according to 'house-way'. Think of house-way as a set of rules on how to play a certain hand.
I'm having a problem with how to structure my components. I've heard that you should keep your components small and my component is already 300 lines long.
Any advice on how to restructure this? I've tried putting methods on a different file and importing them, but I had trouble when it comes to setting state. In other words, 'this.setState()' throws an error unless it is a method of a class.
the code is a mess, but I basically need help with how to make my component less bloated.
export default class Layout extends Component {
constructor(props) {
console.log("starting up");
super(props);
//each hand holds a randomly generated tile object from { tilesSet }
this.state = {
//needs empty spots for when (mounting) <Hands hand1={this.state.hand[0].img} /> else error since hand[0] doesnt exist.
hand: ["", "", "", ""],
cards: false,
pairName: '',
rule: '',
show: false,
history: [],
test: 'test'
};
//binding in the constructor is recommended for performance.
this.handleToggle = this.handleToggle.bind(this);
this.handleClick = this.handleClick.bind(this);
this.handleHW = this.handleHW.bind(this);
this.assignHands = this.assignHands.bind(this);
this.checkPair = this.checkPair.bind(this);
this.checkTeenDey = this.checkTeenDey.bind(this);
this.hiLowMiddle = this.hiLowMiddle.bind(this);
this.compare = this.compare.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
//n = pairTL, n2 = otherTL
split(n, n2){
//Gee Joon
if (n[0].pair === 1) {
let combo1 = baccaratCount(n2[0], n[0]);
let combo2 = baccaratCount(n2[1], n[1]);
//if it meets the split requirements...
if((combo1 >= 7 && combo2 >= 9) || (combo1 >= 9 && combo2 >= 7)){
console.log('got em', combo1, combo2);
return true;
}
else {
return true;
}
//Teen/Dey
// } else if(high[0].val === 2) {
// var combo1 = baccaratCount(high[0].val, low[0].val);
// var combo2 = baccaratCount(high[0].val, low[1].val);
// //checks if any of the tiles are 7,8, or 9. for 9 gong and wong.
// var check7_9 = low[0].val >= 7 && low[0].val <= 9;
// var check7_9_2 = low[1].val >= 7 && low[1].val <= 9;
// //regular 6-8 split rule.
// if((combo1 >= 6 && combo2 >= 8) || (combo1 >= 8 && combo2 >= 6)){
// moveTiles("split");
// return true;
// //we might have 7,8,9 with T/D. (with 8 and 9, it turns to 0 and 1, so we need this part.)
// } else if (check7_9 === true || check7_9_2 === true){
// //if both are 7,8, or 9
// if (check7_9 === check7_9_2){
// moveTiles("split");
// return true;
// //only if 1..
// } else if (check7_9 === true && check7_9_2 === false){
// if (low[1].val >= 3 && low[1].val <=6){
// moveTiles("split");
// return true;
// } else {
// moveTiles();
// return true;
// }
// //if other one...
// } else{
// if (low[0].val >= 3 && low[0].val <=6){
// moveTiles("split");
// return true;
// } else {
// moveTiles();
// return true;
// }
// }
// //does not split.
// } else {
// moveTiles();
// return;
// }
// } else {
// // all other pairs. split pairs are in one array with a length of 2. ex: [7, 9]
// var combo1 = baccaratCount(high[0].val, low[0].val);
// var combo2 = baccaratCount(high[0].val, low[1].val);
// if(combo1 >= high[0].split[0] && combo2 >= high[0].split[0]){
// moveTiles("split");
// } else {
// moveTiles();
// }
// return true;
// }
}
}
//checks for pairs. takes an array as arg
checkPair(hand){
for(let i = 0; i < hand.length; i++) {
for (let ii = 0; ii < hand.length; ii++) {
// if there is a pair and it is not comparing to itself.
if (hand[i].pair === hand[ii].pair && i !== ii) {
let pairTL = hand.filter((x) => x.rank === hand[i].rank); //array of the pair tiles
let otherTL = hand.filter((x) => x.rank !== hand[i].rank); // array of the other 2 tiles. use these two to move tiles accordingly
//if we split this pair...
if (hand[i].split !== false) {
//returns true if it split..
if(this.split(pairTL, otherTL)) {
let copyArr = [pairTL[0], otherTL[0], pairTL[1], otherTL[1]];
this.setState(() => ({hand: copyArr}));
}
else {
let copyArr = otherTL.concat(pairTL);
this.setState(() => ({hand: copyArr}));
return true;
}
}
//don't split
else {
let copyArr = otherTL.concat(pairTL); //concats the two small arrays together and renders.
this.setState(() => ({hand: copyArr, pairName: pairTL[0].name, rule: 'Don\'t Split'}))
return true;
}
}
}
}
return false; // no pairs
}
//will not execute if there is a pair...(checkPair returns true)
checkTeenDey(hand){
//true if we have teen or dey
let teenDey = hand.find((el) => el.val === 2) !== undefined;
//if true...
if(teenDey){
let tile = hand.find((el) => el.val === 2); // teen/ dey object
let tempArr = hand.filter((el) => el.name !== tile.name); //new arr without marked teen/dey. arr.length = 3
let secondTeenDey = tempArr.find((el) => el.val === 2); //second teen/dey (not pair)
let seven = tempArr.find((el) => el.val === 7);
let eight = tempArr.find((el) => el.val === 8);
let nine = tempArr.find((el) => el.val === 9);
//if there is a second teen/dey
if (secondTeenDey){
let twoArr = tempArr.filter((el) => el.name !== secondTeenDey.name);
console.log(tile, secondTeenDey, twoArr);
return true;
}
//look for 7,8,9
else if (seven){
console.log (seven);
return true;
}
else if(eight){
return true;
}
else if(nine){
return true;
}
}
// no teen or dey...
else{
return false;
}
}
//point system used for sort()
compare(a,b){
let comparison = 0;//no change
if(a.realValue < b.realValue){
comparison = -1;//a comes before b
}
else if(a.realValue > b.realValue){
comparison = 1;//b comes before a
}
return comparison;
}
//will not execute if there is a teen dey...
hiLowMiddle(hand){
//makes a new copy of hand and sorts it using sort()'s point system.
let sortedArr = hand.slice().sort(this.compare); //slice used, else mutates hand.
let tempHair = [sortedArr[0], sortedArr[3]];
let tempBack = [sortedArr[1], sortedArr[2]];
let hiLow = tempHair.concat(tempBack); //newly sorted arr
this.setState(() => ({hand: hiLow, rule: 'Hi-Low-Middle'}));
}
//generates new hand and updates them to state.
assignHands() {
let tempArr = [0, 0, 0, 0]; //filler array
let testArr = tilesSet.slice(); //filler array. tilesSet is untouched
//loops through and assigns random tile from deck
let newArr = tempArr.map((x) => {
let counter = Math.floor(Math.random()* (testArr.length - 1));
//used to hold the selected obj. needed since splice changes arr.length and we splice/display the different indexes.
let dummyArr = testArr[counter];
testArr.splice(counter, 1);
return dummyArr;
})
//updates state
this.setState({hand: [newArr[0], newArr[1], newArr[2], newArr[3]], show: true, history: [...this.state.history, [...newArr]]})
}
handleSubmit = (e) => {
e.preventDefault();
}
//toggle effect.
handleToggle = () => {
this.setState(() => ({cards: !this.state.cards}));
}
handleClick = () => {
assignHands(tilesSet);
//works, but not 100% properly. the changes are one step behind. fix async.
//check to see the history length. max set # 20
if(this.state.history.length >= 10){
let temp = this.state.history.slice();
temp.shift();
this.setState(() => ({history: temp}))
}
}
//House Way
handleHW(){
if(!this.checkPair(this.state.hand)){
if(!this.checkTeenDey(this.state.hand)){
this.hiLowMiddle(this.state.hand);
}
}
}
render() {
return (
<div>
{this.state.show ? <h1>{baccaratCount(this.state.hand[0], this.state.hand[1]) + '/' + baccaratCount(this.state.hand[2], this.state.hand[3])}</h1> : <h1>Press New Hand to Start</h1>}
<form onSubmit={this.handleSubmit}>
<label>
<input type='text'/>
</label>
<button>submit</button>
</form>
<Input test={this.state.test}/>
<Hands
cards={this.state.cards}
hand1={this.state.hand[0].img}
hand2={this.state.hand[1].img}
hand3={this.state.hand[2].img}
hand4={this.state.hand[3].img}
/>
<Buttons
type="button"
className="btn btn-dark"
handleClick={this.handleClick}
handleHW={this.handleHW}
hand={this.state.hand}
/>
<h2>Value of tiles: {this.state.hand[0].val ? this.state.hand[0].val : "0"} - {this.state.hand[1].val ? this.state.hand[1].val : "0"} - {this.state.hand[2].val ? this.state.hand[2].val : "0"} - {this.state.hand[3].val ? this.state.hand[3].val : "0"}</h2>
<h2>Pair Name: {this.state.pairName}</h2>
<h2>Rule: {this.state.rule}</h2>
<h2>
History: <div>{this.state.history.map((el) => <li key={Math.random()}>{el.map((ele) => ele.name+ '--')}</li>)}</div>
</h2>
</div>
);
}
}

React state array object changes without setState

I have an array which is a state of the React component. This array is a checklist.
var units1 = this.state.units;
when I update units1, this.state.units changes without the this.setState({ units: units1 })
I use this.setState({ a: 2 }); just to see if the array was updated without this.setState({ units: units2 });
this.state.units gets its value from props so if the state changes the props also changes.
handleItemChange(e) {
var units1 = this.state.units.slice();
var unit_name = parseInt(e.target.attributes.getNamedItem('data-unit_name').value);
var new_unit;
if (!e.target.checked && this.state.units && this.state.units.length > 0) {
this.state.units.map((unit) => {
if (unit_name == unit.codpacunidad) {
if (unit.topics && unit.topics.length > 0) {
unit.topics.map((topic) => {
if (topic.codpacunidadtema == e.target.name) {
new_unit = unit;
var index = units1.indexOf(unit);
//units1.splice(index, 1);
units1 = update(units1, {$splice: [[index, 1]]})
var topics1 = unit.topics.slice();
index = topics1.indexOf(topic);
//topics1.splice(index, 1);
topics1 = update(topics1, {$splice: [[index, 1]]})
new_unit.topics = topics1;
}
});
}
}
});
} else {
var found_unit = false;
var name = parseInt(e.target.name);
var order = parseInt(e.target.attributes.getNamedItem('data-order').value);
var unit_order = parseInt(e.target.attributes.getNamedItem('data-unit_order').value);
if (this.state.units && this.state.units.length > 0) {
this.state.units.map((unit) => {
if (unit.codpacunidad == unit_name) {
found_unit = true;
new_unit = unit;
var index = units1.indexOf(unit);
units1.splice(index, 1);
var new_topic = {
codpacunidadtema: name,
orden: order
};
var topics2 = new_unit.topics;
new_unit.topics = update(topics2, { $push: [new_topic]});
}
});
}
if (found_unit == false) {
new_unit = {
codpacunidad: unit_name,
orden: unit_order,
topics: [{codpacunidadtema: name, orden: order }]
};
}
}
// var units2 = update(units1, { $push: [new_unit]});
// this.setState({ units: units2.sort(function(a, b) {
// return parseInt(a.orden) - parseInt(b.orden);
// })
// });
this.setState({ a: 2 }); //just to test if the array gets updated without this.setStaet({ units: units2 })
}
Anybody knows why this is happening?
As #Alexander van Oostenrijk said to make deep copy of array.
Because array are passed by reference which mean memory address of the array is passed not the value of array.
var units1 = this.state.units.slice();
Now units1 has the reference address of that array any change made to units1 or this.state.units.slice() will change value of both.As basically they are using address and if one change the value at address then both will read changed value.Hope you understand
To create deep copy you can create new object like
var units1 = Object.assign([],this.state.units)
This will create new object with data of this.state.units
extras I think you do not need .slice().

Increment the ID for every addition of elements in React

i am having a problem where i cannot able to increment the ID of the state element for every addition of elements in the row. What i am getting is same number is repeating for every addition of elements, i need something like ID should be 1 to n numbers of addition.
In text-box, i am not entering the ID, i enter only the LText(Name).
In general, the ID should generate for every addition of elements.
What i have tried is..
export default class EPIM091 extends cntrl.WITBase {
constructor(props) {
super(props);
const witState = this.state;
if (Object.keys(witState).length == 0) {
witState.model = { LID: 1, LText: '', SOrder: '', Inventoried: false, Location:[] }; //Here i need to store all other state values to the Location Array
}
}
clickAction = (e) => {
if (e.id == 'btn_add') {
//var i = 1;
const { model } = this.state;
if (model.LText != null) {
if ((model.Location || []).length == 0) {
model.Location = [];
}
// this.setState(prevState => { return { LID: e.id == 'btn_add' ? prevState.LID + 1 : prevState.LID - 1 } }); //This does not worked
model.Location.push({ "LID": model.LID.toString(), "LText": model.LText, "SOrder": model.SOrder, "Inventoried": model.Inventoried.toString() });
this.setState({
model: model,
LID: model.LID + 1 //This also not worked
});
}
}
};
render(){
// Due to confusion of code, i did not add the textboxes codes
<cntrl.WITButton id="btn_add" onWitClick={this.clickAction} />
}
I need something like when i add, i should get the Unique LID from 1 to the number of elements added. What i am getting is same ID i.e 1. Thank you
Many problems here :
1) You are trying to modify a variable that you declared constant:
const witState = this.state;
if (Object.keys(witState).length == 0) {
witState.model = { LID: 1, LText: '', SOrder: '', Inventoried: false, Location:[] };
2) You are trying to access state variable, but you never define it:
const { model } = this.state;
3) You try to modify it even if it's declared constant:
if (model.LText != null) {
if ((model.Location || []).length == 0) {
model.Location = [];
}
// this.setState(prevState => { return { LID: e.id == 'btn_add' ? prevState.LID + 1 : prevState.LID - 1 } }); //This does not worked
model.Location.push({ "LID": model.LID.toString(), "LText": model.LText, "SOrder": model.SOrder, "Inventoried": model.Inventoried.toString() });
this.setState({
model: model,
LID: model.LID + 1 //This also not worked
});
}
Unless there is a lot of code you didn't show us, you must have a lot of errors in your console. Watch it.
-[Edit]-:
The problem is that I can't really help you unless I have the whole code, because as it is, it doesn't make sense. If you just want to increment your LID state var, here is a working example:
constructor(props) {
super(props);
this.state={LID:1}
}
test=(e)=>{
let {LID} = this.state;
this.setState({LID:LID+1})
console.log(this.state.LID);
}
render(){
return(
<button onClick={this.test}> Test</button>
)
}

customizing ag-grid to set a max number of selectable rows

I am trying to customize a data table using ag-grid in my Angular 1.5 based project. The customization is that the user is allowed to select a maximum number of rows in the table, for example, the maximum is 2.
I have the following code by using node.setSelected(false) that I found in the documentation page here, but I got the error: node.setSelected is not a function when the selection exceeds the maximum of 2.
var gridOptions = {
columnDefs: columnDefs,
rowSelection: 'multiple',
onRowSelected: onRowSelected
};
function onRowSelected(event) {
var curSelectedNode = event.node;
var selectionCounts = vm.gridOptions.api.getSelectedNodes().length;
if (selectionCounts > 2) {
var oldestNode = vm.gridOptions.api.getSelectedNodes()[0]; // get the first node, to be popped out
oldestNode.setSelected(false); // causes the above 'not a function' error
}
}
Does anyone know what might be wrong with ag-grid for its setSelected() API? or any better way to do this customization?
it turns out that setSelected(false) method is outdated in its current ag-grid API, and I found that I can use deselectIndex() method to deselect the oldest node:
if (selectionCounts > 2) {
vm.gridOptions.api.deselectIndex(0, true); // This works!
}
Hope this will help someone else in the future!
var columnDefs =[{
headerName: 'Name',
field: 'name',
width: 108,
minLength: 1,
maxLength: 20,
editable: true
}]
- Modify prototype in file .js
TextCellEditor.prototype.init = function (params) {
var eInput = this.getGui();
var startValue;
// Set min & max length
if (params.column.colDef.maxLength)
eInput.maxLength = params.column.colDef.maxLength;
if (params.column.colDef.minLength)
eInput.minLength = params.column.colDef.minLength;
// cellStartedEdit is only false if we are doing fullRow editing
if (params.cellStartedEdit) {
this.focusAfterAttached = true;
var keyPressBackspaceOrDelete = params.keyPress === constants_1.Constants.KEY_BACKSPACE
|| params.keyPress === constants_1.Constants.KEY_DELETE;
if (keyPressBackspaceOrDelete) {
startValue = '';
}
else if (params.charPress) {
startValue = params.charPress;
}
else {
startValue = params.value;
if (params.keyPress !== constants_1.Constants.KEY_F2) {
this.highlightAllOnFocus = true;
}
}
}
else {
this.focusAfterAttached = false;
startValue = params.value;
}
if (utils_1.Utils.exists(startValue)) {
eInput.value = startValue;
}
this.addDestroyableEventListener(eInput, 'keydown', function (event) {
var isNavigationKey = event.keyCode === constants_1.Constants.KEY_LEFT || event.keyCode === constants_1.Constants.KEY_RIGHT;
if (isNavigationKey) {
event.stopPropagation();
}
});
};

Resources