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
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()
I've read this Q/A about databinding and $apply -> $digest in AngularJS :
How does data binding work in AngularJS?
While I understand the principle and the consequences, I'm still unsure about when AngulaJS is going to call $digest to do the dirty-checks. (And so, when should I consider to do something about the $watcher)
Every example I found was about using 'ng-click', 'ng-show', or 'ng-class'. But I'm pretty sure that it is also triggered by any change on variables of the scope ({{myData}}), and by many others directives (All of them maybe ?).
I would like to understand in which cases a $digest is called.
Can you give me any generic rule to knwo when it is called, or an exhaustive list of actions that will trigger a dirty-check ?
Have a look at this:
angularjs docs, specifically at "Integration with the browser event loop" section.
Basically the way it works is that AngularJS binds event handlers to any element that interacts with angular (any element that has a directive attached to it) and every time that event fires, $apply is called which internally calls $digest which will trigger the reevaluation of all the $watches which will check for values changed, etc...
I have multiple custom directives in my ngApp. The demo code is:
<top-nav></top-nav>
<left-sidebar></left-sidebar>
<div class="content">
....
</div>
The height of the 'left-sidebar' needs to be adjusted according to the height of the 'top-nav'.
Similarly there are other interdependent UI tasks. I want to run the post-load code (say app.initializeUI();) only after ALL the directives have been loaded and rendered.
Now, How do I achieve that?
EDIT :
Currently I am using the following code :
App.run(function($timeout){
$timeout(function(){ app.init() },0);
});
This runs fine, however, I am not sure this is the perfect way of doing this.
EDIT 2:
For people who want to avoid setting styles in js - use CSS Flexbox. I find it much better than calculating heights post page load. I got a good understanding of flexbox here
I would create an attribute directive with isolated scope, set terminal=true and add it to your body tag. Then in your link function, setup a $watch - isInitialized which is initially false. In your $watch handler, call your app.init(), and then de-register the $watch, so that it is always initialized once.
Setting up a terminal directive has consequences - no other directive can run after the terminal directive on the same element. Make sure this is what you want. An alternative is to give your directive the lowest possible value so that it is compiled first, and linked last.
The important pieces to this solution are:
By adding your Directive to the body tag, you ensure that it is linked last.
By adding a $watch, you ensure that all other directives have gone through a digest cycle, so by the time your $watch handler is called, all other directives should have already rendered.
Note of caution: The digest cycle may run several times before scope changes stabalise. The above solution only runs after the first cycle, which may not be what you want if you really want the final rendering.
everybody!
Here is my not working and not complete sample for demonstrating purposes.
In few words, suppose that I have some control and some control event (select tv node), in this event I change some scope variable, say $scope.test and I expect it to change (it's value on html page). But in provided sample code it doesn't change unless I use scope.$apply() method (commented), when scope.$apply() is used then everything works as expected.
So, my question is more about applicability of use of scope.$apply() method.
There are a lot of articles related to this and most of them suggest that apply() method shouldn't be used unless you are developing angular directives
or some advanced binding scenarious. That's why I'm a little bit confused with my relatively simple case.
Thanks in advance.
The rule is that you call $scope.$apply() whenever you change some state that Angular has to respond to outside of Angular's framework, eg an event handled by jQuery (or in your case Kendo) event handlers.
Moreover the $scope should not be available in your select: handler because the handler is Kendo, $scope is Angular. (Indeed your example throws Cannot set property 'test' of undefined when clicking on a label.)
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.