How to preserve an object initial data? - angularjs

I am facing some weird behavior with AngularJS in preserving a variable data which got binded to a from using scope, let me explain my scenario.
I have a form whose field values are initially served form controller through a scope variable, I need to compare the updated values with those initial values while saving, so I kept the initial data in a variable and assigned that variable to scope.
Whenever I change the form fields, controller variable data also getting updated along with scope variable. I am not sure whether this is the correct behavior or not, I guess only scope should get updated.
Anyone please suggest the correct behavior and how to solve this issue if that's the correct behavior.
JS Bin: http://jsbin.com/kakapinuhe/edit?html,js,console,output
You will get to know the problem easily by looking at the above JSBin link, let me know if needed any clarification.
Thanks,
Siva

Yes, that is the expected behavior of javascript. When you assign:
$scope.formData = initData;
you are NOT making a copy of your initData variable. Instead, your formData is refering to same memory space as your initData. in short, they are referring to the same data.. so even if formData change.. your initData is lost.
You can fix this by using angular.copy():
$scope.formData = angular.copy(initData);
Here is the modified JSbin: http://jsbin.com/yoviyesero/edit?html,js,console,output

Use angular.copy
Reason: When you write
$scope.formData = initData.
You are creating a reference to same object initData. So any changes in $scope.formData will reflect in object too. Using angular.copy will create a deep copy of source without any references.

Related

Why Angular JS input model value does not get updated if i update value of input field using javascript ex: using setTimeout

I have a input field with model named "name" with some initialized value say "James".
And when i console $scope i can see model value as "James".
ie. $scope.name = "James".
But immediate on next statement if i use setTimeout function and update input field value with Say "Marcus" and console $scope again, why i get $scope.name = "James" only.
I know, i have updated input field value by going out of box than angular and angular is not aware of updation.
So my real question is, Is Angular JS written over Javascript Language? And if yes, why using setTimeout not updated model value in $scope.
Please help me, if i am thinking/question is right or wrong.
This has to do with how angular handles two way data-binding and the digest loop. If you're interested in knowing all the details of how this works then you can read about it here: https://www.ng-book.com/p/The-Digest-Loop-and-apply/
The basic idea is that in angular the digest loop is used to keep track of all the variables you placed in your $scope. Whenever a variable changes it sets off the digest loop, in each cycle of the loop the new value of the variable is compared to the old value and updated accordingly until the new value is equal to the old value which ends the loop. That being said this only works as long as you change your variables within angular's context, this is especially true for changing variables within callback functions or with jquery. Angular doesn't know that a variable was changed so the digest loop never gets started, you could force it by using $scope.$apply but some cases should be avoided in general unless there's no angular alternative for it (Which usually isnt the case).
For setTimeout there's an angular alternative which is $timeout, this has the exact same functionality as setTimeout but it works within angular's context so your variable will be updated.

How to handle $digest triggered by $http

I have been struggling a long way in a issue, wherein I need to update a parent obj from directive scope. I need to fetch some data using $http and fit this data against a property in original parent Obj.
However, after doing this, the view gets updated but somehow the model binded to these view become undefined. Since the view are updated with new data, somehow the models are becoming undefined after that.
Only now I came to know that, $http triggers a $digest, so I think that is the cause of my issue.
What can I do to avoid my models becoming undefined and the fetched values to remain intact in original object.
Just to make things clear, before I attach a wrking plnkr. here is what I mean:
I have a obj $scope.Obj. I have binded the input fields in directive template with this object using 2 way binding like
<input ng-model = Obj.something.something2[$index]/>
Now say I made a API call and update my something2 in $scope.Obj as:
$scope.Obj.something.something2 = APIResponse.something3
The values from new object something3 are visible on UI, but in backend after this
$scope.Obj.something.something2[$index]
becomes undefined.
Pls suggest possible reasons for this...
The other models are becoming undefined because you are replacing the object. Instead you should use angular.extend.
angular.extend($scope.Obj.something.something2, APIResponse.something3);
For more information see the AngularJS angular.extend API Reference.

AngularJS: help me avoid this anti-pattern: setting variables on $rootScope before $location.path

Here's something simple and I think there must be a better way: I often need to navigate to a new view, and at the same time I want to provide some context information to the scope that will be created. I can't figure out how to do both simply -- change the url (resulting in my view/controller being instantiated) and pass some variables I want instantiated in the controller's scope -- for instance, so they can be rendered in the view template
So instead I am doing:
$rootScope.myVar = 'blah';
$location.path = '/newView' ;
and newView.html accesses {{myVar}}
I know this is wrong, what is the simplest solution to avoid using $rootScope like a global dumping ground?

$watchCollection on an array of js objects in an Angular Controller requires an anonymous function?

I have a simple js array being retrieved by an angular factory, injected into a control, watched, then displayed in a table using ng-repeat from within a view state managed by ui-router.
Initially I attempted to subscribe to this array using $watchCollection...
self.$watchCollection( self.Data, function(newData, oldData){
self.total = newData.length;
});
However this was only running when I initially load the app. I found that the only way to subscribe to the running collection was to use an anonymous function returning the array in question.
self.$watchCollection( function() { return self.Data }, function(newData, oldData){
self.totalWatchedWithAnonFunc = newData.length;
})
View this all in http://plnkr.co/edit/L7mycl
From everything I read, all I should have needed to do for a valid subscription was to pass in the array itself and not an anonymous function that returns the array.
The reason I included ui-router is because my actual application is using it heavily and I wanted to mock up my environment as closely as possible to how I've got things set up.
What am I not understanding? Could I be doing something differently?
Thanks.
Are you looking for self.$watchCollection("Data", ...) instead of self.$watchCollection(self.Data, ...)? Try it out! See the docs to see why: the first argument is a string evaluated on the scope, or a function taking a scope that returns something you want to watch.

Angular - ngModel not updating when called inside ngInclude

First and foremost, the plunker: http://plnkr.co/edit/v1uTz5
This is a working demo of the issue I am running into.
I have a ng-include to include a partial.
Inside the partial I have an text input with ngModel AND directive.
The model updates accordingly inside the include, but any interaction outside the include is ignored. The {{test}} outside the include doesn't update, but the {{test}} inside does.
The directive, when called, handles the enter key and calls the correct scope and function. However, the $scope.test variable has never been updated, but $scope.testFinal is updated and the ng-include template renders it appropriately. Trying to reset the $scope.test model does not work either.
Am I missing something here? Or is this a bug with the directive or with the ng-include?
Instead of using a primitiive to define the variable, make it an object.
$scope.model={test:''};
Directives create their own scope for each item. When you equate a primitive to a new scope variable, it has no binding to the original, however when original is an object, a reference is created , not a copy, and changes made in one will reflect in the other
Simple explanatory example:
var a ='foo';
var b= a;
/* now change a*/
a='bar';
alert( b) // is still 'foo'
now do the same with object:
var obj_1= {a:'foo'};
var obj_2=obj_1;
/* now change obj_1.a*/
obj_1.a='bar';
alert( obj_2.a) // change to obj_1 will also change obj_2 and alert returns "bar"*/
Your Plunker Modified
Read this article on angular wiki for more detailed explanation
John Lindquist has a video about it. Although he doesn't quite explains why you need to use an object.
Basically every time there is a new non-isolated scope, every property of the parent scope is copied to the new scope and, as #charlietfl explained, copying a primitive type really creates a "copy" but with objects what you get is a reference, hence the changes are global.
ng-include creates its own scope and it is different than outer scope. Use this.test instead of $scope.test inside ng-include template. It will work properly.

Resources