Skip to content

Instantly share code, notes, and snippets.

@mdsaleemj
Forked from dwayne/01-intro.md
Created November 4, 2015 09:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mdsaleemj/d69345f6ac0bd68ad03f to your computer and use it in GitHub Desktop.
Save mdsaleemj/d69345f6ac0bd68ad03f to your computer and use it in GitHub Desktop.
My notes from the book "ng-book: The Complete Book on AngularJS by Ari Lerner".

Introduction

Author: Ari Lerner.

AngularJS offers a single framework that can be used to build dynamic, client-centric applications. It provides:

  • Module support
  • DOM manipulation
  • Animations
  • Templating
  • Two-way data binding
  • Routing
  • History
  • Ajax
  • Testing

Foreword by Dan Wahlin. You can also find Dan on Twitter.

About the Book

It is packed with solutions you need to be an AngularJS ninja. Topics include:

  • Interacting with a RESTful web service
  • Building custom reusable components
  • Testing
  • Asynchronous programming
  • Building services
  • Providing advanced visualizations

Audience

  • New to AngularJS
  • Working knowledge of HTML and CSS
  • Familiarity with basic JavaScript

Developer Environment

Links

The Basics of AngularJS

The official AngularJS introduction describes AngularJS as a:

client-side technology, written entirely in JavaScript. It works with the long-established technologies of the web (HTML, CSS, and JavaScript) to make the development of web apps easier and faster than ever before.

It is a framework that is primarily used to build single-page web applications.

AngularJS augments HTML to give it native Model-View-Controller (MVC) capabilities.

AngularJS is open-sourced under The MIT License.

Interesting

dig performs DNS lookups and displays the answers that are returned from the name server(s) that were queried. Try

$ dig google.com

to see the DNS server response.

Links

Data Binding and Your First AngularJS Web Application

The chapter starts off by writing a hello world application to show off data binding.

AngularJS creates live templates as a view. Individual components of the views are dynamically interpolated live.

Basic Setup

  1. Include https://ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular.js in the HTML.
  2. Set the ng-app attribute on an element in the DOM.

The ng-app attribute declares that everything inside of it belongs to the Angular app. The only components that will be affected by Angular are the DOM elements that are declared inside of the one with the ng-app attribute.

Automatic data binding gives us the ability to consider the view to be a projection of the model state. Any time the model is changed in the client-side model, the view reflects these changes without writing any custom code. It just works.

Recommended Reading: GUI Architectures

How Hello World Works

When Angular thinks that the value could change, it will call $digest() on the value to check whether the value is "dirty". This process is called dirty checking. The dirty check is done inside its event loop to ensure everything is consistent.

What we did was to bind the "name" attribute to the input field using the ng-model directive on the containing model object ($scope). This means that whatever value is placed in the input field will be reflected in the model object.

Miscellaneous

The model object refers to the $scope object. The $scope object is a JavaScript object whose properties are all available to the view and with which the controller can interact.

Bi-directional in Angular means that if the view changes the value, the model observes the change through dirty checking, and if the model changes the value, the view updates with the change.

Declaring ng-controller attribute on a DOM element says that all of the elements inside of it belong to the controller.

It's considered a best-practice in Angular to bind references in the views by an attribute on an object, rather than the raw object itself.

Modules

  • Placing code in the global namespace is a BAD idea
  • It can cause collisions that are tough to debug
  • Which costs development time

This chapter talks about how to write efficient, production-ready controllers by encapsulating functionality in a single core unit called a module.

In Angular, a module is the main way to define an AngularJS app. An app can contain several modules, each one pertaining to specific functionality.

Advantages of modules include:

  • Keeping the global namespace clean
  • Making tests easier to write and keeping them clean so as to more easily target isolated functionality
  • Making it easy to share code between applications
  • Allowing the app to load different parts of the code in any order

The Angular module API allows us to declare a module using the angular.module() API method.

// the setter method
angular.module('myApp', []); // name of module, list of dependencies (or injectables)

We can always reference the module by using the same method with only one parameter.

// the getter method
angular.module('myApp'); // this method fetches the app

From here, we can create our applications on top of the angular.module('myApp') variable.

When writing large applications:

  • Create several different modules to contain your logic
  • Creating a module for each piece of functionality gives the advantage of isolation in which to write and test large features

Angular modules have properties we can use to inspect the module.

The name (string) property on the module gives the name of the module as a string.

The requires (array of strings) property contains a list of modules (as strings) that the injector loads before the module itself is loaded.

Scopes

A core of any Angular app. The scopes of the application refer to the application model. Scopes are the execution context for expressions. The $scope object is where we define the business functionality of the application, the methods in our controllers, and properties in the views.

Scopes serve as the glue between the controller and the view.

  • Before our app renders the view to the user
  • The view template links to the $scope
  • And the app sets up the DOM to notify Angular for property changes

Scopes are the source of truth for the application state. Because of live binding, we can rely on the $scope to update immediately when the view modifies it, and we can rely on the view to update when the $scope changes.

$scope in angularJS are arranged in a hierarchical structure that mimics the DOM and thus are nestable: We can reference properties on parent $scopes.

Scopes provides the ability to watch for model changes. They give the developer the ability to propagate model changes throughout the application by using the apply mechanism available on the scope. We define and execute expressions in the context of a scope; it is also from here that we can propagate events to other controllers and parts of the application.

The $scope View of the World

  • Angular starts to run
  • It generates the view
  • It creates a binding from the root ng-app element to the $rootScope
  • The $rootScope eventually becomes the parent of all $scope objects

The $rootScope object is the closest object we have to the global context in an Angular app. It's a bad idea to attach too much logic to this global context, in the same way that it's not a good idea to dirty the JavaScript global scope.

  • The $scope object is a plain old JavaScript object
  • The $scope object is the data model in Angular

All properties found on the $scope object are automatically accessible to the view.

Through Angular, we can use different types of markup in a template. These include:

  • Directive: the attributes or elements that augment the existing DOM element into a reusable DOM component
  • Value bindings: the template syntax {{ }} binds expressions to the view
  • Form controls: user input validation controls

Scopes have the following basic functions:

  • They provide observers to watch for model changes
  • They provide the ability to propagate model changes through the application as well as outside the system to other components
  • They can be nested such that they can isolate functionality and model properties
  • They provide an execution environment in which expressions are evaluated

You can think of scopes as view models.

$scope Lifecycle

When the browser receives a JavaScript callback that executes inside of the Angular execution context the $scope will be made aware of the model mutation. If the callback executes outside of the Angular context, we can force the $scope to have knowledge of the change using the $apply method.

After the scope expression is evaluated and the $digest loop runs, the $scope's watch expressions will run dirty checking.

  • Creation
    • When we create a controller or directive, Angular creates a new scope with the $injector and passes this new scope for the controller or directive at runtime.
  • Linking
    • When the $scope is linked to the view, all directives that create $scopes will register their watches on the parent scope. These watches watch for and propagate model changes from the view to the directive.
  • Updating
    • During the $digest cycle, which executes on the $rootScope, all of the children scopes will perform dirty digest checking. All of the watching expressions are checked for any changes, and the scope calls the listener callback when they are changed.
  • Destruction
    • When a $scope is no longer needed, the child scope creator will need to call scope.$destroy() to clean up the child scope. Note that when a scope is destroyed, the $destroy event will be broadcasted.

Links

Controllers

Controllers exist to augment the view of an AngularJS application. The controller adds additional functionality to the scope of the view. We use it to set up an initial state and to add custom behavior to the scope object.

When we create a new controller on the page, Angular passes it a new $scope. This new $scope is where we can set up the initial state of the scope on our controller. Since Angular takes care of handling the controller for us, we only need to write the constructor function.

Setting up an initial controller looks like this:

function FirstController($scope) {
  $scope.message = "hello";
}

Best Practice: Name controllers as [Name]Controller rather than [Name]Ctrl.

Angular will call the controller method when it creates the scope.

Best Practice: Namespace controllers by first creating a module and then creating the controller within the module.

var app = angular.module('app', []);
app.controller('FirstController', function ($scope) {
  $scope.message = "hello";
});

Using controllers allows us to contain the logic of a single view in a single container.

Best Practice: Keep slim controllers.

The controller is not the appropriate place to do any DOM manipulation, formatting, data manipulation, or state maintenance beyond holding the model data.

Controller Hierarchy (Scopes Within Scopes)

Every part of an AngularJS application has a parent scope. At the ng-app level this scope is called $rootScope.

One exception: A scope created inside of a directive is called the isolate scope.

With the exception of isolate scopes, all scopes are created with prototypal inheritance (i.e. they have access to their parent scopes).

How it works

For any property that AngularJS cannot find on a local scope, AngularJS will crawl up to the containing (parent) scope and look for the property or method there. If AngularJS can't find the property there, it will walk to that scope's parent and so on and so forth until it reaches the $rootScope. If it doesn't find it on the $rootScope, then it moves on and is unable to update the view.

Expressions

Expression are roughly the result of an eval(javascript). Properties include:

  • All expressions are executed in the context of the scope and have access to local $scope variables.
  • An expression doesn't throw errors if it results in a TypeError or a ReferenceError.
  • They do not allow for any control flow functions (conditionals; e.g., if/else).
  • They can accept a filter and/or filter chains.

N.B. Expressions all operate on the containing scope within which they are called.

Parsing an Angular Expression

Although your Angular app will run parse for you automatically when running the $digest loop, sometimes it's useful to parse an Angular expression manually.

Angular evaluates expressions by an internal service (called the $parse service) that has knowledge of the current scope.

To manually parse an expression, we can inject the $parse service into a controller and call the service to do the parsing for us.

Interpolating a String

Although it's uncommon to need to manually interpolate a string template in Angular, we do have the ability to manually run the template compilation. Interpolation allows us to live update a string of text based upon conditions of a scope, for instance.

To run an interpolation on a string template, we need to inject the $interpolate service in our object.

Filters

A filter provides a way to format the data we display to the user. Filters are invoked in HTML with the | (pipe) character inside the template binding characters {{ }}. For e.g. to use the uppercase filter,

{{ name | uppercase }}

We can also use filters from within JavaScript by using the $filter service. For e.g. to use the lowercase filter,

app.controller('DemoController', ['$scope', '$filter',
  function ($scope, $filter) {
    $scope.name = $filter('lowercase')('Dwayne');
  }
]);

To pass an argument to a filter in the HTML form, we pass it with a colon after the filter name (for multiple arguments, we can simply append a colon after each argument). For e.g. the number filter allows us to limit the number of decimal places a number can show.

{{ 123.456789 | number:2 }} <!-- Displays: 123.46 -->

We can use multiple filters at the same time by using two or more pipes.

Making Our Own Filter

To create a filter, we put it under its own module.

var filters = app.module('app.filters', []);

filters.filter('capitalize', function () {
  return function (input) {
    if (input) {
      return input[0].toUpperCase() + input.slice(1);
    }
  }
});

Filters are just functions to which we pass input.

Resources

Form Validation

AngularJS supports form validation with a mix of the HTML5 form validation inputs as well as with its own validation directives. It makes it pretty easy for us to handle client-side form validations without adding a lot of extra effort.

To use form validations:

  • Ensure that the form has a name associated with it
  • Use the novalidate attribute on the form element, as it prevents the browser from natively validating the form
<form name="form" novalidate>
  <!-- ... -->
</form>

Control Variables in Forms

AngularJS makes properties available to us (in the format formName.inputFieldName.property) on the containing $scope object available to us as a result of setting a form inside the DOM. These properties enable us to react to the form in real time.

formName.inputFieldName.$pristine is a boolean property that tells us whether the user has modified the form. It is true if the user hasn't touched the form, and false if they have.

formName.inputFieldName.$dirty is a boolean property that tells us if and only if the user has actually modified the form. It is set regardless of validations on the form.

formName.inputFieldName.$valid is a boolean property that tells us whether or not the form is valid. If the form is currently valid, then it would be true.

formName.inputFieldName.$invalid is a boolean property that tells us whether or not the form is invalid. If the form is currently invalid, then it would be true.

formName.inputFieldName.$error contains all of the validations on a particular form and a record of whether they are valid or invalid. If a validation fails, then this property will be true; if it is false, then the value has passed the input field.

When AngularJS is handling a form, it adds specific classes to the form based upon the current state of the form (i.e. if it's currently valid, unchanged, etc.). These classes are named similarly to the properties that we can check, as well.

These classes are:

  • ng-pristine
  • ng-dirty
  • ng-valid
  • ng-invalid

There are some really good examples in this section of the book. Consult the book to find out more.

Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment