I want to add & delete datasets from chart.js using checkboxes.
Right now I am adding using this:
var ds1 = {
label: ..
data: .. etc
};
data.datasets.push(ds1);
When I uncheck any of the checkboxes, it's always deleting the last dataset added, which is not necessary the one of the checkbox.
I'm using data.datasets.pop(ds1); to remove the when a checkbox is clicked.
If you're removing using data.datasets.pop(ds1) you'll never get the result you're looking for. The dataset property is an array, so let's just focus on arrays and ignore Chart.js.
First issue is that the pop() method of the Arrays type does not take an argument, so providing which element you want to remove is irrelevant. Pop() will always remove the last element from an array.
To remove a specific element from the array you need to use the splice() function.
Let's say that ds1 is the element you want to remove.
let removalIndex = data.datasets.indexOf(ds1); //Locate index of ds1
if(removalIndex >= 0) { //make sure this element exists in the array
data.datasets.splice(removalIndex, 1);
}
This will delete the 1 record in the array starting at the index we located ds1.
If you look at ChartJS' internal object chart.data.datasets, the datasets are distinguishable by the label you gave when initially adding the datasets (it's weird that there's no ID):
So it's really just a matter of filtering out an object from the array by that Label, and calling chart.update();.
// Filter out and set back into chart.data.datasets
chart.data.datasets = chart.data.datasets.filter(function(obj) {
return (obj.label != targetLabel);
});
// Repaint
chart.update();
Thank you JNYRanger!
It got like this:
...
$(document).ready(function() {
$('#ind4').change(function(){
if (this.checked) {
graph.data.datasets.push(ds4);
graph.update();
} else {
let removalIndex = graph.data.datasets.indexOf(ds4);
graph.data.datasets.splice(removalIndex, 1);
graph.update();
}
});
});
$(document).ready(function() {
$('#ind5').change(function(){
if (this.checked) {
graph.data.datasets.push(ds5);
graph.update();
} else {
let removalIndex = graph.data.datasets.indexOf(ds5);
graph.data.datasets.splice(removalIndex, 1);
graph.update();
}
});
I just added graph.data.... (when graph is the var of my chart) and graph.update() by the end of every action.
Actually you can add an ID in your dataset :
var ds1 = {
label: ..
data: ..
id : 'myId'
};
data.datasets.push(ds1);
It will not affect your dataset or your chart in anyway
Then you can find and delete (or update) :
data.datasets.find((dataset, index) => {
if (dataset.id === 'myId') {
data.datasets.splice(index, 1);
return true; // stop searching
}
});
myChart.update();
Related
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()
);
}
Lets say I have this variable
this.mylist.push([{key:{"test1":name,"test":desc,"hi i am a test":other,"none":type,"amount":0} }]);
This will be added to the array mylist
I want however on some condition to remove the current created list by deleting it through the unique key so
I want to splice the object array so the value will be removed to prevent duplicates
I tried
this.mylist.splice(this.mylist.indexOf(key), 1);
But did not work
i try to remove the array by getting the unique key which holds these child values
I also tried
this.mylist.splice(this.mylist.indexOf([{key}]), 1);
Someone who can help me out :(
CheckBox(values,values,values) {
this.bool = (this.mylist.indexOf(key) === -1);
if (this.bool) {
this.mylist.push([{key:{"key":key,"naam":naam,"beschrijving":beschrijving,"type":type,"aantal":aantal} }]);
}
else {
this.mylist.splice(this.mylist.indexOf(key), 1);
}
}
The function above is an event when a user clicks on a checkbox. When true the array must be filled with values. Else the array with the unique key must be removed to prevent duplicates
After the push statement, this.mylist will contain the array of arrays since you have pushed
[{key:{"test1":name,"test":desc,"hi i am a test":other,"none":type,"amount":0} }]
which is an array and you cannot access the array with the key (in this case you cannot access this.mylist[0], if the above array is added as first element, with this.mylist.indexOf(key))
One possible solution is you can make the type of mylist as object instead of array, then you can add elements to the Object like this
[{key:{"test1":name,"test":desc,"hi i am a test":other,"none":type,"amount":0} }];
this.mylist.key = {"test1":name,"test":desc,"hi i am a
test":other,"none":type,"amount":0} }
later you can use checkbox function like this
CheckBox(values) {
this.bool = (this.mylist.key === -1);
if (this.bool) {
this.mylist.key={"key":key,"naam":naam,"beschrijving":beschrijving,"type":type,"aantal":aantal};
} else {
delete this.mylist.key;
}
}
The logic behind Checkbox function seems incorrect, as the function will be checking if mylist contains a key, if it's not present then add the key to mylist and removes if its present. This logic does not properly handle the removing of duplicates if that's your final goal.
I have an array that looks like:
var testArr = ["40", "A1", "B9", "58"]
I want to loop over all div elements of a certain class and return only the elements where the data attribute matches ANY of the items in that array.
If I do something like this:
$("div.prodCodes").filter(function(e) {
var x1 = $(this);
var x2 = $(this).data("prodCode");
testArr.forEach(function(e) { if (e == x2) { console.log("MATCH"); } });
});
That console outputs the correct number of matches, but I cannot return those elements from the filter function.
What on earth am I missing here? I've tried creating a new array and pushing each item onto it and returning that, but it's always empty. I'm sure I'm missing something obvious here. I've also tried rewriting this using .grep() and getting nowhere. Help is appreciated.
You need to return a truthy value in filter() to have an item included.
Try :
$("div.prodCodes").filter(function(e) {
return testArr.indexOf($(this).attr('data-prodCode')) >-1;
}).doSomething();
Without a return all items will be excluded
I would use a Set for constant-time lookup.
Be aware that jQuery reads the attribute value "58" as a number when using the data method, so it won't match unless you make sure the data type is the same:
// Use a set
var testSet = new Set(["40", "A1", "B9", "58"]);
var texts = $("div.prodCodes").filter(function() {
var x = $(this).data("prodCode").toString(); // data type must match
// Return a boolean to indicate whether the div element should be kept
return testSet.has(x); // Set#has() is fast
}).map(function(){
// For demo only: get the text content of the matching div elements
return $(this).text();
}).get(); // convert that to a plain array
console.log(texts);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="prodCodes" data-prod-code="A1">Hello</div>
<div class="prodCodes" data-prod-code="XX">Not this one</div>
<div class="prodCodes" data-prod-code="58">There</div>
I want to be able to delete the value but the not the key from the local storage, any clue?
$(".delete").on("click", function() {
localStorage.removeItem('Property');
});
Try to use setItem() and set the value as null or blank like,
$(".delete").on("click", function() {
localStorage.setItem('Property',"");
});
Updated as per #qutz comment, If you have multiple items then first you need to store values in JSON format then you will be able to parse and splice to remove a single item like,
$(".delete").on("click", function() {
var ar = JSON.parse(localStorage.getItem("Property")),
item='color';// let we wants to remove color property from json array
var index = ar.indexOf(item);
if (index > -1) { // if color property found
ar.splice(index, 1);// then remove it
}
// again set the property without having color
localStorage.setItem("Property", JSON.stringify(ar));
});
Hope this will help you, may be some changes are required as per your logic and method which you've used for it.
I have a list which shows a query of words from a db, from there i can click on one word and it gets pushed to another list which i can save than. With this i can create different wordlists. What i want to do is to give the words another color if i have already pushed them on my new list.
To do so i use a function in my controller to compare the two lists with and angular.foreach. If wordFromQuery._id === wordOnNewList._id i gave the words another background color with ng-style.
Here is my code:
View
ng-repeat="word in searchWords" ng-click="addWordToSet(word)" ng-class="isInside(word)" ng-style="{ background: choosenWords.value == 'exist' ? 'lightgreen' : 'white' }"
I iterate over the words query (searchWords) and with addWordtoSet(word) i push them to my other array (this works great). isInside(word) will do the angular.foreach to compare the two arrays and the ng-style should provide different styles, according to the if-statement from the isInside function.
Controller
$scope.isInside = function (word) {
angular.forEach($scope.currentWordlist, function (item) {
if (item._id === word._id) {
$scope.choosenWords = {value: 'exist'};
} else {
$scope.choosenWords = {value: 'notOnList'};
}
});
};
The angular.forEach compares the words from both arrays. currentWordList is the array in which i push with addWordToSet
What happens is that one word on the searchword array gets the green color (and its set of by +1, so if the word in arraypos. 0 would be right the arraypos. 1 gets the green color).
I suspect that i did it all wrong with the ng-class element, but i didnt found another good opportunity to get the word._id another way. Did i do something obviously wrong here?
I would appreciate tips or hints. Thanks!
UPDATE
It works quite fine with the addWordToSet function:
$scope.addWordToSet = function (word) {
var exists = false;
angular.forEach($scope.currentWordlist, function (item) {
if (item._id === word._id) {
exists = true;
}
});
if (exists === false) {
$scope.currentWordlist.push(word);
}
};
The only thing i need i think is not doing this on click but instantly without clicking anything. is my ng-class="isInside(word)" the right choice for that?
You can assign a color to a variable inside the same function and use it in the view.
$scope.isInside = function (word) {
angular.forEach($scope.currentWordlist, function (item) {
if (item._id === word._id) {
$scope.choosenWords = {value: 'exist'};
$scope.color = 'lightgreen'
} else {
$scope.choosenWords = {value: 'notOnList'};
$scope.color = 'white'
}
});
};
ng-style="{'background-color':color}"
View:
ng-repeat="word in searchWords" ng-click="addWordToSet(word)" ng-class="isInside(word)" ng-style="{'background-color':color}" }"
Try
$scope.choosenWords.value = 'exist';
Also initialize choosenWords at the start of the controller.
If this doesn't work check the order of priority of execution of the ng modules.
Is the controller initialized through a partial?
I sat together with a friend and we came up with a working version of this problem, so here is the solution in case someone has a similar problem and hand.
In the Controller we used the following function:
$scope.isSelected = function (word) {
var found = false;
angular.forEach($scope.currentWordlist, function (item) {
if (item._id === word._id) {
found = true;
}
});
return found;
};
It uses the foreach to compare the arrays and if there are ids that are a match the found bool returns true.
In the View we used the following:
ng-class="isSelected(word) ? 'marked' : 'unmarked'"
which uses the marked or unmarked css class for, in my case, coloring the matched words in green (marked). All other words are getting the background color white.
here is the CSS:
.marked {
background: $lightgreen;
}
.unmarked {
background: $nicewhite;
}
In my case i use scss and colorvariables, but you can, of course use all other colors like red; or #fff. The result of this are two arrays that are views. The first one is a searchquery from a DB which shows all words. The second is a new array in which you can push words by clicking on one of the words. If you do so the word gets pushed AND it gets a green background. Thats it, i hope this is good information.