Strange behavior when splicing array, javascript - javascript

I'm working with list of checkboxes and I have next logic behavior for it:
if all items selected, checkbox "select all" is checked
if one of all selected items has been unchecked, checkbox "select all" is unchecked as well
This logic is clear. Depends of what item is checked I extract its id to an additional array and then using this array for request that to get data.
For pushing everything works fine, but for slicing the logic is strange. So I can slice the array until first item is checked, however I unchecked the first item, pushed and sliced items no more related with checkboxes.
I have reproduced plunker with it, so I appreciate if anybody could help me to find what I'm missing.
$scope.modelClass = {
selectedAll: false
};
$scope.selectAllClass = function (array) {
angular.forEach(array, function (item) {
item.selected = $scope.modelClass.selectedAll;
$scope.param =''
});
};
$scope.checkIfAllClassSelected = function (array) {
$scope.modelClass.selectedAll = array.every(function (item) {
return item.selected == true
});
$scope.checked = array.filter(function (item) {
return item.selected == true
}).length;
angular.forEach(array, function (obj) {
if(obj.selected == true){
requestClass(obj)
}
});
};
var selectedClass = [];
var requestClass = function (obj) {
selectedClass.push(obj);
angular.forEach(selectedClass, function (val) {
if (val.selected != true) {
selectedClass.splice(selectedClass.indexOf(val.id), 1);
}
else {
selectedClass = selectedClass.filter(function (elem, index, self) {
return index == self.indexOf(elem);
})
}
});
$scope.param = _.map(selectedClass, 'id')
};
$scope.classes = [
{"id":4,"name":"Achievement","selected":false},
{"id":13,"name":"Information","selected":false},
{"id":6,"name":"Issue","selected":false},
{"id":5,"name":"Message","selected":false},
{"id":9,"name":"Request","selected":false}
]

The logic looks good for me, not sure what's wrong here. I've took the first solution from this post (it looks like you are using the second one) and slightly modified it for your needs.
$scope.model = {
selectedClass : []
}
$scope.isSelectAll = function(){
$scope.model.selectedClass = [];
if($scope.master){
$scope.master = true;
for(var i=0;i<$scope.classes.length;i++){
$scope.model.selectedClass.push($scope.classes[i].id);
}
}
else{
$scope.master = false;
}
angular.forEach($scope.classes, function (item) {
item.selected = $scope.master;
});
$scope.param = $scope.model.selectedClass
}
$scope.isChecked = function() {
var id = this.item.id;
if(this.item.selected){
$scope.model.selectedClass.push(id);
if($scope.model.selectedClass.length == $scope.classes.length ){$scope.master = true;
}
} else {
$scope.master = false;
var index = $scope.model.selectedClass.indexOf(id);
$scope.model.selectedClass.splice(index, 1);
}
$scope.param = $scope.model.selectedClass
}
$scope.classes = [
{"id":4,"name":"Achievement","selected":false},
{"id":13,"name":"Information","selected":false},
{"id":6,"name":"Issue","selected":false},
{"id":5,"name":"Message","selected":false},
{"id":9,"name":"Request","selected":false}
]
html
<div ng-class="{'selected': master, 'default': !master}">
<div>
<input type="checkbox" ng-model="master" ng-change="isSelectAll()" > Select all
</div>
</div>
<div ng-repeat="item in classes | orderBy : 'id'" ng-class="{'selected': item.selected, 'default': !item.selected}">
<div >
<input type="checkbox" ng-model="item.selected" ng-change="isChecked()">
{{ item.name }}
</div>
</div>
this is fixed plunker

Related

Angular List color change based ng-click

I am using angularjs I have two list when I click first one I will push the value into another scope and bind the value to second list. Now my requirement is when first list values which are moved to second list, I need to change the color of moved values in list1
Here I attached my fiddle
Fiddle
You can use findIndex and ng-class together to check if the second list contains the same item as first. If present apply css class to the first list item.
JS:
$scope.checkColor = function(text) {
var index = $scope.linesTwos.findIndex(x => x.text === text);
if (index > -1) return true;
else return false;
}
HTML:
<li ng-click="Team($index,line.text)" ng-class="{'change-color':checkColor(line.text)}">{{line.text}}</li>
Working Demo: https://jsfiddle.net/7MhLd/2659/
You can do something like this:
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.lines = [{
text: 'res1'
},
{
text: 'res2'
},
{
text: 'res3'
}
];
$scope.linesTwos = [];
$scope.Team = function(index, text) {
var obj = {};
obj.text = text;
$scope.linesTwos.push(obj)
}
$scope.Team2 = function(index, text2) {
$scope.linesTwos.splice(index, 1)
}
$scope.containsObj = function(obj, list) {
var i;
for (i = 0; i < list.length; i++) {
if (angular.equals(list[i], obj)) {
return true;
}
}
return false;
};
}
.clicked {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<ul ng-repeat="line in lines">
<li ng-class="{'clicked': containsObj(line,linesTwos)}" ng-click="Team($index,line.text)">{{line.text}}</li>
</ul>
<ul>
<li>__________</li>
</ul>
<ul ng-repeat="line in linesTwos">
<li ng-click="Team2($index,line.text)">{{line.text}}</li>
</ul>
</div>
you have to achieve it using ng-class and create a dynamic class style for pushed data please check my working example fiddle
JS fiddle sample
in HTML nedd to do these changes
<li ng-click="Team($index,line.text,line)" ng-class="{'pushed':line.pushed}">
<li ng-click="Team2($index,line.text,line)">
In css
.pushed{color:red;}
In Controller
`$scope.Team=function(index,text,line){
var obj={};
obj = line;
$scope.linesTwos.push(obj)
line.pushed = true;
}`
`scope.Team2 = function(index,text2,line){
$scope.linesTwos.splice(index,1)
line.pushed = false;
}
`
its because angular two way binding

Select checkbox on load

After I load all topics from the database I need to select the ones for specific user. How can I do that?
<div ng-repeat="item in topicEditItems">
<md-checkbox ng-checked="existsEditTopic(item, topicEditSelected);" ng-click="toggleEditTopic(item, topicEditSelected);" value="{{item.TopicID}}">
{{item.TopicName}}
</md-checkbox>
</div>
$scope.topicEditSelected = [];
adminService.getTopics().then(function (response) {
$scope.topicEditItems = $.parseJSON(response.data);
//Select topics
})
$scope.toggleEditTopic = function (item, list) {
var idx = list.indexOf(item);
if (idx > -1) {
list.splice(idx, 1);
}
else {
list.push(item);
}
};
$scope.existsEditTopic = function (item, list) {
return list.indexOf(item) > -1;
};
The easiest way is having 2 checkbox mimic to the original 1 then show and hide based on your filter. You may need to apply md-checkbox styling into the fake one
<input type="checkbox" class="check" aria-label="Cb user"
ng-model="cbFake" ng-if="isTopicExist(item.id)" ng-checked="isTopicExist(item.id)"/>
<md-checkbox ng-checked="exists(item, selected)" ng-click="toggle(item, selected)" aria-label="Cb user"
ng-model="cbOri" ng-if="!isTopicExist(item.id)"> </md-checkbox>
update $scope.existsEditTopic to
$scope.existsEditTopic = function (item, list) {
var index = list.indexOf(item);
if(index > -1){
return true;
}else{
return false;
}
};

Adding and removing items from arrays, based on a condition in AngularJS

I'm trying to make a dynamic "favorites" toggle-button.
When clicking the button, it should add the selected player to the users favorite-list. If the player is already favorited, it should remove it.
I've also tried to iterate through the favorites, to check if a player is already favorited. If true, it colors the favorite star gold.
A few problems here. My for loop for checking seems to be working properly as long as the array only contains one item. But as soon as I try adding more, the gold icon is only gold colored on the last player added to favorites. So the check only finds one favorite at a time, and I can add a player to favorites many times, as long as I vary the players I add.
If someone could point me in the right direction and help me understand why my loop isn't working correctly, that would be awesome!
http://codepen.io/utrolig/pen/LNgRwv
Javascript
angular.module('test', [])
.controller('TestController', function($scope){
$scope.players = [
{
uniqueid: "gem",
name: "Ole Christian",
cake: false,
},{
uniqueid: "utrolig",
name: "Stian",
cake: false,
},{
uniqueid: "drozo",
name: "Adrian",
cake: false,
}
];
$scope.user = {
name: "Stian",
username: "stiba",
favorites: [{uniqueid: "drozo"}],
}
$scope.checkFavorite = function(id){
fav = $scope.user.favorites;
var exists;
for (var i=0; i < fav.length; i++){
if(fav[i].uniqueid == id){
exists = true;
} else {
exists = false;
}
}
return exists;
}
$scope.toggleFavorite = function(id){
fav = $scope.user.favorites;
if(fav.length === 0){
var newfav = {uniqueid: id};
fav.push(newfav);
} else {
if($scope.checkFavorite(id) === true){
for(var i = 0; i < fav.length; i++){
if (fav[i].uniqueid === id) fav.splice(i, 1);
}
} else if ($scope.checkFavorite(id) === false) {
var newfav = {uniqueid: id};
fav.push(newfav)
} else {
console.log('Error!');
}
}
}
$scope.isFavorited = function(){
return true;
};
})
HTML
<body ng-app="test">
<div class="container" ng-controller="TestController">
<h3>Players</h3>
<div ng-repeat="player in players" class="player-cont">
<div class="player">
<div class="favorite" ng-click="toggleFavorite(player.uniqueid)" ng-class="{'active': checkFavorite(player.uniqueid)}">
<i class="material-icons">star</i>
</div>
<i class="material-icons player-icon">person</i>
</div>
<div>
<p ng-bind="player.uniqueid"></p>
<p ng-bind="player.name"></p>
</div>
</div>
<h3>Favorites</h3>
<div ng-repeat="favorite in user.favorites track by $index">
<h5>{{favorite.uniqueid}}</h5>
</div>
<p class="user">
{{user.favorites}}
</p>
</div>
</body>
There's a couple of errors in your code.
The first is checkFavorite, if you examine the code you'll see that only the last item is actually compared to id, since the exists flag is updated for each item. You need to "short circuit" the loop and return true as soon as you find a value.
btw, is* is a common name convention for checking boolean values.
$scope.isFavorite = function(id){
var fav = $scope.user.favorites;
for (var i=0; i < fav.length; i++){
if(fav[i].uniqueid == id){
return true;
}
}
return false;
}
Your toggle is also very verbose, if you "reduce" the code you end up with something like this
$scope.toggleFavorite = function(id){
var fav = $scope.user.favorites;
// no previous items, remove, OK
if(fav.length === 0) {
var newfav = {uniqueid: id};
fav.push(newfav);
return;
}
// if already a favorite, uncheck/remove
if($scope.isFavorite(id)) {
for(var i = 0; i < fav.length; i++){
if (fav[i].uniqueid === id) fav.splice(i, 1);
}
}
// otherwise add the item
// remember, isFavorite returns true of false, there is no third state
else { // if ($scope.isFavorite(id) === false) {
var newfav = {uniqueid: id};
fav.push(newfav)
}
}
This can be edited further, since the isFavorite function will return false if the list is empty, i.e. no need for the first if
$scope.toggleFavorite = function(id){
var fav = $scope.user.favorites;
// if already a favorite, uncheck/remove
if($scope.isFavorite(id)) {
for(var i = 0; i < fav.length; i++){
if (fav[i].uniqueid === id) {
fav.splice(i, 1);
// unless the item exists more than once, break the loop
break;
}
}
}
// otherwise add the item
else {
var newfav = {uniqueid: id};
fav.push(newfav)
}
}

Angularjs checkbox value on edit profile page

I am new to angularjs and getting some problem on checkbox values on edit profile page.
I have an array for checkbox
$scope.checkBoxes = [
{id:'1', value:'Reading'},
{id:'2', value:'Cooking'},
{id:'3', value:'Dancing'},
{id:'4', value:'Singing'}
];
and I have used ng-repeat for showing checkbox
<div ng-repeat="checkBox in checkBoxes" style="display:inline;">
<input type="checkbox" ng-model="checkBox.selected" id={{checkBox.id}}' ng-checked="checkItem(checkBox.id)" ng-change="listActionsHandler(checkBoxes)">{{ checkBox.value }}
</div>
This is my ng-change function:
$scope.listActionsHandler = function(list) {
var has = [];
angular.forEach(list, function(item) {
if ( angular.isDefined(item.selected) && item.selected === true ) {
has.push(item.id);
}
});
formData['hobby'] = has;
}
and this is my ng-checked function that is used for showing checkbox checked according to database saved value.
var arr_checked_items = data.data.hobby;
var arraySize = arr_checked_items.length;
$scope.checkItem = function (id) {
var checked = false;
for(var i=0; i<= arraySize; i++) {
if(id == arr_checked_items[i]) {
checked = true;
}
}
return checked;
};
These function work properly on add and edit page, but problem is that when I doesn't apply any changes on checkbox and click on submit button it take blank value.
My question is that: if I does not apply any changes, it should take old value of checkbox and if I apply any changes, it should take new value of checkbox.
Please try with below code. I am not sure weather it is working or not.
$scope.checkItem = function (id) {
var checked = "";
for(var i=0; i<= arraySize; i++) {
if(id == arr_checked_items[i]) {
checked = "checked";
}
}
return checked;
};

remove value from the list

I have a list of checkboxes. Upon clicking on each of the checkboxes i am adding the value to the hidden variable. But the question is if I want to remove the value from the list upon unchecking the checkbox . How this piece cab be done
here is the hidden form variable
<input name="IDList[]" type="hidden" id="IDList" value="" />
and the jquery
$(".myCheckboxClass").change(function() {
var output = 0;
$(".myCheckboxClass").change(function() {
if ($(this).is(":checked")) {
output += ", " + $(this).val();
} else {
output = $.grep(output, function(value) {
return value != $(this).val();
});
}
$("#IDList").val(output);
});
});
Something like this: (demo) http://jsfiddle.net/wesbos/5N2kb/1/
we use an object called vals to store the info. ADding and removing as we check/uncheck.
var vals = {};
$('input[type=checkbox]').click(function() {
var that = $(this);
if (that.is(':checked')) {
console.log(this.name);
vals[this.name] = "In your Object";
}
else {
delete vals[this.name];
}
console.log(vals);
});
Following your logic, you could do this:
$('#IDList').data('value', []);
$(".myCheckboxClass").change(function() {
var list = $('#IDList').data('value');
if ($(this).is(":checked")) {
list.push($(this).val());
} else {
var indexToRemove = list.indexOf($(this).val());
list.splice(indexToRemove, 1);
}
$('#IDList').val(list);
});
But if you only care about the value of #IDList upon data submission or other actions, you probably want to consider an alternative approach: collating the checked values when you need them.
$('#form').submit(function() {
var list = $('input.myCheckboxClass:checked', this).map(function() {
return $(this).val();
}).get();
$('#IDList').val(list);
});
See both of the above in action: http://jsfiddle.net/william/F6gVg/1/.

Categories

Resources