It's not easy to frame this question, so I will try to explain what I want to know with an example:
Consider this simple angularjs app
: PLUNKER
angular.module('testApp', [])
.controller('mainCtrl', function($scope) {
$scope.isChecked = false;
})
.directive("testDirective", function () {
return {
restrict: 'E',
scope: {
isChecked: '='
},
template: '<label><input type="checkbox" ng-model="isChecked" /> Is it Checked?</label>'+
'<p>In the <b>directive\'s</b> scope <b>{{isChecked?"it\'s checked":"it isn\'t checked"}}</b>.</p>'
};
});
With this html:
<body ng-controller="mainCtrl">
<test-directive is-checked="isChecked"></test-directive>
<p>In the <b>controller's</b> scope <b>{{isChecked?"it\'s checked":"it isn\'t checked"}}</b>.</p>
</body>
The app:
- Has one controller called: "mainCtrl" where we have defined a scope variable called "isChecked"
- It also has one directive called "testDirective" with an isolated scope and a binding property called "isChecked".
- And in the html we are instantiating the "testDirective" inside of the "mainCtrl" and binding the "isChecked" property of the "mainCtrl" scope with the "isChecked" property of the isolated scope of the directive.
- The directive renders a checkbox which has the "isChecked" scope property as a model.
- When we check or un-check the checkbox we can see that both properties of both scopes are updated simultaneously.
So far, so good.
Now let's make a little change, like this: PLUNKER
angular.module('testApp', [])
.controller('mainCtrl', function($scope) {
$scope.isChecked = false;
$scope.doingSomething = function(){alert("In the controller's scope is " + ($scope.isChecked?"checked!":"not checked"))};
})
.directive("testDirective", function () {
return {
restrict: 'E',
scope: {
isChecked: '=',
doSomething: '&'
},
template: '<label><input type="checkbox" ng-change="doSomething()" ng-model="isChecked" /> Is it Checked?</label>'+
'<p>In the <b>directive\'s</b> scope <b>{{isChecked?"it\'s checked":"it isn\'t checked"}}</b>.</p>'
};
});
and this:
<!DOCTYPE html>
<html ng-app="testApp">
<head>
<script data-require="angular.js@1.3.0-beta.5" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="mainCtrl">
<test-directive is-checked="isChecked" do-something="doingSomething()"></test-directive>
<p>In the <b>controller's</b> scope <b>{{isChecked?"it\'s checked":"it isn\'t checked"}}</b>.</p>
</body>
</html>
The only thing that we have done is:
- Define a function in the scope of the controller that does a
window.alert
indicating if the 'isChecked' attribute of the controller's scope is checked or unchecked. (I'm doing awindow.alert
on purpose, because I want the execution to stop) - Bind that function into the directive
- In the "ng-change" of the checkbox of the directive trigger that function.
Now, when we check or uncheck the checkbox we get an alert, and in that alert we can see that the scope of the directive hasn't been updated yet. Ok, so one would think that the ng-change
gets triggered before the model gets updated, also while the alert is being displayed we can see that according to the text rendered in the browser "isChecked" has the same value in both scopes. All right, no big deal, if that's how the "ng-change" behave, so be it, we can always set a $watch
and run the function there... But lets do another experiment:
Like this: PLUNKER
.directive("testDirective", function () {
return {
restrict: 'E',
scope: {
isChecked: '=',
doSomething: '&'
},
controller: function($scope){
$scope.internalDoSomething = function(){alert("In the directive's scope is " + ($scope.isChecked?"checked!":"not checked"))};
},
template: '<label><input type="checkbox" ng-change="internalDoSomething()" ng-model="isChecked" /> Is it Checked?</label>'+
'<p>In the <b>directive\'s</b> scope <b>{{isChecked?"it\'s checked":"it isn\'t checked"}}</b>.</p>'
};
});
Now we are just using a function of the scope of the directive to do the same thing that the function of the scope of the controller was doing, but this time it turns out that the model has been updated, so it seems that at this point the scope of the directive is updated but the scope of the controller is not updated... Weird!
Let's make sure that that's the case: PLUNKER
angular.module('testApp', [])
.controller('mainCtrl', function($scope) {
$scope.isChecked = false;
$scope.doingSomething = function(directiveIsChecked){
alert("In the controller's scope is " + ($scope.isChecked?"checked!":"not checked") + "\n"
+ "In the directive's scope is " + (directiveIsChecked?"checked!":"not checked") );
};
})
.directive("testDirective", function () {
return {
restrict: 'E',
scope: {
isChecked: '=',
doSomething: '&'
},
controller: function($scope){
$scope.internalDoSomething = function(){ $scope.doSomething({directiveIsChecked:$scope.isChecked}) };
},
template: '<label><input type="checkbox" ng-change="internalDoSomething()" ng-model="isChecked" /> Is it Checked?</label>'+
'<p>In the <b>directive\'s</b> scope <b>{{isChecked?"it\'s checked":"it isn\'t checked"}}</b>.</p>'
};
});
This time we are using the function of the scope of the directive to trigger the bound function of the controller, and we are passing an argument to the controller's function with the value of the directive's scope. Now in the controller's function we can confirm what we already suspected in the previous step, which is: that the isolated scope gets updated first, then the ng-change
gets triggered, and it's not after that, that the bindings of the directive's scope get updated.
Now, finally, my question/s:
- Shouldn't angularjs update all the bound properties at the same time, before doing anything else?
- Could anyone give me a detailed explanation of what's happening internally in order to justify this behavior?
In other words: if the "ng-change" got triggered before the model gets updated, I could understand that, but I'm having a very hard time understanding that a function gets triggered after updating the model and before finishing to populate the changes of the bound properties.
If you read this far: congratulations and thanks for your patience!
Josep