Although the Angular ng-model directive creates a privately-scoped object for an element, any descendants of that element also have access to that object.
In the previous article of this series: “Getting Started With Angular.js: Data-Binding Basics With the ng-model Directive (Part II)” we demonstrated how two HTML elements with their own unique ng-controller values can reference the same-named data value, yet access completely different data. The main purpose of that article was to demonstrate how each individual ng-controller directive creates a privately-scoped $scope object.
In this article, we will demonstrate how each privately-scoped $scope object inherits from the next ancestor with an ng-model directive, all the way up the DOM tree.
There are three very important native JavaScript concepts at play here:
- scope
- context
- object.prototype
Although a thorough discussion of any of these three topics is more than can adequately be covered here, it is important to note that they all come into play. I’ll point them where appropriate.
Example # 1
1 2 3 4 5 6 7 8 9 |
<div class="parent"> <hgroup> <h2>Parent</h2> <h3>Type Something In the Text-Box</h3> </hgroup> <input type="text" ng-model="myData.parent" maxlength="20"/> <p class="content parent"><b>The Value of "myData.parent" is:</b><span class="val">{{myData.parent}}</span></p> <!-- remaining code removed for brevity sake..... --> </div> |
In Example # 1, we have an element with the class “parent”. It wraps the two “children” elements that we used in the previous article. Notice that the ng-model directive has a value of: “myData.parent”. This means that this element has a privately-scoped $scope object, with a property “myData”, which is an object, and that object has a property named “parent”. Note as well that the {{myData.parent}} placeholder is bound to the exact same data.
Example # 2
1 2 3 4 5 6 7 |
<div class="child left" ng-controller="leftController"> <h2>Left Child</h2> <h3>Type Something In the Text-Box</h3> <input type="text" ng-model="myData.left" maxlength="20"/> <p class="content parent"><b>The Value of "myData.parent" is:</b><span class="val">{{myData.parent}}</span></p> <p class="content"><b>The Value of "myData.left" is:</b><span class="val">{{myData.left}}</span></p> </div> |
In Example # 2 we have a “child” element. Compared to the HTML from Part II of this article, there are a few changes:
The text box and span.val elements are bound to the data: “myData.left”
We have added two elements that are bound to “myData.parent”.
The reason for adding the second set of data-bound elements is to demonstrate that this “child” elements has access to not only its own privately-scoped data: “myData.left”, but also the “myData” property of the $scope object of it’s next descendant with an ng-model attribute. This is an example of leveraging JavaScript’s prototype object to create inheritance. While it may seem that JavaScript “scope” is driving this kind of parent -> child access to the Angular $scope object, it is, in fact, inheritance through the prototype object.
HERE IS THE JS-FIDDLE.NET LINK FOR EXAMPLE # 2: http://jsfiddle.net/Gfm7Q/
How to Demo: Type anything in the box. You’ll see that when you type in the “parent” text box, both “children” have access to its data, and that updated data is injected into the DOM of each “child” element in real-time. Yet each “child” element has access to its own privately-scoped $scope.myData object (i.e. myData.left and myData.right).
Example # 3 A
1 2 3 4 5 6 7 8 9 |
<div class="grandParent"> <hgroup> <h2>Grand Parent</h2> <h3>Type Something In the Text-Box</h3> </hgroup> <input type="text" ng-model="myData.grandParent" maxlength="20"/> <p class="content grandParent"><b>The Value of "myData.grandParent" is:</b><span class="val">{{myData.grandParent}}</span></p> <!-- remaining code removed for brevity sake..... --> </div> |
Example # 3 B
1 2 |
<input type="text" ng-model="myData.parent" maxlength="20"/> <p class="content grandParent"><b>The Value of "myData.grandParent" is:</b><span class="val">{{myData.grandParent}}</span></p> |
In Example # 3A, we have a new element: “grandParent”. The exact same concept as the “parent” element applies: this element has an Angular ng-model directive with a value of: “myData.grandParent”, yet its privately-scoped $scope object is inherited by the “parent” object and both “child” objects.
In Example # 3B, we see a snippet of HTML that has been added to the “parent” and “child” elements. The purpose of this HTML is to demonstrate that we can bind DOM elements to data that belongs to ancestor elements.
HERE IS THE JS-FIDDLE.NET LINK FOR EXAMPLE # 3: http://jsfiddle.net/Gfm7Q/1/
How to Demo: Type anything in the text box. You’ll see that when you type in the “Grand Parent” text-box, the “parent” element and both “children” have access to its data, and that updated data is injected into the DOM of each descendant element in real-time. The same goes for the “parent” element. Yet once again, each “child” element has access to its own privately-scoped $scope.myData object (i.e. myData.left and myData.right).
Example # 4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
<!DOCTYPE html> <html> <meta charset="utf-8"> <head> <title>Getting Started With Angular.js: Data-Binding with the ng-model Directive (Part III) | blog.kevinchisholm.com</title> <link href="style.css" rel="stylesheet" /> <script src="angular.min.js"></script> <script> leftController = function(){}; rightController = function(){}; </script> </head> <body ng-app> <header> <img src="http://sub1.kevinchisholm.com/blog/images/angularjs-logo-small.png" class="logo" > <hgroup> <h1>Getting Started With Angular.js<span>Data-Binding with the ng-model Directive <i>(Part III)</i><span></h1> <h2>Working Example</h2> <h3><a href="http://blog.kevinchisholm.com" target="_blank">blog.kevinchisholm.com</a></h3> </hgroup> </header> <div id="main"> <div class="grandParent"> <hgroup> <h2>Grand Parent</h2> <h3>Type Something In the Text-Box</h3> </hgroup> <input type="text" ng-model="myData.grandParent" maxlength="20"/> <p class="content grandParent"><b>The Value of "myData.grandParent" is:</b><span class="val">{{myData.grandParent}}</span></p> <div class="parent"> <hgroup> <h2>Parent</h2> <h3>Type Something In the Text-Box</h3> </hgroup> <input type="text" ng-model="myData.parent" maxlength="20"/> <p class="content grandParent"><b>The Value of "myData.grandParent" is:</b><span class="val">{{myData.grandParent}}</span></p> <p class="content parent"><b>The Value of "myData.parent" is:</b><span class="val">{{myData.parent}}</span></p> <div class="child left" ng-controller="leftController"> <h2>Left Child</h2> <h3>Type Something In the Text-Box</h3> <input type="text" ng-model="myData.left" maxlength="20"/> <p class="content grandParent"><b>The Value of "myData.grandParent" is:</b><span class="val">{{myData.grandParent}}</span></p> <p class="content parent"><b>The Value of "myData.parent" is:</b><span class="val">{{myData.parent}}</span></p> <p class="content"><b>The Value of "myData.left" is:</b><span class="val">{{myData.left}}</span></p> </div> <div class="child right" ng-controller="rightController"> <h2>Right Child</h2> <h3>Type Something In the Text-Box</h3> <input type="text" ng-model="myData.right" maxlength="20"/> <p class="content grandParent"><b>The Value of "myData.grandParent" is:</b><span class="val">{{myData.grandParent}}</span></p> <p class="content parent"><b>The Value of "myData.parent" is:</b><span class="val">{{myData.parent}}</span></p> <p class="content"><b>The Value of "myData.right" is:</b><span class="val">{{myData.right}}</span></p> </div> </div> </div> </div> </body> </html> |
In Example # 4, we have the full HTML for this article’s working code.
Summary
Much like Part I and Part II of this series on Angular.js data-binding, the examples in this article are very simple. The goal was to provide a very basic demonstration of how DOM elements have access to the data that is bound to their ancestors via their ng-model directive. A key concept to focus on is the fact that Angular provides abstraction for this functionality which would require a great deal of code to replicate. Furthermore, this abstraction is application-agnostic, so you can leverage Angular for a wide range of projects. The features provided have no knowledge of how they will be employed. They “just work.”
Helpful Links for Angular $scope Inheritance
https://github.com/angular/angular.js/wiki/Understanding-Scopes
http://www.ramandv.com/blog/angular-js-sharing-data/
http://stackoverflow.com/questions/14232397/scope-inheritance-in-angularjs
http://www.cubicleman.com/2013/03/14/angularjs-and-a-js-prototypal-inheritance-gotcha/