AngularJS .destroy() shows console error, but works? - angularjs

I use destroy() function something like this:
$scope.Cities[i].destroy();
Then when I use it my app works fine, but the console says:
$scope.Cities[i] is undefined
However without it, it doesn't work. Should I ignore the error?
MORE CODE
$scope.Somefunction= function (id) {
for (var i = 0; i < $scope.Cities.length; i++) {
if ($scope.Cities[i] == id) {
$scope.SpliceCities(i);
$scope.Cities[i].destroy();
}
}
$scope.SpliceCities = function(i) {
$scope.Cities.splice(i, 1);
};
}
Function is called on ng-click on country.

splice mutates the array, so the i index points to another element when calling destroy(). If i pointed to the last element before the splice, you get this error. Fortunately splice also returns the elements that were spliced out as an array, so try this:
$scope.Somefunction = function (id) {
for (var i = 0; i < $scope.Cities.length; i++) {
if ($scope.Cities[i].id == id) {
var spliced = $scope.Cities.splice(i, 1);
spliced[0].destroy();
break;
}
}
}

Related

Code works in Plunkr but not when outputting from my editor

I have made a custom filter to filter a list with multiple criteria on one column.
I have made this plunk
var app = angular.module('main', ['angular.filter'])
.filter('filterIns', function() {
var obj = {};
return function(list, ins) {
var out = [];
for (var i = 0; i < list.length; i++) {
if (ins.indexOf(list[i].instrument) > -1) {
obj = list[i];
out.push(obj);
}
}
if (ins.length === 0) {
out = list;
}
return out;
};
})
[...]
Everything is working as expected. However when I copy this code to my localhost environment I get this nasty, (general?) error: 'undefined is not an object (evaluating 'list.length')' referring to line 11.
Why is it working perfectly in Plunkr but not on localhost?
Can anybody tell me?
Is your list loaded async or something? Clearly it is undefined when the filter runs for the first time. So just place a condition before line 11, like:
if (!list) { return []; }
// or
if (!list) { return undefined; }
// or even better
if (!list) { return list; } // this keeps things like 0, undefined or null intact
and you should be safe.

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. {} != {}

Return promise inside the for loop

I have a logic like below,
getSpecificCell: function(tableObject, rowText, columnCss) {
var ele = element.all(by.repeater(tableObject)).count().then(function(count) {
for (var i = 0; i < count; i++) {
return element(by.repeater(tableObject).row(i)).getText().then(function(txt) {
if (txt.indexOf(rowText) !== -1) {
return element(by.repeater(tableObject).row(i)).element(by.css('[' + columnCss + ']'));
}
});
}
});
return ele;
}
But it is returning the value in first iteration itself.
Is that possible to return the promise inside this kind of for loop or do we have any other solution for this?
First, you don't need to use for loops with an ElementArrayFinder. That's what the each() method is for.
Second, you shouldn't need to loop at all. It sounds like you should be using filter() to get the table cells that match your specification, though I'm not sure what exactly you're trying to accomplish.
var table = element.all(by.repeater(tableObject));
// list is an ElementArrayFinder of all elements that matched the filter
var list = table.filter(function (elem) {
return elem.getText().then(function (text) {
return txt.indexOf(rowText) !== -1
})
});
// do something with list
list.count().then(function (count) {
console.log(count);
});

Use an array of values for a slider

I want to use an array of id, for my little slider-viewer I'm building. I just want to display multiple things using 2 buttons next and previous.
For now I use something like this, using only 2 url id, but I need at least 20 urlid in my viewer :
var urlid1 = 'd3885ca76ac54e958c2855a4fbd3dbf3';
var urlid2 = '3aa64527d1614998b4812bfefbbc896a';
function Next() {
client.init( urlid2 );
}
function Previous() {
client.init( urlid1 ); }
So I've never use an array before, and the things I tried didn't work. Do you know a proper way to do that ?
This seems straight forward enough
var index = 0;
var array = ["url1", "url2", "url3", ...]
function next() {
index++;
if(index > array.length) {
index = 0;
}
client.init(array[index]);
}
function previous() {
index--;
if(index < 0) {
index = array.length;
}
client.init(array[index]);
}
It may be better practice to actually refactor those changes to the index variable to other functions to enable reuse, but this should give you an idea of how it is done.
If client is something that you have wrote then you might want to look at the naming of your functions, init is normally reserved for instantiating the class as new, from your example you seem to be changing a resource so you may want a function name like client.show(); or client.update();
Thank's Varedis.
Final code is :
var index = 0;
var array = ["0e4b4e0789bf41c7b05129a76de0abb0","3aa64527d1614998b4812bfefbbc896a","d3885ca76ac54e958c2855a4fbd3dbf3","8f927023e10c40b9b22d3c13df7c08aa"];
client.init(array[index]);
function next() {
index++;
if(index >= array.length) {
index = 0;
}
client.init(array[index]);
}
function previous() {
index--;
if(index < 0 ) {
index = array.length-1;
}
client.init(array[index]);
}
I had to use " index = array.length-1; " but I can't explain it. You know why ? How index could be 5 if I only have 4 values ?

The first element of an Object Array is passed as undefined when using JSON.parse

I'm using HTML5's local storage to save a small database of user's preferences. To do that I have the following function:
function save(UserDB) {
if (window.localStorage) { // Only do this if the browser supports it
localStorage.setObject('UserDataBase', JSON.stringify(UserDB));
}
with
Storage.prototype.setObject = function(key, value) {
this.setItem(key, JSON.stringify(value));
}
UserDB is an array of objects similar to this:
[{"uid":"ABC","groupname":"My Group","calendarid":"sdf44d7g3ak5q8ifdrl308hk0#group.calendar.google.com","link":null,"userenable":true,"color":"528800"},{"uid":"CdO","groupname":"CKHO","calendarid":"apkrty45sdfer44fd1mr55dfghfg8#group.calendar.google.com","link":null,"userenable":true,"color":"AB8B00"}]
This seems to work just fine.
Then, when the user loads the site again I'd want to be able to regenerate the object array in a variable from the information stored in the previous session:
Storage.prototype.getObject = function(key) {
var value = JSON.parse(this.getItem(key));
return JSON.parse(value);
}
This also seems to work. The problem is when I use this last function to actually store the array:
function ApplyUserConfiguration(Data) {
if (window.localStorage) {
var UserDBx = localStorage.getObject('UserDataBase');
console.log("User Configuraiton found");
console.log(UserDBx);
console.log("ActualGetObject");
console.log(localStorage.getObject('UserDataBase'));
console.log(UserDBx.length);
for (var i = 0; i < Data.length; i++){
for (var j=0; j< 5; j++){
if(Data[i].uid == UserDBx[j].uid){
Data[i].userenable = UserDBx[j].userenable;
Data[i].Color = UserDBx[j].color;
delete UserDBx[j];
break;
}
}
}
}
return Data;
};
It turned out that console.log(localStorage.getObject('UserDataBase')); returns the object array correctly but console.log(UserDBx); returns the object array with the first element as "undefined". Any idea why this happens?
I faced the same issue of seeing undefined element in start. Following was my code:
var x;
for (i = 0; i < content.length; i++) {
x += content[i].name;
console.log(x);
}
The reason was undefined x. I was declaring it but I was not assigning it any value. So I just changed var x to var x="" and it solved the problem.
Error free code
var x="";
for (i = 0; i < content.length; i++) {
x += content[i].name;
console.log(x);
}

Resources