Wednesday, May 14, 2014

Random Excerpt from Ch 4

Data Models in AngularJS


Developers new to AngularJS might be somewhat confused when the landing page of angularjs.org mentions that AngularJS models are plain POJOs or plain old JavaScript objects, but then there is this thing called $scope which is the data that is bound to the HTML in AngularJS' data-binding.  The distinction is that the last sentence was not entirely correct. It should be "which is what holds" the data that is bound to the HTML. To be clearer, the data in AngularJS is modeled in a basic JavaScript object, which may be converted to JSON and visa versa. That model is then attached to an AngularJS $scope object which provides a context for the data in the DOM hierarchy.

4.0 A model and a context in AngularJS
// Model is a plain old JavaScript object- no getters, setters, or inheritance
// It may start life as a JSON string from a REST call or from form field inputs
var aDataModel = {
    field_0: 'a string',
    field_1: 5
    field_2: false,
    field_3: {
        subField_0: ['an array']
    }
};

// $scope is a contextual container for a model
function ourController($scope){

       // Our data is now bound to the view, and so is another property and method
       // not a part of our data.
       $scope.data = aDataModel;
       $scope.method_0 = function(){};
       $scope.property_0 = '';
};

Models in AngularJS do not need to be part of any inheritance hierarchy or have OO style setters and getters, or other methods. On the other hand, scopes in AngularJS usually (but not always) are part of an inheritance hierarchy, and have a number of built-in methods and properties. Any developers with Backbone.js experience know that data models in that toolkit are inherited and have OO style encapsulation methods.

The reasoning behind using POJOs for models in AngularJS is for increased ease of testing, maintenance, and reusability. Tomorrow if we decide to switch to another framework or even Web Components, untangling the data will require minimal effort.

Data Representation in the View 


The $scope object in AngularJS is the container that allows both the view and the controller access to the data. When we attach our data object to a $scope object in AngularJS we have effectively created a ViewModel as most developers would define MVVM.  The AngularJS team refer to AngularJS as an MVC framework perhaps for marketing purposes or for attractiveness to back-end developers, but the distinction between VM and C is fuzzy at best and probably depends more on whether it is used for web apps or components.

Expressions and Bindings


In compiled template HTML, or the view, $scope data fields, methods, and properties are normally accessed and manipulated via AngularJS expressions. AngularJS expressions are snippets of JavaScript with a few distinctions. Expressions evaluate against the scope of the current element or directive, whereas, JavaScript expressions embedded in HTML always evaluate against the global (usually window) context. Attempting to evaluate a property that is null or undefined fails silently rather than throwing a reference error, and control flow logic (if, else, while, etc.) is not allowed since computational logic belongs in controllers, not templates.

4.1 Difference between AngularJS and JavaScript expressions
// setting and accessing a scope context
<div id="expExample" ng-controller="ourController" ng-click="angularExpression()"       onclick="globalExpression()">
    <span> {{ aScopeProperty }} </span>
    <input type=text ng-bind="aScopeProperty"/>
</div>

The code above is short, but represents much of a developer's everyday use of AngularJS. We are applying a scope to div#expExample, and binding properties and methods to elements on or within the scope.

The default AngularJS binding delimiters are {{}}. The can be changed if there is conflict with other client or server template delimiters.  {{}} is the read-only version of ng-bind.  {{}} is typically used when displaying a scope property as output text in HTML. ng-bind is typically used to give two-way binding to form input elements, although it can also be used in cases where a delay in the JavaScript execution thread might cause an undesired momentary flash of the raw "{{ var }}". Behind the scenes, the expressions are passed to the $eval() method on the $scope object analogous to JavaScript's eval() method with the exception that $eval() evaluates against the $scope context. It is a best-practice to keep statements for $eval() simple as in a property name or a function call that returns a value.

$scope creation and destruction


Automatic Scopes


The concept of scope, scope hierarchies and other inter-scope relationships are a source of massive hair loss among AngularJS noobs. "Why does a bound property change when I do this, but not when I do that?" The answer is almost always because of an attempt to access the same property, but within different, unexpected scope contexts due to a scope popping up out of nowhere for some reason. So it important to understand the various AngularJS activities that result in automatic scope creating- one of the downsides to a massive reduction in boilerplate.

So starting from the top, when an AngularJS app module is defined and attached to the DOM via ng-app, a root scope is automatically created on the ng-app element and is accessible from any sub scope via $rootScope. That said, my advice is to avoid at all costs directly referencing the  $rootScope  property from an inner scope as it leads to highly coupled dependencies. Accessing a $rootScope  from a UI component directive scope should never be done.

In AngularJS single page apps, scopes are most often created when ng-controller="myController" is placed in an element declaration. Any data-bindings within that DOM fragment will reference that scope until an inner element or directive that instantiates a new scope is reached.

Scopes are automatically created when a new template is inserted into the DOM via ng-route or ng-include. Same goes for a form element when given a name attribute, <form name="name">, and also for any directive that clones HTML templates, a new scope is created for each cloned template when attached to the DOM as with ng-repeat.

Manual Scope Creation


Behind the scenes, any scopes that are automatically created via one of the avenues above are done so with the $new() method, available on any existing $scope object. We may also purposely call $scope.$new()to create one manually, but the use cases are rare and is usually a clue that we are not doing something the AngularJS way.

Finally new scopes may be granted existence on purpose when a custom directive is instantiated, and we almost always want to create a new scope if our directive encapsulates a UI component. Recall the scope option in the directive definition object from the previous chapter. For the purpose of using directives to create re-usable UI components, we will focus closely on this type of scope creation.

Scope Inheritance Options for Directives


When defining a directive for the purpose of encapsulating a UI component, we typically want to do a few special tasks with any new scope that is created.  If we omit the scope option in our directive definition object, the default is no new scope, the same as if we declare scope:false. On the other hand, if we declare scope:true, a new scope is instantiated when the directive executes, but it inherits everything, prototypal style, from the parent scope.  This is the default for automatic scope creation, and the reason for the hair pulling when the property names are the same, but the values are not. 

Manipulating a value on a sub-scope creates a new variable overwriting the inherited value.

The third value for the scope option is scope:{}. This creates an isolated scope for the directive instance with no inheritance from any parent scope.  Because UI components should not know about state outside their boundaries, we typically want to use this option for our component directives. One side note with isolate scopes is that only one isolate scope can exist on an element, but this is usually not an issue with elements designated as component boundaries.

The value for the scope option does not need to be an empty object literal. We can include default properties, values and methods for the isolated scope using object literal notation. Also, the previous statement of no inheritance was not entirely correct. We can also selectively and purposely inherit specific properties from a parent or an ancestor scope, and we can also selectively accept values of the directive element attributes. Both of these abilities create some interesting options for defining APIs on our custom UI components.

Isolate Scopes as Component APIs


While directives are a great tool for creating re-usable UI components, the primary focus of the framework is toward being comprehensive for web application functionality. Therefore, there is no official AngularJS best-practice for defining UI component APIs beyond choosing a method that allows for loose coupling and dependency injection. The framework gives us multiple options for dependency injection, configuration, and API communication from the containing DOM. The option used should be the best fit for the use case and the user.

Directive API as HTML Attributes


Suppose we have some epic user-story that requires we create a pallet of UI widgets and components that might be used by designers or junior developers to create websites or webapps. The consumers of our pallet might not have a high level of programming skill, so we would want to give them a method to include any configuration or data declaratively, or as close to plain English as possible.
For example, we could create a custom search bar directive with our organization's look-and-feel that a page author could include via adding a custom element:

<my-search-bar></my-search-bar>

But this is of limited use since search bars are often needed in different contexts, locations, and with certain configurations.

If we were using jQuery UI widgets, any customization or configuration would need to happen by passing in a configuration object to the imperative statement the creates the widget on an element:

$('#elementId').searchBar({autoComplete:true});

This limits the accessibility of widget use to developers with an adequate knowledge of JavaScript.
However, via AngularJS directives we could give the page author the ability to pass in configuration information declaratively via element attributes:

<my-search-bar auto-complete="true"></my-search-bar>

removing the need to touch any JavaScript.  This is accomplished by configuring the isolate scope options in the directive definition object to use the values of matched attributes on our directive element.

4.2 Isolate scope attribute options
<my-search-bar auto-complete="true"></my-search-bar>

myModule.directive('mySearchBar', function factory(injectables) {
  var directiveDefinitionObj = {
    template: ...,
    restrict: 'E',
    scope: {
        autoComplete: '@', // matches an attribute of same name
         anotherModelName: '@autoComplete' // if using a different name
    },
    controller: ...,
    link: function postLink( ... ) { ... }
  };
  return directiveDefinitionObj;
});

Directive API as Selective Scope Inheritance 


The downside of UI component APIs using static attribute values like the example above is the restriction that only string values may be passed in since that is what all HTML attribute values are, and the value is not a live binding.  

If our use-case requires that the component consumer have the ability to pass in configuration information of all types and dynamically at run-time, then we more options available if the consumer is familiar with JavaScript and AngularJS at least somewhat.

By including a variable within AngularJS tags,
<my-search-bar auto-complete="{{ scopeVar }}"></my-search-bar>
as the value of an attribute we can create a live, read-only binding to a value on the parent scope. If scopeVar on the parent scope changes during the life of the directive instance, then so will the corresponding property on the directive scope.

Additional variable and method inheritance options for isolate scopes exist. Using '=' instead of '@' creates a live two-way binding between a value on a parent element scope and the directive scope, and using '&' allows the scope to call a function within the parent element's context. Both of these options can be useful for inner directives, but we should avoid using these options as UI component APIs since they allow a component directive to directly affect state outside its boundaries. If the world outside a component's boundaries needs to react to some change in component state, its is much better to use the publish-subscribe pattern made available to AngularJS scopes via:

$scope.$emit(evtName, args) //propagates up the scope hierarchy
$scope.$broadcast(evtName, args) //propagates down the scope hierarchy
$scope.$on(evtName, listenerFn)


Any scope with the same $rootScope can communicate via events whether isolate or not. Note that while the publish-subscribe pattern is discouraged in favor of data-binding in the official AngularJS documentation, for maintaining loosely coupled UI components, it is necessary.