Code


<!DOCTYPE html> <html> <head> <script src="angular.js"></script> <script src="script.js"></script> </head> <body ng-app> <p> <h2>Nested controllers with model variables defined directly on the scopes</h2> (typing on an input field, with a data binding to the model, overrides the same variable of a parent scope) </p> <div ng-controller="firstControllerScope"> <h3>First controller</h3> <strong>First name:</strong> {{firstName}}<br /> <br /> <label>Set the first name: <input type="text" ng-model="firstName"/></label><br /> <br /> <div ng-controller="secondControllerScope"> <h3>Second controller (inside First)</h3> <strong>First name (from First):</strong> {{firstName}}<br /> <strong>Last name (new variable):</strong> {{lastName}}<br /> <strong>Full name:</strong> {{getFullName()}}<br /> <br /> <label>Set the first name: <input type="text" ng-model="firstName"/></label><br /> <label>Set the last name: <input type="text" ng-model="lastName"/></label><br /> <br /> <div ng-controller="thirdControllerScope"> <h3>Third controller (inside Second and First)</h3> <strong>First name (from First):</strong> {{firstName}}<br /> <strong>Middle name (new variable):</strong> {{middleName}}<br /> <strong>Last name (from Second):</strong> {{$parent.lastName}}<br /> <strong>Last name (redefined in Third):</strong> {{lastName}}<br /> <strong>Full name (redefined in Third):</strong> {{getFullName()}}<br /> <br /> <label>Set the first name: <input type="text" ng-model="firstName"/></label><br /> <label>Set the middle name: <input type="text" ng-model="middleName"/></label><br /> <label>Set the last name: <input type="text" ng-model="lastName"/></label> </div> </div> </div> <br /> <p> <h2>Nested controllers with model variables defined inside objects</h2> (typing on an input field, with a data binding to the model, acts on a specific object without overriding variables) </p> <div ng-controller="firstControllerObj"> <h3>First controller</h3> <strong>First name:</strong> {{firstModelObj.firstName}}<br /> <br /> <label>Set the first name: <input type="text" ng-model="firstModelObj.firstName"/></label><br /> <br /> <div ng-controller="secondControllerObj"> <h3>Second controller (inside First)</h3> <strong>First name (from First):</strong> {{firstModelObj.firstName}}<br /> <strong>Last name (from Second):</strong> {{secondModelObj.lastName}}<br /> <strong>Full name:</strong> {{getFullName()}}<br /> <br /> <label>Set the first name: <input type="text" ng-model="firstModelObj.firstName"/></label><br /> <label>Set the last name: <input type="text" ng-model="secondModelObj.lastName"/></label><br /> <br /> <div ng-controller="thirdControllerObj"> <h3>Third controller (inside Second and First)</h3> <strong>First name (from First):</strong> {{firstModelObj.firstName}}<br /> <strong>Middle name (from Third):</strong> {{thirdModelObj.middleName}}<br /> <strong>Last name (from Second):</strong> {{secondModelObj.lastName}}<br /> <strong>Last name (from Third):</strong> {{thirdModelObj.lastName}}<br /> <strong>Full name (redefined in Third):</strong> {{getFullName()}}<br /> <br /> <label>Set the first name: <input type="text" ng-model="firstModelObj.firstName"/></label><br /> <label>Set the middle name: <input type="text" ng-model="thirdModelObj.middleName"/></label><br /> <label>Set the last name: <input type="text" ng-model="thirdModelObj.lastName"/></label> </div> </div> </div> </body> </html>
var firstControllerScope = function ($scope) { // Initialize the model variables $scope.firstName = "John"; }; var secondControllerScope = function ($scope) { // Initialize the model variables $scope.lastName = "Doe"; // Define utility functions $scope.getFullName = function () { return $scope.firstName + " " + $scope.lastName; }; }; var thirdControllerScope = function ($scope) { // Initialize the model variables $scope.middleName = "Al"; $scope.lastName = "Smith"; // Define utility functions $scope.getFullName = function () { return $scope.firstName + " " + $scope.middleName + " " + $scope.lastName; }; }; var firstControllerObj = function ($scope) { // Initialize the model object $scope.firstModelObj = { firstName: "John" }; }; var secondControllerObj = function ($scope) { // Initialize the model object $scope.secondModelObj = { lastName: "Doe" }; // Define utility functions $scope.getFullName = function () { return $scope.firstModelObj.firstName + " " + $scope.secondModelObj.lastName; }; }; var thirdControllerObj = function ($scope) { // Initialize the model object $scope.thirdModelObj = { middleName: "Al", lastName: "Smith" }; // Define utility functions $scope.getFullName = function () { return $scope.firstModelObj.firstName + " " + $scope.thirdModelObj.middleName + " " + $scope.thirdModelObj.lastName; }; };

Example


Description


We need to pay special attention when we're working with nested controllers. Defining variables directly on the $scope objects managed by the controllers can have unexpected behaviors if we don't know how AngularJS works.

Here we have firstControllerScope, secondControllerScope and thirdControllerScope. All of them define variables directly on their respective $scope objects.

firstControllerScope defines the firstName variable and binds it to an input element in the HTML template. Whenever the user types something in the input element, the firstName value changes accordingly.

secondControllerScope is nested inside firstControllerScope. secondControllerScope defines a new lastName variable on its own $scope object and the getFullName function that uses both a firstName and a lastName variable to return a result. In the HTML template, both firstName and lastName are bound to some elements inside the DOM tree rooted in the div where secondControllerScope is defined. When the page is first loaded, the value of firstName in the DOM tree belonging to secondControllerScope is taken from the scope of the first ancestor controller that has the variable defined in its $scope, in this case from firstControllerScope, because that variable is not defined by secondControllerScope. When the user types in the input element inside the firstControllerScope DOM tree, the value changes also in the elements bound to the firstName variable in the children controllers, but as soon as the user types something in the input element belonging to the DOM tree of secondControllerScope and bound to firstName, then a new variable named firstName is created in the $scope object managed by secondControllerScope. From that moment, there are two different instances of firstName belonging to the scopes of firstControllerScope and secondControllerScope and whenever the user types on the input of firstControllerScope, only its own copy of the variable changes.

The same behavior can be observed also in thirdControllerScope, but here we show that we can access the value of the lastName variable in the parent scope simply using the $parent notation.

There's a solution to avoid possible conflicts and we see it in firstControllerObj, secondControllerObj and thirdControllerObj. Essentially, we just define the variables inside distinct objects and define the objects on the scopes of the three controllers. In this way there's no overriding of the variables whenever the user types in any of the input elements and we can avoid unexpected behaviors.