$scope variables become undefined outside of init function - angularjs

I have an AngularJS function called init that is called from my HTML code here:
<div ng-controller="main" ng-init="initpara('<?php echo values;?>', '<?php echo values;?>', '<?php echo values;?>')">
The controller is already defined and $scope is already injected (didn't include the controller stuff in here). I know the Angular function is receiving the arguments just fine because I did a console.log on it already. I've always done this to pass PHP variables into my controllers (albeit, might be a bad practice), however, today I ran into a problem. My scope variables automatically became undefined whenever they were outside the function. Normally, this doesn't happen to (although it may have just been a coincidence in worked in the past)
$scope.initpara = function(userStatus, feedbackStatus, favoriteStatus) {
$scope.userStatus = userStatus;
console.log($scope.userStatus);
}
console.log($scope.userStatus);
Output:
undefined line 79 <- outside the function
1 (the result I want for userStatus) line 76 <- inside the function
Is there another way to pass PHP variables into AngularJS? It's pretty strange because it's never done this to me before.

You have not yet initialized $scope.userStatus at the very moment of logging since you just defined $scope.initpara without ever calling it before. Angular is asynchronous as it may not have loaded the DOM yet when you declare initpara, my guess is that it only initializes data after the console log.

Related

Can I tell why Angular 1 is calling a rootScope function on every digest

I am having an issue where I have a function like this...
$rootScope.canNavigate = function(stateName) {
return !stateName || Authentication.canNavigate.call(Authentication, $state.get(stateName));
};
The problem is this function gets called continuously. The stack trace is different every time but has one thing in common, it is always coming from $apply. I have commented out all of the watches that are using this function and it is still happening. Does anyone know why this is happening? I cannot seem to reproduce in a plunker.
Asiel probably has the right answer depending on your situation, but as to how to prevent it, you can either use the bind once expression as Asiel suggests or, if that's not possible for some reason, you could also save the results so you're not calling that service every time.
var canNavigateCache = {};
$rootScope.canNavigate = function(stateName) {
return !stateName
|| canNavigateCache[stateName]
|| canNavigateCache[stateName] =
Authentication.canNavigate.call(Authentication, $state.get(stateName));
};
This also gives you the ability to clear the cache if you need to by just resetting the cache object (canNavigateCache = {})
If there is a call of this function on any of your html, this function will be called every time angular executes a digest cycle in order to do dirty checking (See this question on SO in order to get some kind of reference about this)... except you use the bind once expression (See syntax on this article and this on SO)
It is called from $apply because
$apply() is used to execute an expression in angular from outside of
the angular framework. (For example from browser DOM events,
setTimeout, XHR or third party libraries). Because we are calling into
the angular framework we need to perform proper scope life cycle of
exception handling, executing watches.
but $apply is (also) used by the own framework for making a digest cycle.

$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.

Access angular scope from outside function on load

I have a bit of a problem with an Angular application. Basically I want to manipulate the scope outside of the application itself. If I type in the console:
$scope = angular.element($('.lead-system')).scope()
I can use $scope with $apply and it all works fine. However, when I use this in a rails coffeescript file:
angular.element(document).ready ->
console.log angular.element($('.lead-system')).scope()
It logs undefined and of course I can't use $apply with that.
What's up with that? I'm loading the JS files in this order:
app.js <-- Main angular app initializer
pending.js.coffee <-- The file listed above
lead_controller.js <-- The controller of the scope I'm trying to access
Wrap it in the $timeout, just to check if it will work then. That should tell you if this can be fixed by different timing or give you ideas for further exploration.
My recommendation though would be to fire an event (emit/broadcast or even arbitrary external thing in your case) in controller and in reponse to that event manipulate the scope. You could even pass the scope as parameter for convenience.

ng-include - Controller is called multiple times

Situation:
I am trying to include a partial with ng-include without the need for any routing. I just want to include a specific partial of many dynamically. This is more or less how it looks like:
<div ng-controller="SomeController">
//This controller defines a $scope.getPartial(id)
<ng-include src="getPartial(something.id)"></ng-include>
</div>
It works, the partial is included. But looking at the console I can see that the controller is called several times and the first time it is called, I get a 404
GET path/to/partials/undefined.html [HTTP/1.1 404 Not Found 162ms]
it seems that something.id is not defined, when the include is made for the first time.
Questions:
how can I simply include a partial, without creating a new scope?
if that's not possible, how can I make sure, the controller is called only once?
and how can I avoid the 404?
I'm fairly new to AngularJS and may therefore make wrong assumptions about stuff or miss obvious things, please enlighten me.
ngInclude creates a new scope by definition so you can't easily circumvent it. And, since nested scopes inherit from each other your newly created scope will be able to read whatever is in your SomeController, so you shouldn't have any problems with new scope.
ngInclude's src attribute will get re-evaluated on each $digest scope, so you can't stop it from calling your controller's method repeatedly. For that matter you need to make sure your method is light and fast and it returns the same output given the same input
You may avoid initial 404 by returning empty string "" when id is not yet defined:
$scope.getPartial = function(id){
if(!id){
return "";
}
...
}

Chrome javascript tools: Global, Closure and Local Scope

I'm playing around with a backbone calendar app (it lets you post events on a calendar) in Chrome javascript console and notice on the right side (see image) it has a panel showing local, closure and global scope. In this particular app, I set debugger in the EventsView, and Chrome tells me that the Event (a model) and the EventView (view for one particular event) are in Closure scope.
I sort of understand global, closure and local. Global scope would be anything in the global namespace. Local is all the variables within the current scope. Can you explain in practical terms what it means for the Event and EventView to be in closure scope and how this might improve my understanding of how the app is working...What insight can this provide me? Also, you'll notice that in the Local scope, 'this' is said to be a 'child.' Why? What would the parent be?
Read up here on closures: How do JavaScript closures work?
But the short answer, assuming you're paused on a breakpoint on line 6 below, the variable 'global' will appear in the Global variables section of debug tools, 'closure' in the Closure section, and 'local' in the Local section:
1 var global = 'foo';
2
3 function bar() {
4 var closure = 'baz';
5 function oof() {
6 var local = 'rab'; // stopped on a breakpoint on this line
7 }
8 }
When debugging, it's just helpful to know how many closures up you need to look to find the variable definition, how many other contexts it might apply to, the consequences of changing it in local scope, etc.

Resources