Remove item from state - reactjs

I know this question has been asked a lot but I haven't seem to find a solution even tho I've tried different scenarios.
this.state = {
chartTypes: [{name: 'Bar', id: 1},{name: 'Line', id: 2},{name: 'Pie', id: 3},{name: 'Radar', id: 4} ],
selectedChart: [],
}
onSelect = (selectedList, selectedItem) => {
// selectedItem.name returns name of chartTypes, selectedItem.id returns id of chartTypes.
this.setState({
selectedChart: selectedItem.name
})
}
onRemove = (selectedList, removedItem) => {
// removedItem.name returns name of chartTypes, removedItem.id returns id of chartTypes.
}
The select option works fine but I just put it there so you can have a better understanding. onSelect puts every selectedItem.name into selectedChart. On the remove function, how may I remove item from this.state.selectedChart based on the value/index.

I think you can do something like this
let temp = this.state.selectedChart;
temp=temp.filter((item) => item !== removedItem.name);
this.setState({ selectedChart: temp });

var newArray = this.state.selectedChart.filter((el)=>
el.id !==removedItem.id
);
after filter
this.setState({selectedChart:newArray})

Just filter and set backed to state
onRemove = (selectedList, removedItem) => {
let filtered = this.state.chartTypes.filter(list=> list.id !==removedItem.id);
this.setState({ chartTypes: filtered });
}

Related

Avoid pushing duplicate objects to an angular array

I have multiple checkboxes in my angular application. When user checked and unchecked checkboxes I want to pass those true/false values into an array. It's happening from below code.
But my problem is as you can see the below console.log, it has duplicate checkbox values(index 0 and 3 have same thing) and push it to the array.
I want to know how to check duplicate objects and avoid pushing object to the array.
.ts file
layerChange(e:any){
var isChecked = e.target.checked;
var id = e.target.attributes.id.nodeValue;
const layer = {
isChecked: isChecked,
id: id,
}
this.layers.push(layer);
console.log(this.layers);
}
.html file
<input id="population" (change)="layerChange($event)" type="checkbox">
<input id="gender" (change)="layerChange($event)" type="checkbox">
<input id="householdIncome" (change)="layerChange($event)" type="checkbox">
console.log(this.layers)
**0: {isChecked: true, id: 'population'}**
1: {isChecked: true, id: 'age'}
2: {isChecked: false, id: 'population'}
**3: {isChecked: true, id: 'population'}**
You can check if an entry exists first using either :
var id = e.target.attributes.id.nodeValue;
var attr = this.layer.find(x => x.id === id);
if(!attr)
this.layers.push(layer);
or
this.layer.filter(x => x.id === id)
Edit
in your scenario is better to construct the array only one time in page load.
ngOnInit(): void {
this.layer = [{id: 'age', isChecked: false}, {id:'population',isChecked: false}]
}
and then alter the check state when user check/uncheck :-
layerChange(e:any){
var isChecked = e.target.checked;
var id = e.target.attributes.id.nodeValue;
this.layer.find(x => x.id === id).isChecked = isChecked;
console.log(this.layers);
}
You can create a string array which contains only ids and you can insert or remove elements from the array as per the selection
layerChange(e:any) {
const id = e.target.attributes.id.nodeValue;
const index = this.layers.findIndex(el => el === id)
if(index === -1) {
this.layers.push(id);
} else {
this.layers.splice(index, 1)
}
console.log(this.layers);
}
my GOD!
Kalana (and others) we need re-thinking the problem using "variables". Yes, Angular philosophy is binding variables. Variables in .ts makes the values are showed in the .html
So, some simple like declare an array of object in the .ts
layers:any[]=[{isChecked: true, id: 'population'},
{isChecked: true, id: 'age'},
{isChecked: false, id: 'population'}
]
Allow us write in .html
<ng-container *ngFor="let layer in layers">
<input type="checkbox" [(ngModel)]="layer.isChecked"
(change)="onChange()">
</ng-container>
In .ts we has a function:
onChange(){
console.log(this.layers)
}
Finally , I was able to find a solution. Thank you.
layerChange(e:any){
var isChecked = e.target.checked;
var id = e.target.attributes.id.nodeValue;
const index = this.layers.findIndex(el => el.id === id);
const layer = {
isChecked: isChecked,
id: id,
}
if(index > -1){
this.layers[index].isChecked = isChecked;
}else{
this.layers.push(layer);
}
console.log(this.layers);
}

How can I pass an array to a function

I have a function that save elements in an array onclick. I mean whe I click on an element, this element sends to the function that save it in an array:
private renderTags(tag: Tags, index: number) {
console.log('SELECTED TAG: ' + this.state.selectedTags.length);
return <div>
<div onClick={(e) => { e.stopPropagation(); this.collectTags (tag); }}>
<p
className={styles.tag}
># {tag.title} <i className="ms-Icon ms-Icon--CirclePlus"></i></p>
</div>
</div>
}
I can see that elments are in fact in the selectedTags array.
This is the collectTags function:
private collectTags(tag: Tags): any {
this.setState({ savingSettings: true, tagActive: true });
let selectedTags: Tags[] = this.state.selectedTags;
selectedTags.push(tag);
this.setState({
selectedTags: selectedTags
});
return selectedTags;
}
No, I have another function that update the state of an array containing the selected elemenents. The problem is that I can't find the way to pass the array to this function:
private saveSettings(newTag: Tags): void {
this.setState({ savingSettings: true, tagActive: true });
let tags: Tags[] = this.state.items;
// Add the new tag to the tags array and update the state
tags.push(newTag);
this.setState({
items: tags,
activeTile: -1
});
var cacheManager = new CacheManager();
cacheManager.set(this.cacheKey, tags);
this.props.provider.saveSettingsData(tags).then(() => {
this.setState({
savingSettings: false
});
});
}
To save the array I am trying to use an onclick event that looks like this:
onClick={this.saveSettings(this.state.selectedTags)}
In VS I am getting this error:
"Argument of type 'Tags[]' is not assignable to parameter of type 'Tags'.
Type 'Tags[]' is missing the following properties from type 'Tags': title, id, defaultTag"
What can I do?
Regards
Americo
private saveSettings(newTag: Tags): void {
this.setState((prevState )=>({
savingSettings: true,
tagActive: true,
items: [...prevState.items,newTag],
activeTile: -1
}));
var cacheManager = new CacheManager();
cacheManager.set(this.cacheKey, tags);
this.props.provider.saveSettingsData(tags).then(() => {
this.setState({
savingSettings: false
});
});
}
and you can call click throu ES6 function
onClick={()=>{this.saveSettings(this.state.selectedTags)}

How to remap array only on changes?

I have component Page which contains components Editor and Preview.
Page contains array items.
[
{
value: 0,
text: 'Item 1'
},
...
]
Array items is passed to Editor & Preview like this:
<editor [items]="items"></editor>
<preview [items]="items"></preview>
Editor can add/delete/edit/reorder items.
Issue is preview needs this array in another format.
[
{
index: 0,
label: 'Item 1'
},
...
]
If I do like this
getRadioItems(): any[] {
const items = [];
for (let i = 0; i < this.items.length; i++) {
items.push({ index: this.items[i].value,
label: this.items[i].text });
}
return items;
}
and then
<radio-list [radioItems]="getRadioItems()"></radio-list>
It refreshes radio list hundreds times per second. You can't even change value because it will be reset on every refresh.
If it were without remapping - it would work fine.
What is correct way to remap items to radioItems in such case?
Have you tried setting the ChangeDetectionStrategy of the preview component to OnPush? Then change detection should only be run when the #Input() items is updated.
It is stupid solution, but it works.
getRadioItems(): any[] {
const newJson = JSON.stringify(this.items);
if (this.json === newJson) {
return this.cachedItems;
}
this.json = newJson;
this.cachedItems = [];
for (let i = 0; i < this.items.length; i++) {
this.cachedItems.push({ index: this.items[i].value,
label: this.items[i].text });
}
return this.cachedItems;
}

Vue.js filtering on array

I am trying to filter an array using a computed property in vue.js. I would like to search on on multiple fields, name, state, tags etc.
My data:
events: [
{
id: 1,
name: 'Name of event',
url: '#',
datetime: '2017-05-10T00:00:00Z',
description: 'The full text of the event',
state: 'VIC',
tags: [
'ordinary',
'advanced'
]
},
{
id: 2,
name: 'Another event',
url: '#',
datetime: '2017-05-12T00:00:00Z',
description: 'The full text of the event',
state: 'VIC',
tags: [
'beginner'
]
},
{
id: 3,
name: 'Great event',
url: '#',
datetime: '2017-05-18T00:00:00Z',
description: 'The full text of the event',
state: 'NSW',
tags: [
'beginner'
]
}
]
},
The following function works as expected, however I cant work out how to have it search the items in 'tags' (commented out).
searchevents: function(){
let result = this.events
if (this.filterValue){
result = result.filter(event =>
event.name.toLowerCase().includes(this.filterValue.toLowerCase()) ||
event.state.toLowerCase().includes(this.filterValue.toLowerCase())
// event.tags.toLowerCase().values().includes(this.filterValue.toLowerCase())
)
}
return result
}
The following returns a blank array, this method works ok when i have done it in angular but not in vue.
searchevents2: function(){
var searchRegex = new RegExp(this.filterValue,'i')
this.events.filter(function(event){
return !self.filterValue || searchRegex.test(event.name) || searchRegex.test(event.state)
})
}
Ideally I would either like to be able to list array items to filter by or just filter by the entire array.
Appreciate any help, first post here so be gentle. I have a lot more experience with Python than Javascript so i may also use incorrect terminology at times.
You weren't too far off.
For your searchEvents filter, you just needed to add the tag filter. Here's how you might do that.
searchevents: function(){
let result = this.events
if (!this.filterValue)
return result
const filterValue = this.filterValue.toLowerCase()
const filter = event =>
event.name.toLowerCase().includes(filterValue) ||
event.state.toLowerCase().includes(filterValue) ||
event.tags.some(tag => tag.toLowerCase().includes(filterValue))
return result.filter(filter)
}
Array.some() is a standard array method that returns true if any element of the array passes your test.
searchevents2: function(){
const searchRegex = new RegExp(this.filterValue,'i')
return this.events.filter(event =>
!this.filterValue || searchRegex.test(event.name) || searchRegex.test(event.state))
}
With searchEvents2 you really only left an errant self in there. Either you needed to set self before you executed the filter, or, as I have done here, turned it into an arrow function.
Example.
const app = new Vue ({
el: '#app',
data: {
search: '',
userList: [
{
id: 1,
name: "Prem"
},
{
id: 1,
name: "Chandu"
},
{
id: 1,
name: "Shravya"
}
]
},
computed: {
filteredAndSorted(){
// function to compare names
function compare(a, b) {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
}
return this.userList.filter(user => {
return user.name.toLowerCase().includes(this.search.toLowerCase())
}).sort(compare)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<div class="search-wrapper">
<input type="text" v-model="search" placeholder="Search title.."/>
<label>Search Users:</label>
</div>
<ul>
<li v-for="user in filteredAndSorted">{{user.name}}</li>
</ul>
</div>

ReactJS How can I push a data from SelectField to an object within an array?

I have an array which you can see below. How can I insert data from a Selectfield into prints.printMethod?
selectedOrder: {
category: {},
product: {},
brand: {},
prints: {
printMethod: {},
height: '',
width: '',
colors: ''
}
}
My code below is the onChange function. I am not sure if I am missing something. I hope someone can help me.
onBrandingsChange = (event, index, value) => {
var newSelect = this.state.selectedOrder;
newSelect.prints.push(value);
this.setState({selectedOrder: newSelect});
}
<SelectField value={this.state.selectedOrder.prints.printMethod} onChange={this.onBrandingsChange}>
{_.map(this.state.brandings, (b) => {
return <MenuItem value={b.name} primaryText={b.name} key={b.id}/>;
})}
</SelectField>
prints is an Object not an array type so push can't be used here.
You can assign the value to the key directly if you wish it to be single depth.
Something like:
onBrandingsChange = (event, index, value) => {
var newSelect = this.state.selectedOrder;
// set it
newSelect.prints.printMethod = value
// or use assign
newSelect.prints = Object.assign(newSelect.prints, { printMethod: value });
this.setState({selectedOrder: newSelect});
}
Of if you wish this to be an array you need to change the types of your selectedOrder state.

Resources