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.)
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
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...
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(){};
});
One thing that sets apart AngularJS from other JavaScript-MVC frameworks is it's ability to echo bound values from JavaScript into HTML using bindings. Angular does this "automatically" when you assign any value to the $scope variable.
But how automatic is this? Sometimes, Angular won't pick up on the change so I need to call $scope.$apply() or $scope.$digest() to inform angular to pickup the change. Sometimes when I run either of those methods then it throws an error and says that a digest is already in progress.
Since the bindings (anything inside {{ }} braces or ng-attributes) are echoed with eval then does this mean that Angular is constantly polling the $scope object to look for changes and then performing an eval to push those changes to the DOM/HTML? Or has AngularJS somehow figured out the use magic variables which fire events which are triggered when a variable value changes or is assigned? I've never heard of it being fully supported by all browsers, so I doubt it.
How does AngularJS keep track of it's bindings and scope variables?
In addition to the documentation section found by Mark I think we can try to enumerate all possible sources of change.
User interaction with HTML inputs ('text', 'number', 'url', 'email', 'radio', 'checkbox'). AngularJS has inputDirective. 'text', 'number', 'url' and 'email' inputs bind listener handler for 'input' or 'keydown' events. Listener handler calls scope.$apply. 'radio' and 'checkbox' bind similar handler for click event.
User interaction with select element. AngularJS has selectDirective with similar behavior on 'change' event.
Periodical changes using $timeout service that also do $rootScope.$apply().
eventDirectives (ngClick, etc) also use scope.$apply.
$http also uses $rootScope.$apply().
Changes outside AngularJS world should use scope.$apply as you know.
As you found out it's not polling, but using it's internal execution loop so that's why you need to use $apply() or $digest() to kick things into motion.
Miško's explanation is quite thorough, but the bit missing is that Angular is just trying to make $scope get back to a clear internal state whenever anything happens within its own context. This might take quite some bouncing around between model states, so that's also why you can't rely on $watch() firing only once and also why you should be careful with manually setting up relations between models or you'll end up in endless circular refreshes.