delete multiple selected items from the list angular js - angularjs

I am trying to delete the one or more selected items in the list in angular js.
I tried to add the items in the following way:
$scope.selectedNodes = [];
$scope.addItem = function(e) {
$scope.selectedNodes.push({
id :$scope.selectedNodes.length + 1,
Nodeid: e.item.id,
title: e.item.text});
$scope.$apply();
}
html is as below:
<select ng-model="selectedItems" multiple ng-multiple="true">
<option ng-repeat="node in selectedNodes" value="{{node}}">{{node}}</option>
</select>
<button ng-click="remove(selectedItems)" type="submit">
Remove
</button>
The above html is listing fine with all the items.
Now I am trying to delete one or more items from the list, so the code I have written is:
$scope.remove = function (nodes) {
alert(nodes); // it's giving the selected records info, no problem with it
angular.forEach(nodes, function (node) {
var index = $scope.selectedNodes.indexOf(node);
alert(index) //problem here, it's always -1
$scope.selectedNodes.splice(index, 1);
});
};
The above code is removing the last item if one item is selected. And if more than one is selected, let's say two, it's then removing the last two records.
The index value is always -1 for any no. of iterations in the foreach loop.
Could anyone please help with the above code to delete one or more selected records and the list should get refreshed. No problem with refreshing for the above code.
I tried as you mentioned below, but no luck.
$scope.remove = function (nodes) {
alert(nodes); // it's dispalying correct results
for(var i = 0; i< nodes.length; i++)
{
alert(nodes.length); // correct result, no problem
alert(nodes[i]); //correct result, no problem
alert(nodes[i].Nodeid); // problem, value Nodeid is Undefined
for (var idx = 0; idx < $scope.selectedNodes.length; idx++) {
alert($scope.selectedNodes[idx].Nodeid);
if ($scope.selectedNodes[idx].Nodeid == nodes[i].Nodeid) {
$scope.selectedNodes.splice(idx, 1);
break;
}
}
};
};

You're trying to locate the node using indexOf, which compares values using the strict '===' operator, and you're trying to compare objects.
I think it would be easier for you to use one of the many libraries outthere for collection manipulation (lodash, underscore, etc) but if you want to do it as you were doing then this is the code:
$scope.remove = function (nodes) {
angular.forEach(nodes, function (node) {
for (var idx = 0; idx < $scope.selectedNodes.length; idx++) {
if ($scope.selectedNodes[idx].Nodeid == node.Nodeid) {
$scope.selectedNodes.splice(idx, 1);
break;
}
}
});
};
If the number of nodes to delete is very high and you're concerned about optimization then you can iterate the selectedNodes array in inverse order and be deleting the node if it's in the nodesToDelete collection. The code structure is similar, just iterating outside the selectedNodes and inside the nodesToDelete.

Related

Infinite Digest Loop in AngularJS filter

I have written this custom filter for AngularJS, but when it runs, I get the infinite digest loop error. Why does this occur and how can I correct this?
angular.module("app", []).
filter('department', function(filterFilter) {
return function(items, args) {
var productMatches;
var output = [];
var count = 0;
if (args.selectedDepartment.Id !== undefined && args.option) {
for (let i = 0; i < items.length; i++) {
productMatches = items[i].products.filter(function(el) {
return el.Order__r.Department__r.Id === args.selectedDepartment.Id;
});
if (productMatches.length !== 0) {
output[count] = {};
output[count].products = productMatches;
output[count].firstProduct = items[i].firstProduct;
count++;
}
}
}
return output;
};
}).
This is the relevant HTML:
<tr class='destination' ng-repeat-start='pickupAccount in pickupAccounts | department : {"selectedDepartment": selectedDepartment, "option": displayExclusive }'>
<!-- td here -->
</tr>
displayExclusive is boolean.
I have written this custom filter for AngularJS, but when it runs, I get the infinite digest loop error.
Keep in mind that filter should return array of the same object structure. When we activate filter, it fires digest cycle that will run over our filter again. If something changed in output list - fires new digest cycle and so on. after 10 attempts it will throw us Infinite Digest Loop Exception
Testing
This empty filter will works (100%). Actually we do nothing here but return the same object that filter receives.
filter('department', function(filterFilter) {
return function(items, args) {
var output = items;
return output;
};
})
Now the main idea is: write some condition to push to output objects from input list a.e. items based on some if statement, a.e.
var output = [];
if (args.selectedDepartment.Id !== undefined && args.option) {
angular.forEach(items, function(item) {
if(<SOME CONDITION>) {
output.push(item);
}
});
}
By this way it will work too.
our case:
we have this logic:
productMatches = items[i].products.filter(function(el) {
return el.Order__r.Department__r.Id === args.selectedDepartment.Id;
});
if (productMatches.length !== 0) {
output[count] = {};
output[count].products = productMatches;
output[count].firstProduct = items[i].firstProduct;
count++;
}
Here we completely modified object that has been stored in output.
So next digest cycle our items will change again and again.
Conclusion
The main purpose of filter is to filter list and not modify list object content.
Above mentioned logic you wrote is related to data manipulation and not filter. The department filter returns the same length of items.
To achieve your goal, you can use lodash map or underscorejs map for example.
This happens when you manipulate the returned array in a way that it does not match the original array. See for example:
.filter("department", function() {
return function(items, args) {
var output = [];
for (var i = 0; i < items.length; i++) {
output[i] = {};
output[i] = items[i]; // if you don't do this, the next filter will fail
output[i].product = items[i];
}
return output;
}
}
You can see it happening in the following simplified jsfiddle: https://jsfiddle.net/u873kevp/1/
If the returned array does have the same 'structure' as the input array, it will cause these errors.
It should work in your case by just assigning the original item to the returned item:
if (productMatches.length !== 0) {
output[count] = items[i]; // do this
output[count].products = productMatches;
output[count].firstProduct = items[i].firstProduct;
count++;
}
output[count] = {};
Above line is the main problem. You create a new instance, and ng-repeat will detect that the model is constantly changed indefinitely. (while you think that nothing is changed from the UI perspective)
To avoid the issue, basically you need to ensure that each element in the model remains the 'same', i.e.
firstCallOutput[0] == secondCallOutput[0]
&& firstCallOutput[1] == secondCallOutput[1]
&& firstCallOutput[2] == secondCallOutput[2]
...
This equality should be maintained as long as you don't change the model, thus ng-repeat will not 'wrongly' think that the model has been changed.
Please note that two new instances is not equal, i.e. {} != {}

$localStorage is not adding new items with the old items

I want to add new items into $localStorage plus old items. But, here in my code $localStorage loosing all previous items. My code is as follows,
$scope.cart = [];
$scope.cart = productService.getSharedProduct();
if ($scope.cart != 0) {
$localStorage.items = $scope.cart;
}
else {
$scope.cart = $localStorage.items;
}
Yes because you are not adding items to your $localStorage.items but you are assigning new values to it every time and so it lost the last added values.
You are re-initializing them every time and so they loose last added values.
You should do something like this
if ($scope.cart != 0) {
// instead of this
//$localStorage.items = $scope.cart;
// you should do this
for(var i=0; i<$scope.cart.length; i++)
{
$localStorage.items.push($scope.cart[i]);
}
}

How can I use "ui-scroll" with my own data?

I'm trying to create a infinite scroll feature in my application but it feels a bit abstract. I want to use ui-scroll and this fiddle shows a simple example of how it works.
I've read the readme and looked through some examples also I've integrated the example in my project and got it working, but I can't figure out on how to combine this with data from my own database.
I have a database table called movies. The movies have a few values such as title, release_date, image_url
How would I insert that data into the $scope.movieDataSource so I can use it in my view?
$http.get(('/movies.json'), {
cache: true
})
.success(function(data, status, headers, config) {
if (status == 200) {
$scope.userMovies = data;
} else {
console.error('Error happened while getting the user list.')
}
$scope.movieDataSource = {
get: function(index, count, callback) {
var i, items = [$scope.userMovies], item;
var min = 1;
var max = 1000;
for (i = index; i < index + count; i++) {
if (i < min || i > max) {
continue;
}
item = {
title: $scope.userMovies.title,
imageURL: $scope.userMovies.poster_path
};
items.push(item);
}
callback(items);
}
}
});
I've tried to create an example of what I'm trying to get at. I use a http.get to fill my userMovies scope with records from my database and I want to use those records as items in the movieDataSource.
But when I visit the page I that ui-scroll does add results in the container, but it does not show content.
<div class="imageCell ng-scope" ui-scroll="item in movieDataSource">
<img title="">
</div>
If I console.log("movieDataSource" + $scope.movieDataSource) it shows me movieDataSource[object Object].
You are making this more complex than necessary. The uiScroll directive is a replacement for ngRepeat, which takes a Data Source with 3 properties:
index indicates the first data row requested
count indicates number of data rows requested
success function to call when the data are retrieved. The implementation of the service has to call this function when the data are retrieved and pass it an array of the items retrieved. If no items are retrieved, an empty array has to be passed.
in your case, you have an array of items. Each time the index or count changes, the success fires, and this function should return a subset of your array from index to index + count. There are multiple ways to accomplish this. The example you posted uses a for loop to iteratively push items into the array. You could also use the Array.slice() method.
Option 1:
$scope.movieDataSource = {
get: function(index, count, callback) {
var i, items = [], item;
for (i = index; i < index + count; i++) {
item = $scope.userMovies[i];
items.push(item);
};
callback(items);
}
}
Option 2:
$scope.movieDataSource = {
get: function(index, count, callback) {
var items = $scope.userMovies.slice(index, index + count);
callback(items);
}
}
As for your HTML, it should be identical to if you were using ng-repeat:
<div ui-scroll="item in movieDataSource">
{{item.title}}
<img title="{{item.title}}" ng-src="{{item.poster_path}}"></img>
</div>
Apparently ui-scroll calls the given object "movieDataSource" with an index and a count. It then expects the function to push all items between index and index + count in the returned array.
That means that you have to implement code that fetches the respective items from your DB (via REST or however you access your data) and insert the returned records in the items array.

Angularjs array changes reference after splice

I am using angularjs for my project and its working fine. Today I am facing little but different problem
so just need some kind of suggestion.
Consider code :
$scope.deleteEmpty = function(){
for(var sp=0;sp < $scope.column.length;sp++){
var offerings = $scope.column[sp];
if(offerings.spName == -1){
$scope.removeanswer(sp);
}
}
},
$scope.removeanswer = function(position){
for(var question=1;question<$scope.rows.length;question++){
$scope.rows[question].answerlst.splice(position, 1);
}
},
here i am deleting answerlst according to offerings position, it is getting deleted but the there is one problem. Consider one example:
$scope.column = [{'spName':'-1',spId:'1'},{'spName':'-1',spId:'2'},{'spName':'-1',spId:'3'},{'spName':'-1',spId:'4'}]
when first time call to $scope.removeanswer(sp); it delete answerlst for first column but after that the position of answerlst get changed. So it deletes for 1st and 3rd position and not for the whole.
Any suggestion please.
Thanks.
The following solution could be improved if there was more information.
The idea is to store the column indexes in a toRemove array and then remove them all at once from the answerlst of each question.
$scope.deleteEmpty = function(){
var toRemove = []; // column indexes to be removed
for(var sp=0;sp < $scope.column.length;sp++){
var offerings = $scope.column[sp];
if(offerings.spName == -1){
toRemove.push(sp);
}
}
$scope.removeanswer(toRemove);
},
$scope.removeanswer = function(positions){
for(var question=1;question<$scope.rows.length;question++){
$scope.rows[question].answerlst = $scope.rows[question].answerlst.filter(function(value, index) {
return positions.indexOf(index) < 0;
});
}
},

Testing Angular Filter That Returns An Array with Jasmine

So, I'm having issues testing an angular filter that takes an array that has previously been sorted by a group property. It uses a flag property to indicate that the item is the first observation of that group, and then false for subsequent observations.
I'm doing this to have a category header in the UI with an ng-repeat directive.
When I test the filter, the output does not return the array with the flags unless I create new objects for the return array. This is a problem, because it causes an infinite loop when running in a webpage. The code works in the webpage when it just adds a flag property to the input object.
Is there some additional step I should be taking to simulate how angular handles filters so that it outputs the proper array?
This is what my test looks like right now.
describe('IsDifferentGroup', function() {
var list, itemOne, itemTwo, itemThree;
beforeEach(module("App.Filters"));
beforeEach(function () {
list = [];
itemOne = new ListItem();
itemTwo = new ListItem();
itemThree = new ListItem();
itemOne.group = "A";
itemTwo.group = "B";
itemThree.group = "C";
list.push(itemOne);
list.push(itemOne);
list.push(itemOne);
list.push(itemOne);
list.push(itemTwo);
list.push(itemThree);
list.push(itemThree);
list.push(itemThree);
list.push(itemThree);
list.push(itemThree);
});
it('should flag the items true that appear first on the list.', (inject(function (isDifferentGroupFilter) {
expect(list.length).toBe(10);
var result = isDifferentGroupFilter(list);
expect(result[0].isDifferentGroup).toBeTruthy();
expect(result[1].isDifferentGroup).toBeFalsy();
expect(result[4].isDifferentGroup).toBeTruthy();
expect(result[5].isDifferentGroup).toBeTruthy();
expect(result[6].isDifferentGroup).toBeFalsy();
expect(result[9].isDifferentGroup).toBeFalsy();
})));
});
And here is something like the code with the filter:
var IsDifferentGroup = (function () {
function IsDifferentGroup() {
return (function (list) {
var arrayToReturn = [];
var lastGroup = null;
for (var i = 0; i < list.length; i++) {
if (list[i].group != lastGroup) {
list[i].isDifferentGroup = true;
lastAisle = list[i].group;
} else {
list[i].isDifferentGroup = false;
}
arrayToReturn.push(list[i]);
}
return arrayToReturn;
});
}
return IsDifferentGroup;
})();
Thanks!
I figured out my issue.
When I was passing the items into the list, I just pushed a pointer to an item multiple times. I was not passing in unique objects so the flag was being overridden by the following flag in the array(I think). So, I just newed up 10 unique objects using a loop, pushed them into the array and ran it through the filter. And it worked.
I'm not entirely sure my analysis is correct about the override, because itemTwo was not being flagged as unique when it was the only itemTwo in the array. But the test is working as I would expect now so I'm going to stop investigating the issue.

Resources