I have been working on an AngularJS project and have come across this issue before. I have the following array in my controller like so:
$scope.items = [];
I then fill this array with objects from a call to our REST API:
$http.get(API_URL_HERE).then(onSuccess);
The onSuccess method is called and the objects fill the array. I now have:
console.log($scope.items) resulting in [Object, Object, Object, ...]
Now when I use the $scope.items in an AngularJS ng-repeat loop it works great, as it should BUT if I try to remove an element from the $scope.items array, it always seems to remove the element BUT replaces it with an undefined value and this undefined value is interpreted by AngularJS in the ng-repeat loop and outputs an empty row in the template with no data. This causes issues if using ng-show / ng-hide for example as even though you have no real objects in the array, it still sees it as full because it is full of [undefined, undefined, undefined ...].
This is causing major headaches and I have seen other people with same issue but the fixes don't seem to work well.
The solution I have found to this, that seems to work well is below. Please also note that some people have said this is just a pure Javascript issue, well I don't think it is, when I tried it within AngularJS and pure Javascript, I got 2 different results with the same setup and data.
For example I have a $scope.remove method which handles removing items from the $scope.items array.
When I do the following code:
$scope.items.splice(index, 1);
It results in this console.log output [Object, Object, undefined, Object, ...].
However, by doing the following code:
$scope.items.splice(index, 1);
$scope.items.pop();
Results in the following output from console.log [Object, Object, Object] and my ng-repeat loop updates as it should without the empty rows.
My solutions seems to work fine but please do let me know if you find anything wrong with it. This code certainly looks cleaner than some of the others I have spotted on different sites and is working across all browsers I have tested.
UPDATE
My onSuccess method looks like this:
var onSuccess = function(response){
$scope.items = response.data.items;
//results in [Object, Object, Object, ...]
};
My $scope.remove method looks like this:
$scope.remove = function(index){
$scope.items.splice(index, 1);
//results in [Object, Object, undefined, Object, ...]
//add the following code
$scope.items.pop();
//results in [Object, Object, Object, ...] the undefined has gone
};
And only when adding in the pop method does it work as it should.
Related
So I want to take my list and add the first two (0 & 1) items together.
I tried this:
total=foo.shift();
foo[0]=total
And got this:
Uncaught TypeError: Object [object Array] has no method 'split'
I then tried to simply call "foo" and output it; same error.
This leads me to think that it is not in a form I can work with without some workaround.
Any help would be much appreciated. Thank you.
this is a working code that can help you:
//we initialize an array with two objects
var arr=[{'id':1,'nom':'ali'},{'id':2,'nom':'ahmed'}];
console.log(arr);
//array with two objects displayed in the console: [Object, Object]
//we call shift method
arr.shift();
console.log(arr);
//array with one object displayed in the console: [Object]
I have an array of objects which are presented to the user as blocks. They can drag and drop to change the order that that blocks appear, which I then want to change the position of the objects in the array.
$scope.myArray = [{a:1,b:2,c:3}, {a:11,b:22,c:33}, {a:111,b:222,c:333}];
function orderChanged(event) {
console.log($scope.myArray);
//logs [{a:1,b:2,c:3}, {a:11,b:22,c:33}, {a:111,b:222,c:333}]
console.log("source:", event.source.index, "dest:", event.dest.index);
//logs source: 1 dest: 2
$scope.myArray.move(event.source.index, event.dest.index);
console.log($scope.myArray);
//logs [{a:1,b:2,c:3}, {a:11,b:22,c:33}, {a:111,b:222,c:333}]
};
//this is going to rearrange the array
Array.prototype.move = function (from, to) {
this.splice(to, 0, this.splice(from, 1)[0]);
};
The orderChange event has the source index and destination index as integers that represent their order as present to the user, which also maps to their positions in the array before any moving has occurred.
I cannot get the array to rearrange, each time I log the array in the orderChange function both logs return the same order.
All of the other examples of array re-ordering are for arrays that do not contain objects, I'm wondering if this is what is messing up my code?
I'm testing your code and works ok. Where are you modifying the Array prototype? Just try to replace the call to move() with the code that actually does the reorder and test...
Anyway, you're using AngularJS. Why are you messing with the DOM? Add a property called Order to each of your objects and let Angular do the syncrhonisation... that what is meant for.
In short, take a look at this module, maybe it would do your life easier:
http://ngmodules.org/modules/ng-sortable
I think, your code works ok, but the log does not.
console.log might not represent the values at runtime, but at viewtime, especially with multidimensional objects and arrays.
Try a different log to see console.log($scope[0].a, $scope[1].a, $scope[2].a)
You might want to check in a different brwoser, as this seems to be a Chrome issue, see here:
Is Chrome's JavaScript console lazy about evaluating arrays?
Wrong value in console.log
My javascript/angular code is performing contrary to what i expect and i can't figure out why. I have an angular service defining two methods for getting and setting items
app.factory('orderItems',['Restangular','$mdDialog',function(Restangular,$mdDialog){
var orderItems = [];
return{
getOrderItems: function(){
return orderItems
},
setOrderItems: function(item,fillings){
...
orderItems.push(item)
...
}, ...
Items are set using ng-click and passing an item and checked addons;
product_template.html
<div >
<md-button class="md-raised" ng-click="showAddons($event,product)">Add to Cart</md-button>
</div>
controller
app.controller('productCtrl',['$scope','getProducts','orderItems','Restangular','$mdDialog',function($scope,getProducts,orderItems,Restangular,$mdDialog){
//$scope.products;
$scope.products = getProducts(Restangular);
// Dialog
$scope.showAddons=function(ev,product){
$mdDialog.show({
locals:{current_Product: product,},
//inline controller
controller:['$scope','current_Product','orderItems',function($scope,current_Product,orderItems){
$scope.product = current_Product;
$scope.addons = {
checked:[],}
...
}],
templateUrl: 'dialog.html',
targetEvent: ev,
})
}
}])
dialog.html
<md-list-item ng-repeat="addon in product.productAddons">
...
<md-checkbox class="md-secondary" checklist-model="addons.checked" checklist-value="addon"></md-checkbox>
</md-list-item>
<md-button class="md-raised" style="background-color:#f44336; color:#fff" ng-click="setOrderItems(product,addons.checked)">Done</md-button>
The problem arises when setOrderItems is passed a similar object more than once. The first time around, orderItems.push works as expected then when a similar product is passed to 'setOrderItems', it is pushed to 'orderItems' but all items in 'orderItems' are updated to this currently set item.
That is, if orderItems was[{name:"chicken burrito",fillings:[{name:"cabbage"},{name:"cheese"}],...}] before, after setting a similar item but with diff fillings, orderItems is updated to [{name:"chicken burrito",filling:[{name:"guac"}],...},{name:"chicken burrito",filling:[{name:"guac"}],...}] . If setOrderItems is passed a different product say [{name:"goat burrito",...}] it is added as expected.
Can't happen to find a similar issue around. What am i doing wrong. I want to be able to add similar items but with different fillings to orderItems.
It's difficult to see exactly with the code you've given. But if I'm following what you are doing correctly, then the problem is probably here:
$scope.products = getProducts(Restangular);
you are getting a set of products, I assume that there is an object for chicken burrito and an object for goat burrito. The problem, I think, is that you are using the same object for every chicken burrito. So when you push it onto the array you are pushing a reference to the object in products, you then edit that object and push it again. But again you are pushing a reference to the same object. If you actually look at your array before the second push, I suspect you will see that the object already on the array is already altered to match the one you are about to push because it's the same object.
To solve this, you need to make a copy of the object. To do that, take a look at this question:
Deep copying objects in angular?
angular.copy should let you copy your product so that you aren't always pushing the same object.
For example, you could do this:
setOrderItems: function(item,fillings){
...
var itemCopy = angular.copy(item);
orderItems.push(itemCopy);
...
}
Now you are always pushing a copy into your array and your array will fill with independent objects.
I'm running a splice on an array like this, where the array has 5 elements:
array.splice(3, 0, newObj);
The splice doesn't work and I still have 5 elements.
When I debug it in Chrome with console.log I see an array of six objects, but when I open the array I see five elements (see pic below). What does this mean?
You're inserting a new object into that array -> newObj
For example, if you run this in Chrome console, works fine :
function aHandler (){
var a = [{1:1},{1:2},{1:3},{1:4},{1:5}];
a.splice(3,0,{1:20});
console.log(a);
}; aHandler();
Even if you insert null or undefined console.log should show you the right object.
So, maybe you modify the object somewhere between splice and console.log
I have an array like:
errors = [ {...}, {...}, {...} ]
It's an instanceof array, yet it only returns 1 for .length?
Relevant code:
if(data.error){
errors.push({'element':ele,error:data.error});
}
//Above is looped a few times and there are N number of errors now inside
console.log(errors) //Returns 2+ objects like {...}, {...}
console.log(errors.length) //Returns 1
For Uzi and Muirbot, here's the errors array:
[
Object
element: b.fn.b.init[1]
error: "You must enter "example" into the field to pass"
__proto__: Object
,
Object
element: b.fn.b.init[1]
error: "Crap!"
__proto__: Object
It is correct, this code:
var errors = new Array();
errors.push({'element':'ele', error:'data.error'});
...adds ONE object to the array. The object has two properties.
It's possible your code is executing in an order other than what you're expecting. ie, when you log both errors and errors.length, errors does contain only 1 object. But after that you are adding to the errors array, and only after that are you looking at the console. At that point you could see a larger array in errors for two reasons - first, your actual code isn't logging errors but some object that contains errors. In that case the console display is live, and will show you not what was in errors at the time, but what is in it now. Alternatively, the console could just be taking some time to log errors.
Without more code I can't be sure if this is the case. But you could verify it by replacing console.log(errors); with console.log(errors[1]);. If errors is really only 1 long at the time, it will log undefined.
The problem was that Chrome's Web Inspector's console.log is an async event. So, the length was a property lookup so it gave that back instantly, but the object with two items inside was held off until the rest of the events had fired.
In the future I, and others with this issue, should use debugger; instead.
is it an Array object or something that resembles it?
arrays do work:
> a = [{a:1}, {b:2}]
[Object, Object]
> a.length
2
you'll have to provide more code.
and now that you've provided the relevant code, the correct answer is what Steve Wellens said (which was downvoted, by the way).
Array.push adds a single element, objects may have more than one key but they're still a single object so your real case was different from your original example, which of course works.
another possibility:
> a = []
[]
> a.length = 2
2
> a
[]
> a.length
2
> a instanceof Array
true