I have a controller that listens on $scope.$on, which will show a popup window when triggered. It works 100% of the time from a couple other controllers' $rootScope.$broadcast methods. But one of them won't work ever.
The controller gets the event, and sets the $scope variable needed, but the page doesn't update, even if I fire $scope.$eval(). Then, if I go to another route, the $scope will finally render, and the modal will pop up on top of that route. I can't tell if I've found a bug in angularjs, or I'm missing something fundamental.
You are probably changing the $scope outside of the angular $digest(). Try replacing code making changes with $scope.$apply(function(){ code making changes }). With this the dirty-check should run and update all.
I would recommend using:
$scope.$evalAsync(function() { // scope changes here });
This way you won't run into problems like trying to call apply when there's a digest already in progress.
Related
I'm working my frontend with angular and angular-loading-bar, in the controller I put this code.
$rootScope.$on("cfpLoadingBar:completed",function(){
$(".animated").addClass("fadeIn");
});
or
$scope.$on("cfpLoadingBar:completed",function(){
$(".animated").addClass("fadeIn");
});
When the all XHR requests have returned, I want to add a clase in my section content, but the code inside event don't run.
How is the correct way to achieve it?
Firstly, check that you use appropriate event name. For example, are you sure thet its name is cfpLoadingBar:completed? Maybe its a cfpLoadingBar::completed (its a very common pattern) or something else?
Second, ensure that you have to subscribe to this event using $rootScope. Maybe you have to subscribe for it in some concrete controller witj its own $scope?
And as a big suggestion: DO NOT USE JQUERY AND ANGULAR TOGETHER IN YOU CODE, DO NOT MESS IT UP!!! Angular has a built in possibility to work also as a jquery. All that you need is to call angular.element() which returns you an element as if would use jquery. In your case you can write angular.element(".animated").addClass("fadeIn"); and it will do the same thing, but in angular way
Yeah, I use both cfpLoadingBar::completed and cfpLoadingBar:completed but don't run this event.
In the other hand I only have one controller by one section, it ran but I needed add a main controller and registered this event and propagate up the event with $broadcast in my child controller.
This is code in MainController
$scope.$on('cfpLoadingBar:completed', function(event, data) {
angular.element(".animated").addClass("fadeIn");
});
And This is code in other Child Controller
$rootScope.$broadcast('cfpLoadingBar:completed');
it is the only way to achieve, I don't know why XD
Thanks Andrew this way is better angular.element()
Basically, I am unable to update my controller information when I listen for the $on event if I loaded my html dynamically using ng-include. Plunker example.
If you click once, you'll see the view keeps the original $scope.name. If you click again it will update.
I put a setTimeout on the broadcast to make sure the ng-include was loaded. You can set that to as long as you want, and will never be able to update the $scope on the first try (at least in my example).
Thoughts?
EDIT:
I'm using <ng-include="template"></ng-template>
As an area I can load alternate content in. If there is a better way to do this, please let me know.
setTimeout() is a function out of the control of AngularJS, so AngularJS will not automatically run a digest after the callback runs. That means, your $rootScope.$broadcast() was run, but AngularJS didn't realize that. The next time when you use $rootScope.template = '....';, a digest runs, and the view was updated to the previous run's model.
To solve the problem, you will need to manually call $scope.$apply() at the end of your setTimeout() callback, or use the Angular-wrapped version of setTimeout(), which is $timeout(), that will automatically run a digest afterwards.
Please refer to the docs for more details about digest/apply:
It works for me if you use $timeout instead of setTimeout. Which you should be using for angular applications.
$timeout(function(){
$rootScope.$broadcast('BROADCAST', param);
}, 1000);
There is definitely something wrong with your design if you are trying to do something like this though. Perhaps someone could suggest an alternate solution if you better explained what you are trying to achieve. As you cannot possibly know how long the timeout should be.
A few things:
1) First, do not define the $scope.template in the broadcast function. The ngInclude will not have a file to display until that value is set; so from it makes sense--in my mind--that the template would not be able to make changes before it and the controller are loaded.
2) You never actually apply Controller C2 to the ngInclude. You can do that like:
<ng-include src="template" ng-controller="c2"></ng-include>
Once I do these two things, the code works and updates the first time without the use of the setTimeout() at all.
Plunker
[And a second question (see below) - rootScope variable set in one controller not visible in a second, sibling controller?]
Possible Duplicate: angularjs $location.path only updates URL on second click - the cause of the problem and the answer does not seem relevant in my, more basic, situation
As per the angularjs docs (https://docs.angularjs.org/guide/$location):
[The $location service]
Maintains synchronization between itself and the browser's URL when the user
...
Clicks on a link in the page.
I understood that $location.path() reflects the current url in the browser, but when I click a link to change the view, exhibits strange behaviour: $location.path() does not 'change' the first time one clicks on a link, and every time thereafter it will change to the link that was clicked the previous time
To see this go here: http://jsfiddle.net/7Ah2W/
I attempted a workaround whereby I must manually set $location.path() using the setter overload.
In doing so, I found another flaw in my understanding of angularjs. I tried setting a variable in the rootScope to reflect the 'current path.' The idea is that views would automatically detect the change in the variable and update. Does not every scope inherit from rootScope?
Here is a jsfiddle
Why is my expectation that $rootScope.currentPath, being changed in 'NavCtrl' and updated in 'CtrlTwo' not being met?
My end goal is to have my navigation bar automatically change when a link in the view is clicked. Similar to https://console.developers.google.com where if you click your project, the navigation to the left changes to API&Auth, settings, etc.
The reason it seems to always be "one behind" is that you're accessing the $location.path() before the actual angular page process can run. Strangely enough if you just add a $timeout with even 0ms delay, it'll work as intended.
$timeout(function () {
$scope.currentPath = $location.path();
}, 0);
jsFiddle example
$rootScope is the global scope, as opposed to the regular $scope which is basically the glue between the controller & view.
For example I set $rootScope.test = 123; in your first controller, and in the second controller I alert that variable, and get the result. jsFiddle $rootScope example. Be careful with $rootScope, it creates globally scoped variables
If a ng-model changed in View, the $scope will be updated correspondingly, but if there is a {{x()}} in the View and a $scope.x=function(){} in the js part, is it that when any event or stuff happens in the view, the x() will be triggered?
I am not quite clear about the principle of AngularJs' event and functioning.
Most of the times Angular will properly handle $scope.x=function(){} and update views automatically.
That's because there are only a few moments in application execution time when your code is executed such as page load, AJAX callback etc. Angular knows about such moments and does dirty checking (comparing scope values before and after).
However, there might be times when Angular doesn't aware that you updating scope properties, for example when you integrating with some 3rd party plugins. In such cases you need to wrap your code, which changes scope properties in $scope.$apply method:
$scope.$apply(function(){
$scope.x = function(){};
});
I'm having some trouble sending and catching events in Angular.
Consider this plunker: Plunk..
As you can see I have two controllers and a directive. The directive 'lives' in controller 1. When something happens in the directive, I want to catch this event in controller 2. As you can see in the plunk, nothing is logged to the console; the event is not catched.
I've also tried to use a service. In this scenario I created a method on the service and when this method is called, I throw the event. Just like in the Plunk, I listen for the event in Controller 2, but this also didn't work.
Where it all comes down to is that I want to invoke a method on another controller..
In you Plunker, second controller (SecondController) is registered, but it is never actually initialized. So that's why your listener never logs the event.
It's not clear how, where and when you are using using your second controller, but if you initialize it with either ng-view (through routes) or ng-controller, than its listener will observe the event.
PLUNKER
Each time you visit a route, the associated controller (and a $scope) are (re)created.
Since you mentioned that Controller 2 is associated with a route, it will only exist when you visit that route (as #Ajay and #Stewie already mentioned), so you can't catch an event in that controller.
I suggest you create a service with some API/methods to do the following:
record that an event triggered
check to see if an event triggered
clear an event
Your directive would call the method to record the trigger.
Your Controller 2 would call the check method when it is created (and then probably call the clear method if it finds the trigger set).