Reputation: 2725
In my AngularJS app, I generally pass data using the $rootScope
. In the various states, I manipulate pull them into $scope when necessary. But since $scope has prototypical inheritance, I suppose this is just for readability and not much else. In my app, there are the following variables...
$rootScope.list // object
$rootScope.list.items // array
$rootScope.additions // array
$rootScope.cart // object
$rootScope.cart.items // array
Before I begin, this is generally laid out in pseudocode. So if it's not correct, that's okay. I'm looking for a conceptual understanding.
At first, the cart is empty. You look at list.items
and copy individual list items into the cart.items
array. This works great, until we start talking about duplicate items with unique additions
.
Say, I wanted to have 2 of the same item, except one item has different additions (cart items have a quantity property, not list items).
$rootScope.cart.items = [
{
name: 'listItem1',
quantity: 3,
additions: ['addition1']
}, {
name: 'listItem2',
quantity: 1,
additions: ['addition2']
}, {
name: 'listItem1', // same as cart.items[1], but additions has diff values
quantity: 1,
additions: ['addition1','addition2']
}];
Firstly, AngularJS complained because there were duplicates in my ng-repeater. I resolved that issue by appending track by $index
in the ng-repeat
directive. It was also the first red flag I saw that told me I was doing something wrong.
My first idea was to create a temporary cartItem
, arbitrarily add its additions, then push it onto $rootScope.cart.items[]
. But when it's time to push the 3rd cart item onto the cart, which is another copy of the 1st list item, it overwrites the additions[]
of the first cart item. So, $rootScope.cart.items
looks like this:
$rootScope.cart.items = [
{
name: 'listItem1',
quantity: 3,
additions: ['addition1','addition2']
}, {
name: 'listItem2',
quantity: 1,
additions: ['addition2']
}, {
name: 'listItem1', // same as cart.items[1], but additions has diff values
quantity: 1,
additions: ['addition1','addition2']
}];
From this result, it's ostensibly pass-by-reference when it comes to copying $rootScope variables from one place to another.
So, my next thought was to try using a directive with an isolate scope. But I realized soon enough that my logic was faulty. While an isolate scope would give me one-way data-binding, it wasn't in the direction I was hoping for. The scope parent to the directive would still be able to set values.
What I would ideally like to do is arbitrarily add list.items to cart.items. But any cart items that come from the same list item, I would like to be unbound from each other. Thus, additions in one copy can be different from additions in another copy. What would be the best way to achieve this? I have a feeling it might have been a poor decision to use $rootScope
to pass data between states in my app, though it is the fastest way I have found.
Upvotes: 0
Views: 198
Reputation: 1218
Angular copy is what you are looking for.
https://docs.angularjs.org/api/ng/function/angular.copy
There are a couple methods to use this that you will see in the docs, but my coworkers and I have found better use out using the optional 'destination' option.
So this is how it works
First setup your new target array.
var newArray = [];
Then you use that as the destination in angular copy
angular.copy(firstArray, newArray);
There you go, it is just that easy!
Now your new array will no longer be connected to the original array.
I would also look into using factories as services for your code instead of using rootScope. Learning that will increase your productivity a ton and make your code a lot more readable and user friendly. Here is a good article about it.
http://tylermcginnis.com/angularjs-factory-vs-service-vs-provider/
Edit
And as someone mentioned in the comments, it is indeed a deep copy. That means it will copy all children objects and arrays inside of an array.
Upvotes: 1
Reputation: 19193
When you edit the additions
of your first cart item, you actually edit the additions
of both the first and third cart items, because they are the same.
When you create the third cart item, instead, you could copy the properties of the first one:
var newCartItem = {};
newCartItem.name = firstCartitem.name;
newCartItem.quantity = firstCartitem.quantity;
// Do not point to the other array, instead create a new one and copy over
newCartItem.additions = [];
for (var i = 0; i < firstCartitem.additions.length; i++) {
newCartItem.additions.push( firstCartitem.additions[i] );
}
// Now you can add anything to the new cart item without editing the first one
Upvotes: 0