Angular 1.x Data-Binding Basics: the ng-model Directive (Part III)

Angular

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:

  1. scope
  2. context
  3. 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

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

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

Example # 3 B

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

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/

Associative Arrays in JavaScript

JavaScript

JavaScript LogoYou may be finding conflicting information about associative arrays in JavaScript. Well, the answer is quite simple… and then things start to get a little odd

If you are frustrated because you have been getting different answers on this subject, I”ve got good news and bad news. The good news is, the answer is simple: associative arrays are not supported in JavaScript. Arrays in JavaScript are index-based. Plain and simple, end of conversation. But the bad new is, it’s not quite the end of the conversation. The reason for this is that the following code actually works just fine:

[insert shrugged shoulders here]  “…ok Kevin, so what’s the problem ?

The problem is: you do not have an array with five elements. You have an array with three elements, and two properties.

Huh?

Yup, this is the part where JavaScript array objects behave in an unexpected way. But hang in there, it’s actually kind of cool once you understand what is happening.

Arrays are Objects

Arrays in JavaScript are numerically indexed: each array element’s “key” is its numeric index. So, in a way, each element is anonymous. This is because when you use methods of the Array object such as array.shift() or array.unshift(), each element’s index changes. So, after using array.shift(), array element # 2 becomes array element # 1, and so on. (array.pop() and array.push() may change the length of the array, but they don’t change the existing array element’s index numbers because you are dealing with the end of the array.)

All this is to say that in a JavaScript array, each element can only be identified by an index, which will always be a number, and you always have to assume that this number can change, which means that the whole “key/value” idea is out the window (i.e. no associative arrays in JavaScript. Sorry.).

OK smarty-pants, if you can’t have associative arrays in JavaScript, why does this work: arr[“drink”] = “beer” ?

Glad you asked. This is because in JavaScript, arrays inherit from Object(). Whether you use an array literal or instantiate the array constructor, you are creating an object, plain and simple. Consider the following:

Example # 1

In Example # 1, we create an array in three different ways. The first line creates a new array with three elements, but they are all undefined. The second line creates a new array, but it is empty, with no elements (this is an array literal). The third line creates an array literal, but we provide values for the elements as we define the array. In all three cases, you are creating an instance of the Array() constructor, which inherits from Object(). So, these are ALL objects.

Example # 2:

 

In Example # 2, we create an array literal, but it is empty. (var arr = []; works just fine, but it is an empty array.) When we check the length property and try to inspect the object with console.dir(arr), we can clearly see that it is empty. Then we add elements to the array, and add named properties (e.g. arr[“drink”] = “beer”). But when checking the array’s length property, and inspecting the object, we can see that the actual “array” part of the array still has only three elements (i.e. “music” and “drink” are NOT elements in the array), and that “music” and “drink” are properties of the array object.

Arrays are Objects with their own special “array-ish” properties

What is happening, is that the Array() constructor returns an instance object that has some special members that other objects such as Function() and Date() do not. So when your code says:  arr[“drink”] = “beer” you are simply adding a new property to your object, which happens to be an array, and that property has a name of “drink”, and you have assigned the value of “beer” to it.

You could have easily assigned a number, an object, an anonymous function, or one of JavaScript’s other data types. This property “drink” has no connection to the elements in the array. It does not increase the value of the array’s “length” property, and it cannot be accessed via the array-ish methods such as pop() or shift().

Let’s see what happens when we take advantage of this object’s “array-ness.”

Example # 3:

OK, so things are gettin’ pretty weird, right? Yep, but it’s all cool stuff, and at the end of the day, it’s no big deal. It just illustrates the way objects work in JavaScript. Let’s run it down:

  • First, we use the JavaScrpt Array() object’s push() method to dynamically add an element to the array. It just so happens that this new element is an object literal, with two properties. Its index becomes 3.
  • Next, we use the same push() method to dynamically add another element to the array. This new element is an anonymous function. Its index becomes 4.
  • Next, we create a new property for our array called “testMe”. This new property happens to be an anonymous function. It has NO index, because it is NOT an element in the array, just a new property that we have added.
  • Next, we use the console to check the array’s length property, which is now “5”, and we inspect it.
  •  Dont’ forget it is an array, but it is also sill an object; Array() inherits from Object(). When inspecting the object, we see that our two uses of push() did, in fact, add two new elements to the array; one is an object, the other is an anonymous function. We also have “testMe”, wich is a new property of arr.
Ok, so what happens if we attempt to access the two functions that we added? Oh, they will run just fine, but remember: one is an element in the array, the other is a property of the arr object. So we access them differently:

Example # 4:

The output for Example # 4 would be:

In each case, we are simply executing a function. It’s just that in the first case, that function is an element in the “arr” array. So, we have to access it using its index, which happens to be “4”. This can get tricky fast, and care should be taken in doing this kind of thing, but just to illustrate a point: array elements in JavaScript can be of any data type. In the second case, we access the function by its name “testMe”, because it is a PROPERTY of the array, not an element. Much easier, and there are no issues, because “testMe” will always be “testMe”, so it’s easy to access.

Summary

This is all pretty overboard. I mean, don’t we usually want this: var arr = [“mon”,”tues”,”wed”] ? Well, yes. Most of the time we do. But the point of this post is two-fold:

  1. JavaScript does NOT support associative arrays. Period.
  2. Arrays are objects, so properties can be added any time. Those properties can be any data type.

In JavaScript, arrays are best used as arrays, i.e., numerically indexed lists. The great thing is that those elements in the array can be of any data type. Index # 0 can be a string, # 1 can be an object, # 2 can be an anonymous function, and # 3 can be another array.

But once you start doing things like this: arr[“drink”] = “beer”, you are swimming in somewhat dangerous waters. Unless you really know what you are doing, you will get odd behavior because arr[“drink”] is NOT a numerically indexed “member” of the array (it is not an array “element”), and does NOT have the relation to arr[0] and arr[1] that you may think it does.

As soon as you start doing things like: arr[“drink”] = “beer”, it is time to consider putting this key-value pair in an object literal. You don’t have to, but it’s a better way to manage your data, and the approach leverages JavaScript’s strengths, instead of wrestling with the somewhat odd nature of it’s underlying architecture.

P.S.: If you really wanna see some odd JavaScript array behavior, try this:

The strange output of this one is for another discussion : – )

Helpful links for associative arrays in JavaScript