More Dynamic - Less Work – Misko Hevery – History



More Dynamic - Less Work – Misko Hevery – History

1 1


angular-intro

Introduction to AngularJS for those who already know how to program other languages. This is for adept or intermediate, not intended for a first time programmer.

On Github rhettl / angular-intro

More Dynamic - Less Work

Misko Hevery

  • Founder
  • Google Employee
  • Agile Coach
  • Automated Testing Expert
  • Originally written as a 20% project to speed up the development time of some google internal websites.
  • is a testing expert
  • Teaches unit and end-to-end testing to google employees.

History

Concept: 2009 Original Release 0.9.0 Oct 20, 2010 Most Common 1.0.8 Aug. 22, 2013 Current Stable 1.2.1 Nov. 14, 2013

Most notable features

  • Two way binding
  • End-to-end testing
  • MVC / MVW
  • Re-usable Components
  • Declarative
  • Extends HTML
  • Dependency Injection
  • Q & Promises/A+

These are some of the features Angular is most notable for currently, but they are not why it was originally written. It was written just to be fast to develop and easily testable.

MVW: Model / View / Whatever

Now we will review MVC theory and how AngularJS sees MVC

MVC: The View

The first part to look at is the view in my opinion. AngularJS really looks at the view differently the most other frameworks out there, esp. jQuery.

In AngularJS the View:

  • Is a First Class Citizen!
  • Controls where data goes
  • Controls User-Interactivity
  • Dictates the Final State of the Data
Nothing but what is on the slide

AngularJS View vs. jQuery View

jQuery: HTML/View is an anchor point so jQuery can insert DOM as it sees fit

We all know jQuery and how it interacts with the DOM.

  • Write an anchor point in the DOM using CSS Selectors, ID, Class and Tag
  • In jQuery, select the anchor and manipulate it however the JS controller sees fit.
  • The DOM/View doesn't have any control over what is placed inside
  • meaning Designers MUST understand JavaScript to fully code a page.

In AngularJS

  • The Controller exposes editable points
  • The DOM/View Uses those points as it sees fit.
  • The View decides where loops, dynamic titles, and JS Dropdowns go
  • The Controller allows special functions, but eh view decides when and where they are used.

Hello {{ who }}

Hello !

In this example, I am ONLY using the view. Nothing more nothing less.

I have assigned part of the DOM to listen to a variable named `$scope.who` and an input to manipulate `$scope.who`

How it works

<div ng-app="">
    Hello {{ who }}!<br><br>
    <input ng-model="who" type="text">
</div>

This is the whole code. Notice:

  • ng-app
  • Template Handlebars ``
  • ng-model

Introducing

  • ng-app
  • {{ expression }}
  • ng-model
  • $scope
  • ng-app tells AngularJS what ITS DOM elements are so it knows what to work with and what to ignore.
  • The handlebars tell angular where to bind readable data. Can also do functions, math, and ternary statements
  • ng-model creates a 2-way link to allow for editing data
  • Abstraction Layer ==> see next slide

$scope
  • Abstraction Layer
  • null
    default
  • 2-way binding
  • The `$scope` is an abstraction layer which makes it easier to interact with public controller data and functions and utilizes dirty-data-checking
  • All uninitialized variables start out as `null`. Null equates to a blank string or a zero int
  • Because of dirty checking, `$scope` can be set from Controller OR View and be reflected everywhere.

MVC: The View

Part 2

The view has many more parts to it. Templates and Models are just the beginning.

Hello World

List the people you want to say hello to:

AngularJS has built in functions to help simplify changing data types. In this example we:

  • transfer a comma separated list to an array of strings in the `$scope`
  • Have a repeater say hello to each person on the list
  • Ignore all non names: ex '', ','
  • Demonstrate Directives

How it works

<div ng-app="">
    List the people you want to say hello to:<br><br>
    <input ng-list="" ng-model="who" type="text"> <br>
    <ul>
        <li ng-repeat="person in who">
            Hello {{person}}!
        </li>
    </ul>
</div>
  • ng-list is an example of a directive.
  • Directives are AngularJS code which modify the normal behavior of a DOM element. Often they can have their own scope and can modify the WHOLE look of an element
  • Notice
    • ng-list
    • ng-repeat

Introducing

  • ng-list
  • ng-repeat
  • Directive ng-list takes in a string of comma separated values, splits, trims, reduces, and outputs an array of strings
  • Directive ng-repeat automates a for loop tied to the `$scope` for every item added to the array or object a new child is created. The same goes for removing objects
  • ng-repeat works for arrays AND objects but has different syntax for each.
  • Arrays are easier to use with ng-repeat

How many is that?

You said hello to people

You said hello to nobody?!?!

Notice:
  • Counting the people in `$scope.who` array.
  • Pluralization for 0, 1 and >1
  • Multiple use of the same variable in the view

Great but minor splendors

<p>You said hello to {{ who.length }} people</p>
<p>
    You said hello to
    <ng-pluralize count="who.length || 0" when="{'0': 'nobody?!?!',
                            'one': '1 person.',
                            'other': '{} people!'}"></ng-pluralize>
</p>
<input ng-model="who" type="text" ng-list="">
  • {{ who.length }}
  • ngPluralize
  • Multiple uses of
    $scope.who
  • is standard javascript for finding # of elements in an array.
  • ngPluralize is allowing for correct grammar in areas where it is needed. ngPluralize also includes advanced functionality
  • ngPluralize is also a directive. Notice that directives can be Tags, Attributes, Classes or even Comments
  • `$scope.who` has been referenced here 3 times. It can be referenced as many times as you like pretty much.
  • Example of a short-circuit in the wild. This one says who.length, unless who does not exist or is not an array, in that case use 0

Trim the fat ...

Only These people:
  • Hello Rhett!
  • Hello Ali!
  • Hello Ray!
  • Hello Maria!
  • Hello Jannah!
  • Hello Naz!
  • Hello David!

Notice here that anything you type in will limit the output of the repeat. It is using filters to modify the array before the repeat get it.

How it works

<div ng-init="people = [
        {name:'Rhett'},
        {name:'Ali'},
        {name:'Ray'},
        {name:'Maria'},
        {name:'Jannah'},
        {name:'Naz'},
        {name:'David'}
        ]">
    Only These people:<br><br>
    <input ng-model="onlyMe" type="text"><br>
    <ul>
        <li ng-repeat="person in people | filter:onlyMe">
            Hello {{person.name}}!
        </li>
    </ul>
</div>

Notice

  • ng-init creating the initial list of people
  • the input is tied to `$scope.onlyMe`
  • the repeat is followed by `| filter:onlyMe`

Introducing

  • ng-init
  • | (pipe)
    • filter
    • currency
    • date
    • orderBy
    • Etc...

Notice:

  • ngInit - As soon as angular loads this section of the DOM into memory, it round the code in ngInit
  • Pipe works just like in linux to pass the argument before it into the command after it.
    • filter will show only matching text
    • currency expects a number and outputs currency to your specifications.
    • date formats a JS Date object or milliseconds since epoch and formats it how you define
    • orderBy will order by the object attribute you specify in ASC or DESC orders
    • Many many more.

MVC: The View

Part 3

We are nearly done with the view and its powers. But the view can do so much more! By now I hope you are getting that the view has a lot of power in AngularJS

Clicks are declarative

Counter:

<span>Reset</span><a>+1</a><button>+5</button>

Notice

  • Functions are called dispite clicking on span, anchor, or button
  • anchors automatically prevent default
  • none of this is outside the view

How it works

<h3>Counter: {{ counter }}</h3>
<span ng-click="counter = 0">Reset</span><br>
<a ng-click="counter = counter + 1">+1</a><br>
<button ng-click="counter = counter + 5">+5</button>

This is the phone code.

A counter is set as `$scope.counter` two click handlers are set in place to increment the counter one click event sets it to zero

Introducing ng-click

  • ng-click="myFunc(variable)"
  • ng-click="variable = variable + 1"
  • ng-click="variable = variable + 1; myFunc(variable)"

ngClick is a directive with many traits. It can:

  • Run a function attached to the `$scope`
  • Run a direct command operating on elements in the `$scope`
  • Do Both

It also has built in that any anchor or button with ng-click will automatically prevent default.

Other directives

Here is a list of other directives that are extremely useful to the view.

  • ngClass will turn on 1 or more classes dynamically based on either strings or boolean statements
  • ngHref will help anchors dynamically link to the right place by altering the href of a link according to your template
  • ngShow will show a node based on a boolean statement. Hide does the opposite
  • ngSwitch works almost like a switch case for the view
  • ngSrc is exactly like ngHref for images
  • ngCloak hides unwanted elements from view UNTIL angular has loaded.
  • ngFocus works exactly like ngClick but when someone focuses on an element
  • ngBlur does the same as ngClick and ngFocus but on blur.

There are many more. To read more about these directives and others, click on any of the directives

MVC: The Controller

The controller is just as powerful as the View and is as testable, but only if it is used properly.

  • AngularJS Controller !== jQuery Code. Don't try to use them the same way.
  • AngularJS Controller should be used to initialize data and set up function for the view.
  • Don't try to control the View directly the controller should try to work like an API making some things public and leaving some things private

ng-controller

<div ng-app="" ng-controller="myAppCtrl">
    {{ variable }}
    <span ng-click="myFunc('This will appear in an alert')">Click Me</span>
</div>
var myAppCtrl = function($scope){
    $scope.variable = 'Anything I want';
    $scope.myFunc = function(text){
        alert(text);
    };
};

the top is in the view and the bottom is in the controller

  • See the top uses ngController to link this section of code to either a function stored in cache as `myAddCtrl` OR a function named myAppCtrl
  • `$scope` is a parameter of the controller function
  • in the view === `$scope.variable` in the controller
  • This also demonstrates how variables may be sent back to the controller's functions through the view

Controller Structure

function myController($scope, $timeout, myFactory){
    // Variable Instantiation
    var a, b, c;
    var obj = {};
    var fac = new myFactory();

    // Function Instantiation
    var myFunc = function(text){
        alert(text);
    }
    var initFunc = function(){
        //do initialization stuff here
    }

    // Scope Instantiation
    $scope.myVar = 1;
    $scope.obj = {name: "test"};
    $scope.myFunc = myFunc;
    $scope.round = Math.round;
    $scope.addAB = function(){
        $scope.myVar = a + b;
    }

    // Initiate processes or data with functions
    initFunc();
    fac.init();

}

This is how I like to structure my Controllers.

  • I like to group variables together
  • group functions together
  • group scope related declarations together
  • and always put initialization scripts at the bottom

The order isn't all important, but I really prefer instantiation and declaration at top and function initialization at that bottom.

Notice

  • I am pulling in more than the scope
  • `$timeout` is another helper object
  • i can also pull in models and other data
  • that order is not important, angular takes care of placing the right objects where you ask for them

NOT JQUERY

function myController($scope, $timeout, myFactory){
    var el = angular.element('#someID');
    el.find('div').hide();
}

No really don't do this!!

The controller doesn't work like jQuery. It isn't for writing random code to change the DOM. Instead use it to allow the DOM/View to make it's own changes.

  • This code will be impossible to test
  • disrupt Angular's groove making angular angry
  • Sometimes the cheating way is to initialize a jQuery object from the controller but best recommendation is just don't use jQuery at all. Try to put all DOM manipulation in the View or a directive

The Application

Putting it all together

Lets talk about how an application fits together modularly.

Creating an application and module

<div ng-app="myApp" ng-controller="myAppCtrl">
    {{ variable | whatsMyName }}
    <span ng-click="myFunc('This will appear in an alert')">Click Me</span>
</div>
angular.module('myApp', [])
    .filter('whatsMyName', [function(){
        return function(input){
            return input + " is my name!";
        }
    }])
    .controller('myAppCtrl', ['$scope', function($scope){
        $scope.variable = 'Anything I want';
        $scope.myFunc = function(text){
            alert(text);
        };
    }]);

In this example notice the scafholding for the application.

  • ng-app links the application to the module
  • ng-controller is still linking the controller to the DOM
  • we attached a new filter call `whatsMyName` to the application and used it in the View
  • the `.module('name', requisites)` format. Later in actual examples I'll show you how this works

Directives

Extending HTML

Directives are for altering HTML or creating new behaviors in HTML

examples of when one might make a directive

  • Making a spinner input
  • making a popup box
  • making custom dropdown menus
  • Making Popovers
  • Any place where one migth want a repeatable object that instantiates and de-instantiates itself

Spinner object

<input spinner="" ng-model="count" min="-5" max="5">
angular.module('myApp', []).directive('spinner', [function(){
    return {
        restrict: 'A',
        scope: {
            currentNum: '=ngModel',
            min: '@?',
            max: '@?',
            inc: '=?'
        },
        replace: true,
        template:
            '<div class="spinner">' +
                '<input ng-model="currentNum">' +
                '<span class="plusOne" ng-click="add(inc)">' +
                    '<i class="chevron-up"></i>' +
                '</span>' +
                '<span class="minusOne" ng-click="subtract(inc)">' +
                    '<i class="chevron-down"></i>' +
                '</span>' +
            '</div>',
        link: function(scope, iElement){
            if (typeof scope.min === 'undefined'){
                scope.min = -1000000000000000;
            }
            if (typeof scope.max === 'undefined'){
                scope.max = 1000000000000000;
            }
            if (typeof scope.inc === 'undefined'){
                scope.inc = 1;
            }

            scope.add = function(num){
                var newNum;
                num = (typeof num === 'undefined' ? 1 : num);

                if (num + scope.currentNum > scope.max){
                    newNum = scope.max
                } else {
                    newNum = num + scope.currentNum;
                }

                scope.currentNum = newNum;
            };
            scope.subtract = function(num){
                var newNum;
                num = (typeof num === 'undefined' ? 1 : num);

                if (scope.currentNum - num  < scope.min){
                    newNum = scope.min
                } else {
                    newNum = scope.currentNum - num;
                }

                scope.currentNum = newNum;
            };
        }
    }
}]);

First notice:

  • Directives must be in an application/module
  • Restrict: 'A' will restrict the directive to only work when 'spinner' is an attribute as opposed to Element, Class or Comment
  • It creates its own child scope and sets some variables by default
  • The DOM template is loaded here OR using a templateURL
  • Link is the function runs just after the template is compiled and replaces the original DOm element
  • Inside the Link:
    • Link function takes in the scope and the container element
    • I initialize uninitialized variables
    • and add some helper functions that would normally be on the controller
    • While I can add a controller to this whole thing, I don't feel the need if since my overall work is so small

We can ise the spinner directive anywhere within the ngApp as name times as we like.

Recommendations and Best practices:

  • Manipulate the DOM here NOT in Controller
  • Keep as self enclosed as possible
  • Directives CAN communicate upwards but life is easier when they don't
  • There is nothing wrong with using jQuery or other JavaScript DOM manupulation. It should almost all happen here in the directive.
  • The more enclosed and encapsulated the easier to test and debug.
  • Directives can emit events which controllers can listen for, but again they can make testing frustrating
  • Directives can be as simple or as complicated as you want

MVC: The Model

Providers

Providers are the model. There are several types of providers.

Provider Types

  • Values
  • Constants
  • Factories
  • Services
  • Providers

There are different types of providers that are ment to do different things

  • Values are instantiated after most other objects but before controllers. Singleton
  • Constants are always the first to be instantiated. They should be objects. Singleton
  • Factories are instantiated inside of the controller. You can make as many of them as you like.
  • Services are instantiated before controllers and are Singletons
  • All of these are providers. When creating any of them, the provider function is called and produces the final object.

Factories and Services

  • Encapsulate
  • Feel free to put getters, setters and storage inside
  • Store as little as necessary in services, they are singletons

Example of Factory

angular.module('myApp', [])
    .factory('myLogs', ['$http', function($http){
        var myFact = function(){
            //init script
            this.reqUrl = '/includes/assets/scripts/rest/json.php';
            this.logs = [];

            this.query = {
                func: 'get_logs',
                args: []
            }
        }
        myFact.prototype.getLogs = function(time){
            this.query.args = [time];
            $http.get(this.reqUrl, this.query)
                .success(function(data){
                    this.logs = data;
                }).error(function(){
                    this.logs = [];
                    console.log('there is a problem!!');
                });
        };
    }])
    .controller('myCtrl', ['$scope', 'myLogs', function($scope, myLogs){
        $scope.fac = new myLogs();
    }]);
<div ng-app="myApp" ng-controller="myCtrl">
    <ul>
        <li ng-repeat="log in fac.logs" ng-bind="log.entry"></li>
    </ul>
    <button ng-click="fac.getLogs()">Update</button>
</div>
Notice
  • `.factory(name, ['req', function(req){}])
  • Creating an object for my factory and assigning it functions and variables
  • Query is self contained `this.query`
  • logs stay self contained `this.logs`
  • `myLogs` is fed into the controller as a requirement
  • a new myLogs is created, because this is a factory not a service
  • I wanted myLogs to be accessible so I placed it on the scope.
  • $http is another helper, this one is like the ajax method in jQuery
  • I am showing all the logs I want in almost no view or controller code.
  • My factory is fully testable

Roles

How it is all supposed to work together

to wrap it all up we have 4 main components, and now I will expain how they are to work together to make our lives easier

View

  • Declarative
  • Powerful
  • for designers

Try to keep it simple

If it only deals in how things look it belongs in the view. Ex: Simple Pagination

Simple pagination is when it has nothing to do with the model. All results are returned and the view is just combing through them

Simple pagination doesn't have anything to do with how the data is returned or what data is returned so there is little reason to place it in the controller or the model. If it can cleanly go into the View, put it there.

Model/Provider

  • Factory
  • Service

Most of the time you will deal with these two.

Separate Concerns

What belongs in the model is anything pertaining to the fetching, holding, or checking of a specific type of dataThere are also more general models

What SHOULD go in the model are things like getters for THAT type of information and it is fine for the returned information to stay in the model

The controller Should hold the burden of going the model's dirty work

Controller

Think of this as:

  • A Data Linking Function
  • An initiator
  • Decider of Public/Private

The controller is a place to:

  • pull in the tools you need
  • get them ready for use by the View
  • direct the incoming and outgoing data to the right homs

Don't use it as a place to initialize jQuery or manipulate the DOM

Directives

  • Manipulate the DOM
  • Make it seem like HTML learned new tricks
  • The less communication INTO and OUT FROM the better

Ideally, the best directives appear to work without you touching them from the controller or the view.

Modularity

All elements should be as modular as possible.

  • Model => Multi. Ctrls
  • Ctrl => Multi. Views
  • Direct => Multi. Everything
  • View !=> Multi. Controllers
  • Models should ideally work for ANY controller
  • Controllers should be written so thet don't reference the view at all allowing for any view to work with it
  • Directives should be written so ANY view can use it
  • Views will almost never go with another controller. they are too specific.

GOTO EXAMPLES