context: Angular applications using the ngx-select-dropdown. A user can select multiple values, these values get sorted into "buckets" and sent to the api.
Issue: I am unable to remove a selected item from the end array - this.filterState. I have added a check to ensure that an item cant be added more than once - a click to select the item and a click to deselect the item. I thought that this would be a good time to remove the item with a splice as the user would have clicked to deselect if it already exists in the array - however, the dropdown does not pass the value of a deselected object it just removes it from the value array of the dropdown.
const index = this.filterState[convertedParam].indexOf(value);
if (index === -1) {
this.filterState[convertedParam].push(value);
}
Proposed solution: I think it will need some sort of comparison upon event change against the dropdown value object and the array that was previously sent to the api.
End goal: To be able to add and remove objects from the sorted array
Here is a stackblitz that I put together...
app.component.ts
handleFilterChange(prop: string, value: any): void {
// I think the comparison with "value" and whats already in "this.filterState" will need to be done here?
let field = this.fields.find(f => f.name === prop);
if (field.params) {
value.forEach(v => this.setFilterState(v.type, v, field));
} else {
this.setFilterState(prop, value, field);
}
console.log("SUBMITTED SORTED VALUES", this.filterState);
}
setFilterState(prop: string, value: any, field: Field): void {
const colourParamMap = {
I_am_RED: "reds",
I_am_BLUE: "blues",
I_am_GREEN: "greens"
};
if (field.name === "multiselect_1") {
const convertedParam = colourParamMap[prop];
const index = this.filterState[convertedParam].indexOf(value);
//stops from adding same value again and adds value to array
if (index === -1) {
this.filterState[convertedParam].push(value);
}
} else {
//There will be more logic here
this.filterState[prop] = value;
}
}
https://stackblitz.com/edit/ngx-select-dropdown-xkfbyr?file=app%2Fapp.component.ts
A simple fix in the end - I was trying to over complicate things. I needed to reset the this.filterState as the value from the dropdown will include every value from before, minus the ones that were deselected.
handleFilterChange(prop: string, value: any): void {
this.filterState = {
reds: [],
blues: [],
greens: []
};
Related
I've seen this question asked before but the solutions didn't help me hence why i've asked it again.
Currently, I am storing values into an array and that array is getting stored into localstorage.
This is the object
data.items -
0: {id: 190217270, node_id: 'MDEwOlJlcG9zaXRvcnkxOTAyMTcyNzA=', name: '3-Bit-CNC-Starter-Pack'}
1: {id: 187179414, node_id: 'MDEwOlJlcG9zaXRvcnkxODcxNzk0MTQ=', name: 'inb-go}
I have mapped through this and used 'name' as the value. I am calling this value through a button using this function
const favs = [];
function checkId(e) {
if (e.target.value !== ""){
if (!favs.includes(e.target.value)){
favs.push(e.target.value);
localStorage.setItem("name", JSON.stringify(favs));
console.log(favs);
document.getElementById("favsarray").innerHTML = favs;
}
}
}
and to remove the value from localstorage I am using this function.
function removeId(e, value) {
if (e.target.value !== "") {
favs.pop(e.target.value);
console.log(favs);
document.getElementById("favsarray").innerHTML = favs;
const stored = JSON.parse(localStorage.getItem("name"));
delete stored[value, e.target.value];
localStorage.setItem("name", JSON.stringify(stored));
console.log(stored);
}
}
Although the value is being removed from the array, it is not being removed from localstorage.
side note - I am calling this function with a separate button.
console log
array (item is gone)
[]
localstorage (the value is still there)
[
"Spiral-Up-Cut-Router-Bit"
]
But if I select another item to be added to localstorage, then the previous item gets removed.
UNFAVORITE - FUNCTION REMOVEid
[
"Spiral-Up-Cut-Router-Bit"
]
NEW FAVORITE - FUNCTION NEWId
[
"graphqless"
]
I hope this makes sense, I tried to add detail to it as best as possible.
Try to use localStorage.removeItem method to remove item from storage:
function removeId(e, value) {
if (e.target.value !== "") {
favs.pop();
// other code here
localStorage.removeItem('name'); // method to remove item from storage
}
}
UPDATE:
If an item is removed from array and we want to set this updated value to localstorage, then we can just update this value:
function removeId(e, value) {
if (e.target.value !== "") {
favs.pop();
console.log(favs);
document.getElementById("favsarray").innerHTML = favs;
const stored = JSON.parse(localStorage.getItem("name"));
delete stored[value, e.target.value]; // this code looks very fishy - charlietfl
localStorage.setItem("name", JSON.stringify(favs));
console.log(stored);
}
}
The easiest way is to just overwrite the item in localStorage. Once you remove the item from the favs array, call localStorage.setItem("name", JSON.stringify(favs)); again and you're done.
I am not sure whether this will help you but anyway I am sharing.
I don't understand this part of the abovementioned code:
delete stored[value, e.target.value];
What are you passing in the value and e.target.value? If it is the name ("Spiral-Up-Cut-Router-Bit") itself then the delete won't remove the value from the array. Usually, when you use the delete operator on the JS array you need to pass the index of the value, not the value itself.
Also, When you delete an array element, the array length is not affected. This holds even if you delete the last element of the array.
When the delete operator removes an array element, that element is no longer in the array.
You can refer to the above output image, when I deleted the array values using the value even though its output is true it does not delete the value from the array. But when I used the index value for the delete, it deleted the value from the array.
Note: The array just removed the value but did not clear the index.
Maybe, you should use splice to remove specific values from the array and store the new array into the storage.
Also, the delete operator works well with JS objects. If you want to read more about this you can go to this link.✌🏼
Delete using splice:
var trees = ['redwood', 'bay', 'cedar', 'oak', 'maple']; trees.splice(3,1); console.log(trees);
As suggested, use splice (which will also update the Array's length) to delete the entry from the Array.
const stored = JSON.parse(localStorage.getItem("name"));
const index = stored.indexOf(nameValue);
if (index !== -1) {
stored.splice(index, 1);
localStorage.setItem("name", JSON.stringify(stored));
}
See:
I want to make a list of checkboxes on a UI that user's can use to toggle and filter a set of data results. The checkboxes can be cumulative so I store them as a string array for now. My code looks something like this.
export interface IMyObjectFromAPI {
status: {
id: number,
description: string,
location: string,
name: string,
imageUrl: string
}
}
var filteredByTerms: string[] = [];
var resultsFromAPI: IMyObjectFromAPI [] = [];
var filteredDataResults: IMyObjectFromAPI[] = [];
I save the return results from the api call into the resultsFromAPI array.
On the UI, I have a group of checkboxes based on countries that is populated with a loop through a countries array. On select of a checkbox, I fire off the following code. Again, the goal here is to add multiple things to the array of terms to filter by (so I want to filter by location + name).
filterDataResults(term: string) {
var indexOfTerm = this.filteredByTerms.indexOf(term);
// if the term is not in an array of terms to filter by, add it
if (indexOfTerm === -1) {
this.filteredByTerms.push(term);
this.filteredDataResults = this.resultsFromAPI.filter(x => x.location ===
this.filteredByTerms.includes(term));
}
else {
this.filteredByTerms.splice(indexOfTerm, 1);
this.filteredDataResults = this.resultsFromAPI.filter(x => x.location ===
this.filteredByTerms.includes(term));
}
}
I don't know if I'm explaining this correctly but I've attached a picture to help. A series of checkboxes on the left, a data set on the right, and the checkboxes can be cumulative (so in the image example, if I select ITContractor and Clinical Psychology, the filter function would look for something in the results returned from the API which statifies both conditions.
It seems like some HOFs and map of filters might help you organize your user determined logic/filtering.
const filters = {
lastHourFilter: (result) => result.postedDate > Date.now() - ms('1 hour'),
last24HoursFilter: (result) => result.postedDate > Date.now() - ms('24 hours'),
...
itContractorFilter: generateSpecialismFilter('IT Contractor'),
clinicalPsychologyFilter: generateSpecialismFilter('Clinical Psychology'),
...
fullTimeFilter: generateJobTypeFilter('Full Time'),
temporaryFilter: generateJobTypeFilter('Temporary')
}
Then you inspect the check boxes and determine which filters you should apply to the results. Something like:
function applyFilters(results) {
Object.keys(filters).forEach((key) => {
if (checkboxes[key].checked) results =
results.filter(filters[key]);
});
return results;
}
Here checkboxes is a map of checkboxes in the DOM indexed by the same keys as your filters.
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 have a Vue component that builds the below into a blog form field. The writer is allowed to creatively add/slot any field of choice in between each other when building a blog post ...(like: title, paragraph, blockquote, image) in an object like:
{"post":{"1":{"title":{"name":"","intro":""}},"2":{"paragraph":{"text":"","fontweight":"default-weight","bottommargin":"default-bottom-margin"}},"3":{"image":{"class":"default-image-class","creditto":""}},"4":{"subheading":{"text":"","size":"default"}}}};
I've tried using jQuery each to iterate and add it up into a makedo "dataObj" object and inject it back on the data:
data: { treeData: myUserData.post },
injectFieldType: function(type, position){
var storeObj = {};
var dataObj = this.treeData;
var crntKey;
$.each( dataObj, function( key, value ) {
if(key < position)
{
//remain same as key is not to change
}
else if(key == position)
{
dataObj[''+(parseInt(key)+1)] = dataObj[key]; /*push key further right with +1*/
dataObj[key] = /*add injected field here*/;
}
else if(key > position)
{
dataObj[''+(parseInt(key)+1)] = dataObj[key]; /*push the rest*/
}
});
and inject it back with (this.treeData = dataObj;) when it has injected the desired key and has shifted the rest by adding 1 to their keys when this is clicked:
<button type="button" v-on:click="injectFieldType('image','2')">
I need to have {"post":{"1":{"title":{"name":"","intro":""}},"2":{"image":{"class":"default-image-class","creditto":""}},"3":{"paragraph":{"text":"","fontweight":"default-weight".... When I try to inject the image field in-between the existing "name" and "paragraph" fields and make the paragraph key now 3 (instead of the old 2).
I want "{1:{foo}, 2:{bar}"} to become => {"1:{foo}, 2:{moo}, 3:{bar}" }(notice 3 changed key)
NOTE: the number order is needed to align them reliably in publishing. And data: { treeData: myUserData.post } needs to agree with the changes to allow creating the field and updating each form "name" attribute array.
There are a few problems to address here.
Firstly, trying to use var dataObj = this.treeData; and then this.treeData = dataObj isn't going to help. Both dataObj and this.treeData refer to the same object and that object has already been processed by Vue's reactivity system. You could address the reactivity problems by creating a totally new object but just creating an alias to the existing object won't help.
Instead of creating a new object I've chosen to use this.$set in my example. This isn't necessary for most of the properties, only the new one added at the end really needs it. However, it would have been unnecessarily complicated to single out that one property given the algorithm I've chosen to use.
Another potential problem is ensuring all numbers are compared as numbers and not as strings. In your example you're passing in the position as the string '2'. Operators such as < will give you the expected answer for numbers up to 9 but once the number of items in treeData reaches 10 you may start to run into problems. For string comparision '2' < '10' is false.
The next problem is the order you're moving the entries. In your current algorithm you're overwriting entry key + 1 with entry key. But that means you've lost the original value for entry key + 1. You'll end up just copying the same entry all the way to the end. There are two ways you could fix this. One would be to use a new object to hold the output (which would also help to address the reactivity problem). In my solution below I've instead chosen to iterate backwards through the keys.
new Vue({
el: '#app',
data () {
return {
newEntry: 'Yellow',
newIndex: 4,
treeData: {
1: 'Red',
2: 'Green',
3: 'Blue'
}
}
},
computed: {
treeDataLength () {
return Math.max(...Object.keys(this.treeData))
}
},
methods: {
onAddClick () {
const newIndex = Math.round(this.newIndex)
if (newIndex < 1 || newIndex > this.treeDataLength + 1) {
return
}
this.injectFieldType(this.newEntry, newIndex)
},
injectFieldType (type, position) {
const list = this.treeData
for (let index = this.treeDataLength + 1; index >= position; --index) {
if (index === position) {
this.$set(list, index, type)
} else {
this.$set(list, index, list[index - 1])
}
}
}
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<ul>
<li v-for="index in treeDataLength">
{{ index}}. {{ treeData[index] }}
</li>
</ul>
<input v-model="newEntry">
<input v-model="newIndex">
<button #click="onAddClick">Add</button>
</div>
The decision to use an object with number keys seems very strange. This would all be a lot easier if you just used an array.
Hi I have input(inside ngFor, so same input field created with length of my array) in my HTML and binding to my ngModel and i have ngModelChange function as below:
onChangeReference(value: any, account: any, option: any) {
// this.referenceArray = Object.keys(this.your_reference).map(key => this.your_reference[key]);
console.log(this.referenceArray, value, 'huhu');
let tmpReference:Array<any> = [];
if (value) {
if (_.includes(this.referenceArray, value)) {
console.log('item und');
option.isRefDuplicate = true;
// this.referenceArray.pop();
} else {
console.log('item illa');
this.referenceArray.push(value);
option.isRefDuplicate = false;
}
}
}
Basically what i need to achieve is if there is any duplicated value in this.your_reference Array, i need to show error below that particular input field. I could able to achive it 90%, but now the problem is if enter "aaa" in one input field, and in other input field i key in the same value "aaa", working fine. showing error message. But when i backspace and remove one value its actually a new value "aa" which shouldnt be in my this.your_reference array. But it still showing error. How to fix this guys. Any idea?