need to find out the source of angularjs factory values modifier - angularjs

In my angularjs application, I am using factory to store the values and share them across the controllers. But I am getting into a peculiar problem.
The following is my factory :
factory.quoteLinks = {allLinks : [], allLeftLinks :[], curSection : "-1", insType: "-1", test:"-1"};
factory.setQuoteLinks = function(qlinks, qleftLinks, qsubLink, qinsuranceType, testVal) {
factory.quoteLinks = { allLinks : qlinks, allLeftLinks : qleftLinks, curSection: qsubLink, insType: qinsuranceType, test:testVal};
};
factory.getQuoteLinks = function() {
return factory.quoteLinks;
};
As far as I Know, the values will be stored in factory.quoteLinks, only when I call factory.setQuoteLinks. So whenever I explicitly make call to factory.setQuoteLinks, the values are correctly getting stored. After a while debugging remaining part of the code, during debugging, I noticed even though I am not calling factory.setQuoteLinks, the values of allLinks in factory.quoteLinks is getting modified to some other values and I am not able to figure out from where this is getting modified even though I am not calling factory.setQuoteLinks to modify the allLinks at that particular point. Is there any possibility for me to track from where this value in the factory is getting modified, I mean the cause for this modification? I left with no clue how to figure it out?

Ax Max Sorin said you're probably modifying it outside of here because you're passing back the reference to it in factory.getQuoteLinks. If you need this changed use an angular copy:
factory.getQuoteLinks = function() {
return angular.copy(factory.quoteLinks);
};
This will return a copied quoteLinks.

Related

Assign promise return value to an iterator (AngularJS)

Iterating over objects in CoffeScript I want to calculate and display a certain value for every entry (number of assets in pool)
Controller function:
archivedBidParts = []
for day in BidService.activeDays(bid) when DateService.isBetween(day, from, to, true, true)
splitBidPart = angular.copy(bid_part)
splitBidPart.hours = BidService.hoursActiveOnDay(day, bid)
splitBidPart.number_of_assets_in_pool = number_of_assets_in_pool(bid)
archivedBidParts.push(splitBidPart)
$scope.data.splitBidParts = sort(archivedBidParts)
Helper function:
number_of_assets_in_pool = (bid) ->
Pool.query().$promise.then(pool_memberships.bind(null, bid)).then((pool) -> pool.pool_memberships.length)
The view:
<tr ng-repeat="bid_part in data.splitBidParts">
...
<td ng-hide="bid_part.holidayName">{{ bid_part.number_of_assets_in_pool }}</td>
Problem:
The helper function returns a promise. When trying to console.log the return value inside the promise (in the last .then()-statement) the right number gets printed out in the console.
Does someone have an idea how to use the return value to be displayed properly?
Thanks in advance
I think you should change the following line in your controller
splitBidPart.number_of_assets_in_pool = number_of_assets_in_pool(bid)
To something like this:
number_of_assets_in_pool(bid).then(function(retval) {
splitBidPart.number_of_assets_in_pool = retval;
});
For your example you could assign to the splitBidPart object in the promise resolution.
If you need to know when all the promises are resolved you will need to collect them and then resolve them with a call to $q.all() (note this can be expensive to perform if there are lots of promises).
Note in the example below all the instances of splitBidPart are lost once the loop is complete so this is not a working stand alone example.
Note also you need to provide $q via injecting it into the controller.
promises = []
for day in BidService.activeDays(bid) when DateService.isBetween(day, from, to, true, true)
splitBidPart = angular.copy(bid_part)
splitBidPart.hours = BidService.hoursActiveOnDay(day, bid)
number_of_assets_in_pool(bid).then (theAnswer) ->
splitBidPart.number_of_assets_in_pool = theAnswer
$q.all(promises).then ->
console.log "all the promises are resolved"
Note that in some cases Angular can deal with promises intelligently and it will 'just work'. Would like to articulate this last point but can't really without digging into the docs.
Does the sort function preserve the binding? Can you try what happens when leaving out the sort?

Angular $resource returns too many results

I don't know if my question makes any sense as I'm a bit confused to what is happening here myself, but I have a service that returns information about offices. There seems to be nothing wrong with this and it returns data like this:
[
{
"mainContact": {
"phoneNumbers":[
{"key":1,"number":"22555555","type":"Mobile"}
],
"key":1,
"name":"Ola Dunk",
"email":"oladunk#lol.no"
},
"secretary": {
"phoneNumbers": [
{
"key":2,
"number":"22666666",
"type":"Home"
}
],
"key":2,
"name":"Kari Norrmann",
"email":"kari#test.no"
},
"key":1,
"specialRequirements":null,
"name":"Ola Dunk",
"address":"Test",
"doctorCount":10,
"presumedPurchaseAmount":100000,
"phoneNumber":"22222222",
"membershipStatus":1
}
]
I tried using $resource to get the data using
var r = $resource('http://localhost:60297/api/office');
console.log(r.query());
I use query() over get() since this is the default get all service when no id is specified or some other selector. It just happens to return only the one test office I've created because that's the only office that exists presently.
My problem is what you can see in my console which I've screenshotted here:
That's just the tip of the iceberg. The result keeps getting repeated. Just to check I tried using $http instead of $resource and then it works just fine.
Any ideas on what's wrong?
That's because object 'self-assigned', you can try in console:
var test = { foo : 'bar'};
test.foo = test;
and get same thing. The question is: why it bothers you?

setting $rootScope but value then not visible from another controller

My application requires candidates to be set up with an agency and nationality (among other things). I want to set the list of agencies and nationalities in global variables so that they can be accessed by the profile screen rather than retrieved from the database every time (as they are currently). Thanks to the other questions here I've got so far... but a crucial puzzle piece is clearly missing. The data is being retrieved from the DB but then isn't visible elsewhere.
homecontroller.js which loads up the data...
//Home page Controller
function HomeCtrl($scope, $rootScope, SessionTimeoutService, GetAllAgencies, GetNationalityList){
GetAllAgencies.getData({}, function(agencieslist, $rootScope) {
SessionTimeoutService.checkIfValidLogin(agencieslist);
$rootScope.agencieslistglobal = agencieslist.data;
});
/* I tried hard-coding values here - that worked & was passed thro' ok
$rootScope.nationalitieslistglobal = [
{'nationality_id' : 0, 'name' : 'Unknown'},
{'nationality_id' : 1, 'name' : 'Known'},
{'nationality_id' : 2, 'name' : 'Pants'},
]; */
GetNationalityList.getData({}, function(nationalitieslist, $rootScope) {
SessionTimeoutService.checkIfValidLogin(nationalitieslist);
$rootScope.nationalitieslistglobal = nationalitieslist.data;
});
alert($rootScope.nationalitieslistglobal[8].name);
}
The alert doesn't fire at all.
From the candidatescontroller.js:
function CandidatesAddCtrl($scope, CandidateModel, GetNationalityList, SessionTimeoutService, $http, GetAllAgencies, $rootScope) {
/* commented out as this should now be done globally - this works when it's in place
GetAllAgencies.getData({}, function(agencieslist) {
SessionTimeoutService.checkIfValidLogin(agencieslist);
$scope.agencieslist = agencieslist.data;
});
*/
CandidateModel.getBlankCandidate();
$scope.candidateinfo = CandidateModel;
// get global agencies list
$scope.agencieslist = $rootScope.agencieslistglobal;
alert($scope.agencieslist); // shows 'undefined'
/*
GetNationalityList.getData({}, function(nationalitieslist) {
SessionTimeoutService.checkIfValidLogin(nationalitieslist);
$scope.nationalitieslist = nationalitieslist.data;
});
*/
// get global nationalities list
$scope.nationalitieslist = $rootScope.nationalitieslistglobal;
....
}
So... when I hard-code the data outside of the GetNationalityList.getData function (the bit that's commented out in the example above), it is populated & passed through OK & my drop-down list populates. When I don't do that, the $rootScope values are 'undefined'.
I have 2 theories -
somehow the homecontroller $rootScope isn't being recognised as the global I'm intending (that's just the name of the variable) and some other "passing back" action needs to be taken (I've tried several variations on "return nationalitieslist.data" and assigning it to other variables/handles). Also, nationalitieslist.data is in the same format as the hard-coded list, only it's longer.
I'm being caught out by the asynchronous nature of javascript and when I load the second page, the data just isn't there yet. I'm not convinced this is right as the DB call is finishing and I had an alert which showed me a random nationality name and it was there.
My frustration in part is coming from the fact that the GetNationalityList and GetAllAgencies code snippets work and assigns values correctly in the candidate controller (the bits that are now commented out), but are working subtly differently in the homecontroller.
Top tips, anyone, please?
I am sure this article can help.
http://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/
I had somewhat of a similar issue but I needed to emit from a factory.
My problem was answered here.
Angular $rootScope.$on Undefined
Your 2nd point is more your issue, although it's not JavaScript that's async in nature, it's Angular services such as your GetAllAgencies service. You're making async calls but not waiting for the response. That's why when you assign it manually it all works. Try putting your alert() inside the callback function to the getData() call. This should highlight how the process should work.

Values set in a function coming up as undefined in a different state

Using ui-router. take a look at this.
starting with this in the controller:
$scope.transitionResponse = {};
$scope.hello= "world";
transition.makeTransitionCashOutCall(strippedUrl).then(function(response){
//nested view will need this to build out layout
$scope.transitionResponse.OnlineDistributionProcessUrl = response.data.OnlineDistributionProcessUrl;
console.log(response);
console.log($scope.transitionResponse.OnlineDistributionProcessUrl);
if(!response.data.OnlineDistributionProcess){
$scope.showDocument(transitionType);
}else{
//display phone no. if no url
$state.go('rollover-options.cashout-info');
//display url if it exists (same view ^)
}
});
The console.log for $scope.transitionResponse.OnlineDistributionProcessUrl is giving me correct value. But as soon as I drill down to another state the value is lost and i get undefined on console.logs or alerts. What is going on? really? Why is the value set getting lost? I can access hard coded values like $scope.hello fine with {{hello}} in the cashout-info view. But the other value is getting lost. I'm really lost here.
u have not declared OnlineDistributionProcessUrl this field in the json
$scope.transitionResponse = {};
instead it should be like this
$scope.transitionResponse={
"OnlineDistributionProcessUrl":null
}
or you can use
$scope.transitionResponse['OnlineDistributionProcessUrl'] = response.data.OnlineDistributionProcessUrl;

How to Troubleshoot Angular "10 $digest() iterations reached" Error

10 $digest() iterations reached. Aborting!
There is a lot of supporting text in the sense of "Watchers fired in the last 5 iterations: ", etc., but a lot of this text is Javascript code from various functions. Are there rules of thumb for diagnosing this problem? Is it a problem that can ALWAYS be mitigated, or are there applications complex enough that this issue should be treated as just a warning?
as Ven said, you are either returning different (not identical) objects on each $digest cycle, or you are altering the data too many times.
The fastest solution to figure out which part of your app is causing this behavior is:
remove all suspicious HTML - basically remove all your html from the template, and check if there are no warnings
if there are no warnings - add small parts of the html you removed and check if the problem is back
repeat step 2 until you get a warning - you will figure out which part of your html is responsible for the problem
investigate further - the part from step 3 is responsible for either mutating the objects on the $scope or is returning non-identical objects on each $digest cycle.
if you still have $digest iteration warnings after step 1, than you are probably doing something very suspicious. Repeat the same steps for parent template/scope/controller
You also want to make sure you are not altering the input of your custom filters
Keep in mind, that in JavaScript there are specific types of objects that don't behave like you would normally expect:
new Boolean(true) === new Boolean(true) // false
new Date(0) == new Date(0) // false
new String('a') == new String('a') // false
new Number(1) == new Number(1) // false
[] == [] // false
new Array == new Array // false
({})==({}) // false
Usually that happens when you're returning a different object every time.
For example, if you use this in a ng-repeat:
$scope.getObj = function () {
return [{a: 1}, {b: 2}];
};
You're going to get this error message because Angular tries to have the "stability" and will execute the function until it returns the same result 2 times (comparing with ===), which in our case will never return true because the function always returns a new object.
console.log({} === {}); // false. Those are two different objects!
In this case, you can fix it by storing the object in scope directly, e.g.
$scope.objData = [{a: 1}, {b: 2}];
$scope.getObj = function () {
return $scope.objData;
};
That way you're always returning the same object!
console.log($scope.objData === $scope.objData); // true (a bit obvious...)
(You should never encounter that, even on complex applications).
Update: Angular has added some more in-depth explanation on their website.
Just wanted to throw this solution in here, hopefully it'll help others. I was getting this iteration problem because I was iterating over a generated property which was making a new object every time it was called.
I fixed it by caching the generated object the first time it was requested, and then always returning the cache if it existed. A dirty() method was also added, which would destroy the cached results as needed.
I had something like this:
function MyObj() {
var myObj = this;
Object.defineProperty(myObj, "computedProperty" {
get: function () {
var retObj = {};
return retObj;
}
});
}
And here's with the solution implemented:
function MyObj() {
var myObj = this,
_cached;
Object.defineProperty(myObj, "computedProperty" {
get: function () {
if ( !_cached ) {
_cached = {};
}
return _cached;
}
});
myObj.dirty = function () {
_cached = null;
}
}
There also is the possibility of it not being an infinite loop at all. 10 iterations is not a sufficiently large number to conclude that with any amount of certainty. So before going on a wild-goose chase it may be advisable to rule out that possibility first.
The easiest method to do so is increasing the maximum digest loop count to a much larger number, which can be done in the module.config method, using the $rootScopeProvider.digestTtl(limit) method. If the infdig error does no longer appear you simply have some sufficiently complex update logic.
If you build data or views relying on recursive watches you may want to search for iterative solutions (i.e. not relying on new digest loops to be started) using while, for or Array.forEach. Sometimes the structure is just highly nested and not even recursive, there probably is not much to be done in those cases except raising the limit.
Another method of debugging the error is looking at the digest data. If you pretty print the JSON you get an array of arrays. Each top level entry represents an iteration, each iteration consists of a list of watch entries.
If you for example have a property which is modified in a $watch on itself it is easy to see that the value is changing infinitely:
$scope.vm.value1 = true;
$scope.$watch("vm.value1", function(newValue)
{
$scope.vm.value1 = !newValue;
});
[
[
{
"msg":"vm.value1",
"newVal":true,
"oldVal":false
}
],
[
{
"msg":"vm.value1",
"newVal":false,
"oldVal":true
}
],
[
{
"msg":"vm.value1",
"newVal":true,
"oldVal":false
}
],
[
{
"msg":"vm.value1",
"newVal":false,
"oldVal":true
}
],
[
{
"msg":"vm.value1",
"newVal":true,
"oldVal":false
}
]
]
Of course in larger project this may not be as simple, especially since the msg field often has the value "fn: regularInterceptedExpression" if the watch is a {{ }} interpolation.
Other than that the already mentioned methods, like cutting down the HTML to find the source of the problem, are of course helpful.
I had the same problem - I was creating a new date every time. So for anyone dealing with dates I converted all calls like this:
var date = new Date(); // typeof returns object
to:
var date = new Date().getTime(); // typeof returns number
Initializing a number instead of a date object solved it for me.
the easy way is :
use angular.js,not the min file.
open it and find the line:
if ((dirty || asyncQueue.length) && !(ttl--)) {
add line below:
console.log("aaaa",watch)
and then refresh your page, in the develope tools console,you will
find you error code .
It's a known bug in ui-router, this helped us: https://github.com/angular-ui/ui-router/issues/600
I would also like to mention that I received this error message when I had a typo in the templateUrl of a custom directive that I had in my project. Due to the typo, the template could not be loaded.
/* #ngInject */
function topNav() {
var directive = {
bindToController: true,
controller: TopNavController,
controllerAs: 'vm',
restrict: 'EA',
scope: {
'navline': '=',
'sign': '='
},
templateUrl: 'app/shared/layout/top-navTHIS-IS-A-TYPO.html'
};
Look in the network tab of your web browser's dev tools, and look to see if any resource is having a 404 error.
Easy to overlook, because the error message is very cryptic and seemingly unrelated to the real issue.
I was having this issue in my project because the .otherwise() was missing my route definition and I was hitting wrong route.
I had this issue because I was doing this
var variableExpense = this.lodash.find(product.variableExpenseList, (ve) => {
return ve.rawMaterial.id = rawMaterial.id;
});
Instead of this: (notice = vs ===), my unit test started breaking and I found my stupidity
var variableExpense = this.lodash.find(product.variableExpenseList, (ve) => {
return ve.rawMaterial.id === rawMaterial.id;
});
I ran into this issue where I needed a dynamic tooltip... it caused angular to recalculate it every time as a new value (even though it was the same). I created a function to cache the computed value like so:
$ctrl.myObj = {
Title: 'my title',
A: 'first part of dynamic toolip',
B: 'second part of dynamic tooltip',
C: 'some other value',
getTooltip: function () {
// cache the tooltip
var obj = this;
var tooltip = '<strong>A: </strong>' + obj.A + '<br><strong>B: </strong>' + obj.B;
var $tooltip = {
raw: tooltip,
trusted: $sce.trustAsHtml(tooltip)
};
if (!obj.$tooltip) obj.$tooltip = $tooltip;
else if (obj.$tooltip.raw !== tooltip) obj.$tooltip = $tooltip;
return obj.$tooltip;
}
};
Then in the html, I accessed it like this:
<input type="text" ng-model="$ctrl.myObj.C" uib-tooltip-html="$ctrl.myObj.getTooltip().trusted">
this is how I approached it and found a solution:
I checked the text, it showed:
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [[{"msg":"statement === statment && functionCall()","newVal":[{"id":7287,"referen...
so if you can see the
msg
that's the statment generating the error. I checked the function called in this message, I returned (false) from all of them just to determine which one have the problem.
one of them was calling a function that keeps changing the return, which is the problem.
As crazy as it sounds, I fixed this error just by restarting my browser when it just cropped up all of a sudden.
So one solution is to just clear your browser's cache or try restarting the browser.

Resources