I am creating a questionnaire type form using ReactJs and Ant Design. It is a follow up question of How to create a questionnaire type form using Ant Design?
Now I am succeeded in adding new questions and their respective answers but not in removing them. Let's suppose I have added three questions and when I am trying to remove any one of them, its always removing the last one. The related code for removing is as follows:
remove = k => {
console.log(k);
const { form } = this.props;
// can use data-binding to get
const keys = form.getFieldValue("keys");
// We need at least one passenger
if (keys.length === 1) {
return;
}
keys.splice(k, 1);
// can use data-binding to set
form.setFieldsValue({
keys: keys
});
console.log(keys);
};
The complete code can be found as a demo on codesandbox.io.
I have done something similar in the past. Got rid of the boilerplate of antd's remove and replaced with this. Every time I add a row I push that row (object) to formRows array then removing like this:
remove = key => {
const newRows = this.state.formRows.filter(r => r.key !== key)
this.setState(
prev => ({
formRows: newRows
})
)
}
Related
i have this code https://stackblitz.com/edit/react-wc2ons?file=src%2FSection.js
I have sections, and i can add items to those sections. How can i delete some item? I tried
const removeItem = (i) => {
setSections((section) => {
const itemTarget = section.items[i];
const filtered = section.items.filter((item) => item !== itemTarget);
return {
...section,
items: filtered,
};
});
};
But for some reason it doesn't work
The removeItem callback prop you pass into the Section component is the way to go and you should get rid of passing setSections down to it as well.
removeItem={(i) => removeItem(index, i)}
Child components shouldn't do parent's work so you had it right at first, I'm going to help you implement that since I can already see the removeItem handler being there in the App component.
removeItem has already all the info you need, I'm going to rename the arguments so it's more clear.
const removeItem = (sectionIndex, index) => {
const newSections = sections.slice();
const newItems = newSections[sectionIndex].items.slice();
newItems.splice(index, 1);
newSections[sectionIndex].items = newItems;
setSections(newSections);
};
Then get rid of removeItem implementation in the Section component and destructure it from the props.
You are using setSections, but you return a single section instead of an array of sections. You probably need something like this:
// using the `section` variable from the upper scope
const removeItem = (i) => {
setSections((sections) => {
const itemTarget = section.items[i];
const filtered = section.items.filter((item) => item !== itemTarget);
const newSections = [...sections];
newSections[section.id] = {
...section,
items: filtered,
};
return newSections;
});
};
A few tips (you don't have to follow them): TypeScript can prevent such mistakes and give useful error messages. Immer.js can make writing such code simpler.
Your problem is that section is an array. So you are currently accessing the undefined property items on it. You would have to change your function to something like this
const removeItem = (i) => {
setSections((section) /* aqui vc tinha chamado de prev*/ => {
const itemTarget = section[i].items[j];
const filtered = section[i].items.filter((item) => item !== itemTarget);
return [...section, {
...section[i],
items: filtered,
}]
});
};
where i is the section in question and j is the item you want to delete.
here is a crude solution to your problem (i noticed other bugs in the code but this solves your issue with removing items at least), but i would separate the sections and items into separate components that in turn has its own states.
There you can add/remove items withing its parent section much more easily.
Now we have to work around this by looking for which section the code wants to remove the current item in.
https://stackblitz.com/edit/react-xxbvp1?file=src%2FSection.js
I have a state set as
const [filteredProducts, setFilteredProducts] = useState([]);
I want to be able to append to the end of that state. I am currently trying
products.forEach((product) => {
if (product.category === category) {
setFilteredProducts([...filteredProducts, product]);
}
});
It it looping through the products array correctly. I can even log the product after the setFilteredProducts and it logs the correct ones I want. I am calling this with an onClick.
Find all the products you want to add:
const productsToAdd = products.filter(product => product.category === category)
Then append them
setFilteredProducts((currentFilteredProducts) => ([...currentFilteredProducts, ...productsToAdd]));
The issue with your example is that filteredProducts may get stale after the first iteration. setFilteredProducts will not run synchronously, and filteredProducts keep the original value, until the re-render happen.
You would only append the last match to the existing filteredProducts array.
You can add all matches like so:
setFilteredProducts([...filteredProducts, ...products.filter((product) => product.category === category)]);
I'd recommend you do this in 2 steps:
Create an array of the new products you plan to add
let productsToAdd = [];
products.forEach((product) => {
if (product.category === category) {
productsToAdd.push(product);
}
});
Then combine the arrays and set state
setFilteredProducts([...filteredProducts, ...productsToAdd]);
I think you want what the ES6 built-in function does. You can rewrite your code to give you the the products that match the category like this:
const filteringTheProducts = products.filter(product => {
return product.category === category
})
setFilteredProducts(filteringTheProducts)
The result of the filtering will be the array of all the products that match that criteria.
Here is the documentation for .filter()
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
The problem is, that setFilteredProducts doesn't immediately affect products. It's React's job to decide when to update the state. So when you loop over products, you'll probably ending up adding just the last item, because filteredProducts wasn't updated yet.
What you can do, is preparing an array of products to add:
const productsToAdd = products.filter(product => product.category === category);
And then append them:
setFilteredProducts([...products, ...productsToAdd]);
I have a snippet of code here where i have an array that may or may not have keys in it. When the user presses on a 'friend' they add them to a list (array) where they might start a chat with them (add 3 friends to the array, then start a chatroom). The users selected might be toggled on or off.
Current Behavior:
i can add/remove one person, but i cant add multiple people to the array at the same time. When i add one person, select another - the first person is 'active', when i remove the first person, the second person automatically becomes active
Expected Behavior:
I would like to be able to add multiple people to the array and then remove any of the selected items from the array
onFriendChatPress = (key) => {
console.log(key) // this is my key 'JFOFK7483JFNRW'
let friendsChat = this.state.friendsChat // this is an empty array initially []
if (friendsChat.length === 0) {
friendsChat.push(key)
} else {
// there are friends/keys in the array loop through all possible items in the array to determine if the key matches any of the keys
for (let i = 0; i < this.state.selGame.friends.length; i++) {
// if the key matches, 'toggle' them out of the array
if (friendsChat[i] === key) {
friendsChat = friendsChat.filter(function (a) { return a !== key })
}
else {
return friendsChat.indexOf(key) === -1 ? friendsChat.push(key) :
}
}
}
}
Help please!
From your code, I was quite confused regarding the difference between this.state.selGame.friends and this.state.friendsChat. Maybe I missed something in your explication. However, I felt that your code seemed a bit too overcomplicated for something relatively simple. Here's my take on that task:
class Game {
state = {
friendsChat: [] as string[],
};
onFriendToggle(key: string) {
const gameRoomMembers = this.state.friendsChat;
if (gameRoomMembers.includes(key)) {
this.state.friendsChat = gameRoomMembers.filter(
(member) => member !== key
);
} else {
this.state.friendsChat = [...gameRoomMembers, key];
}
}
}
I used typescript because it makes things easier to see, but your JS code should probably give you a nice type inference as well. I went for readability over performance, but you can easily optimize the script above once you understand the process.
You should be able to go from what I sent you and tweak it to be according to what you need
I built a custom component that filters an array of objects. The filter uses buttons, sets from active to non-active and allows more than one option on/off at the same time.
StackBlitz of my attempt - https://stackblitz.com/edit/timeline-angular-7-ut6fxu
In my demo you will see 3 buttons/options of north, south and east. By clicking on one you make it active and the result should include or exclude a matching "location" either north, south and east.
I have created my methods and structure to do the filtering, I'm struggling with the final piece of logic.
So far I have created a method to create an array of filtered locations depending on what the user clicks from the 3 buttons.
Next this passes to my "filter array" that gets the logic that should compare this filtered array against the original to bring back the array of results that are still remaining.
Its not quite working and not sure why - I originally got this piece of functionality working by using a pipe, but fore reasons do not want to go in that direction.
//the action
toggle(location) {
let indexLocation = this.filteredLocations.indexOf(location);
if (indexLocation >= 0) {
this.filteredLocations = this.filteredLocations.filter(
i => i !== location
);
} else {
this.filteredLocations.push({ location });
}
this.filterTimeLine();
}
// the filter
filterTimeLine() {
this.filteredTimeline = this.timeLine.filter(x =>
this.contactMethodFilter(x)
);
}
//the logic
private contactMethodFilter(entry) {
const myArrayFiltered = this.timeLine.filter(el => {
return this.filteredLocations.some(f => {
return f.location === el.location;
});
});
}
https://stackblitz.com/edit/timeline-angular-7-ut6fxu
Sorry for my expression but u have a disaster in your code. jajaja!. maybe u lost that what u need but the logic in your functions in so wrong. comparing string with objects. filter a array that filter the same array inside... soo u need make a few changes.
One:
this.filteredLocations.push({location});
Your are pushing object. u need push only the string.
this.filteredLocations.push(location);
Two:
filterTimeLine() {
this.filteredTimeline = this.timeLine.filter(x =>
this.contactMethodFilter(x)
);
}
in this function you filter the timeLine array. and inside of contactMethodFilter you call filter method to timeLine again....
See a functional solution:
https://stackblitz.com/edit/timeline-angular-7-rg7k3j
private contactMethodFilter(entry) {
const myArrayFiltered = this.timeLine.filter(el => {
return this.filteredLocations.some(f => {
return f.location === el.location;
});
});
}
This function is not returning any value and is passed to the .filter
Consider returning a boolean based on your logic. Currently the filter gets undefined(falsy) and everything would be filtered out
I am trying to remove duplicate items from a collection that I request via an API in Laravel.
This is my code:
computed: {
// slice the array of data to display
filteredList() {
/* NEW PART */
var tips = this.dublicate;
/* END NEW PART */
tips = this.items.filter(item => {
return item.tip.toLowerCase().includes(this.search.toLowerCase())
})
return tips.slice(0, this.display);
},
dublicate() {
var filtered_array = [];
for(var i =0; i < this.items.length; i++) {
if(this.items[i].tip.toLowerCase() != this.items[i+1].tip.toLowerCase()) {
filtered_array.push(this.items[i])
}
}
return filtered_array;
}
}
}
If I remove the code within the NEW PART comments, everythin works fine.
In the NEW PART I am trying to remove duplicate content, based on the items tip attribute.
If the tip attribute is the same as the next items tip attribute, it should be excluded from the tips array, which is returned as a v-for="tips in filteredList".
However, I just get an empty array with this new part. What am I doing wrong?
I get the following from Vue Devtools:
dublicate:"(error during evaluation)"
filteredList:"(error during evaluation)"
An example data from items, that are from an API request:
(This is the data that I get, when I dont try to remove duplicates, which works)
As this is in VueJS, I cant use the answer provided here.
You are looking past the end of the array with i + 1. You need to push the last item without looking for the one after it (because there isn't one). I think using filter is more straightforward than building an array with a for loop.
dublicate() {
return this.items.filter((a, i) =>
i === this.items.length - 1 ||
a.tip.toLowerCase() !== this.items[i + 1].tip.toLowerCase()
);
}