AngularJS

HTML Reloaded

Ryan Casey — ryepdx.com — @ryepdx

Who am I?

  • Building websites since 5th grade
  • Recently repatriated to Javascript
  • Playing with AngularJS since mid-2013

Who are you?

  • Frontend developer with a little Javascript skill
  • Want to minimize time spent writing "glue" code
  • Ready to join the reactive revolution

Dynamic views are hard.

HTML is declarative.

Javascript is imperative.

Impera-wha-?


Imperative

Telling the computer how to do something.


Declarative

Telling the computer to do something and letting it decide how.


Hello {{yourName}}!

Imperative

(jQuery)

<!doctype html>
<html>
    <head>
        <script src="jquery.js"></script>
        <script>
            $(document).ready(function () {
                var $yourName = $("#yourName");
                var $nameText = $("#nameText");
                $yourName.keyup(function () {
                    $nameText.text($yourName.val());
                    $nameText.toggle($yourName.val() != "");
                });
            });
        </script>
    </head>
    <body>
        <label>Name:</label>
        <input type="text" placeholder="What is your name?" id="yourName">
        <h3 style="display: none">Hello <span id="nameText"></span></h3>
    </body>
</html>

Declarative*

(AngularJS)


<!doctype html>
<html ng-app>
    <head>
        <script src="angular.min.js"></script>
    </head>
    <body>
        <input type="text" placeholder="What is your name?" ng-model="yourName">
        <h3 ng-show="yourName">Hello {{yourName}}!</h3>
    </body>
</html>

*Declarativeness enlarged to show texture.

Modules

  • Declared via ng-app
  • Can be imported and used by other modules
  • Can include many on one page (but usually not necessary)

<!doctype html>

<html ng-app>

  <head>

    <script src="angular.min.js"></script>

  </head>

  <body ng-app>

    <div ng-app="hello">

      <input type="text"
        placeholder="What is your name?"
        ng-model="yourName">

      <h3 ng-show="yourName">Hello {{yourName}}!</h3>

    </div>

  </body>

</html>

angular.module('hello', []);
angular.module('hello', ['goodbye']);

Controllers

  • Mostly imperative
  • Divide modules into views
  • Interact directly with the data model

<!doctype html>

<html ng-app="hello">

  <head>

    <script src="angular.min.js"></script>

  </head>

  <body>

    <div ng-controller='helloCtrl'>

      <input type="text"
        placeholder="What is your name?"
        ng-model="yourName">

      <h3 ng-show="yourName">Hello {{yourName}}!</h3>

    </div>

  </body>

</html>

angular

  .module('hello', [])

  .controller('helloCtrl', function ($scope) {

    $scope.yourName = "Ishmael"

  });


Hello {{yourName}}!

Templates

  • Similar syntax to mustache.js
  • ng attributes & tags & classes, oh my!
    (i.e., directives)
  • Two-way data binding

A quick word on $scope.apply

Use $scope.apply(callback),

not $scope.apply().

<!doctype html>

<html ng-app="hello">

  <head>

    <script src="angular.min.js"></script>

  </head>

  <body>

    <div ng-controller='helloCtrl'>

      <input type="text"
        placeholder="What is your name?"
        ng-model="yourName">

      <h3 ng-show="yourName">Hello {{yourName}}!</h3>

    </div>

  </body>

</html>

Template Expressions

  • Primarily used as arguments to ng attributes.
  • Forgiving: undefined variables don't cause errors.
  • Evaluated against the controller's scope.
    (e.g., {{yourName}} == $scope.yourName)

<!doctype html>

<html ng-app="hello">

  <head>

    <script src="angular.min.js"></script>

  </head>

  <body>

    <div ng-controller='helloCtrl'>

      <input type="text"
        placeholder="What is your name?"
        ng-model="yourName">

      <h3 ng-show="yourName">Hello {{yourName}}!</h3>

    </div>

  </body>

</html>

More Expressions


a * b

a + b


user.name

items[index]

foo.toUpperCase()

Expression Filters

  • Alter the value of an expression
  • Can be chained
  • Take the form <expression> | <filter> [: arg]*

  1. {{ person.name }} — {{ person.money | currency }}

<body ng-app="search">

 <form ng-controller="searchCtrl">

  <p><input type="text" ng-model="search"></p>

  <ol>

   <li ng-repeat="person in people
       | filter:{ 'name':search }
       | orderBy:'money':true">

    {{ person.name }} &mdash;

    {{ person.money | currency }}

   </li>

  </ol>

 </form>

</body>

angular

.module('search')

.controller('searchCtrl', function ($scope) {

  $scope.people = [

    { name: "Joey", money: 2 },

    { name: "Flo", money: 1.75 },

    { name: "Jordan", money: 0.10 }

  ];

});

Directives

  • Create new tags and attributes!
  • Directives are why you don't need jQuery (kinda).
  • ng-show == ng_show == ng:show == ngShow

You can get with this...

<tabs>

  <pane title="English">

    Hello!

  </pane>

  <pane title="French">

    Bonjour!

  </pane>

</tabs>

...or you can get with that.

<div id="tabs">

  <ul>

    <li><a href="#en">English</a></li>

    <li><a href="#fr">French</a></li>

  </ul>

  <div id="en">

    Hello!

  </div>

  <div id="fr">

    Bonjour!

  </div>

</div>

(So how do you get with this?)

<tabs>

  <pane title="English">

    Hello!

  </pane>

  <pane title="French">

    Bonjour!

  </pane>

</tabs>

angular.module('yourApp', ['theirModule']);
var app = angular.module('components', []);

Defining tabs

app.directive('tabs', function () {

  return {

    restrict: 'E', // Can be any of A, C, or E 

    transclude: true, // Pass on parent scope? 

    scope: {}, // Inner scope definition 

    controller: tabsCtrl,

    templateUrl: 'tabsTemplate.html',

    replace: true // Replace element or insert into? 

  }

});

var tabsCtrl = function ($scope, $element) {

  var panes = $scope.panes = [];

  

  $scope.select = function(pane) {

    angular.forEach(panes, function(pane) {

      pane.selected = false;

    });

    pane.selected = true;

  }

  

  this.addPane = function(pane) {

    if (panes.length == 0) $scope.select(pane);

    panes.push(pane);

  }

};

<div>

  <dl class="tabs" data-tab>

    <dd ng-repeat="pane in panes"
        ng-class="{active:pane.selected}">

      <a href="" ng-click="select(pane)">

        {{pane.title}}

      </a>

    </dd>

  </dl>

  <div class="tabs-content" ng-transclude></div>

</div>

Defining pane

app.directive('pane', function() {

  return {

    require: '^tabs', // Require parent's controller 

    restrict: 'E', // E is for Element! 

    transclude: true,

    scope: { title: '@title' }, // =title, &title 

    link: function($scope, $elem, $attrs, tabsCtrl) {

      tabsCtrl.addPane($scope);

    },

    template:

      '<div class="content"' +

      ' ng-class="{active: selected}"' +

      ' ng-transclude></div>';

    replace: true

  };

});

<link rel="stylesheet" href="/css/foundation.min.css">
Hello! Bonjour!

Services

  • Created once and shared across controllers
  • Use signals for cross-controller communication!
  • Best for stateless, reusable code (e.g., the $http service)

angular

  .factory('myService', function() {

    return function(msg) {

      console.log(msg);

    };

  });

angular

  .controller('myController', function(myService) {

    myService("This gets logged.");

  });

angular

  .controller('myController',

  ['myService', function(mySerVar) {

    mySerVar("This gets logged.");

  }]);

Further Study

  • Signals
  • Dependency injection & mocks
  • E2E & unit testing
  • Internationalization support


docs.angularjs.org

github.com/angular/angular.js/wiki

Questions?

Thank you!

These slides can be found at ryepdx.com/slides

For questions after the meetup, feel free to tweet @ryepdx