intro to ANGULARjs



a javascript mvvm framework



(╯°□°)╯︵ ┻━┻

disclaimer


This is a entry-level demo.

It was meant to be presented before people at Dynamo started 
to play around with angularjs, so might be a bit boring
for some of you.

Once again, this is a free-flowing presentation, 
so feel free to interrupt me at any point.
  1. Check out the example app!
  2. Intro
    - What is a SPA (disclaimer: there are no hot baths)
    - Why is Angular dubbed as a MVVM
    - What Angular tries to accomplish
  3. Structure and basics
    - Config and setup
    Routes
    - Services and factories 
    - Controllers
    Mark-up
    - Directives
    - Filters
  4. Specifics
    - Two scoops of $scope
    - Oh promises
    - Validate all the forms!
    - Animate like a baws

1. Foosapp ftw!

If you want to checkout the example app, you can do so here.


It is scaffolded with Yeoman's angularjs generator, 
which you can find here.


Now, let's check this baby, shall we?

2. intro




2.1 WHAT IS A SPA?

Very quickly, a traditional site works like this:

  1. Request the server for the site and wait for all 
    the content 
    to be rendered by the server
  2. Receive the content
  3. If you dare navigate to a new page, you hard refresh the page
  4. Restart from #1

Result: on every pageview, we are requesting 
the content of the site to the server.

Despite caching and other techniques, 
this is slow and relatively inefficient.

Examples? 

2.2 we started building spa


Basically, a single page application is a site that is 
requested and rendered once to the server.


Then everything you see is: 

  1. Happening in the browser
  2. With content often coming asynchronously
    from the server
  3. And content often being pure data


Result: even though the first load can be longer
once you are on the site, it is blazing fast and requires no hard refresh.


2.3 Advantages of a spa


  • Fast, real fast
  • Better interaction for the user
  • Easier to keep/change the state some UI elements
  • Easier to add page transitions, etc.
  • More control in the front-end
  • Easier on the server (or is it... ?)

pitfalls OF A SPA


  • SEO is hard and clunky
  • Uses more CPU
  • Security is a bit harder to set-up

2.4 why is angular a mvvm?


Angular doesn't have a classic MVC structure.

Rather, it uses services/factories to 
  1. fetch data from the server
  2. decorate the data
  3. share it to the controllers

The controllers expose the data 
and methods to the views.

Controllers and views are very very intertwined 
and dependant on each other in a two-way fashion.

More on that later.

2.5 what angular tries to fulfill


Angular was first created to solve 
those four fundamentals paradigms of front-end apps: 

  • Module loading and dependency injection (pre-specs)
  • Easier form validation
  • Creation custom elements / web components (pre-specs)
  • Testing (not covered, sorry Braden)

Over time, it also aimed at simplifying these recurring issues:

  • Scope
  • Routing
  • Two-way data-binding
  • Animations 
  • HTML includes

3. structure and basics


Let's jump straight in!

(I already foresee I spent too much time on the intro...)

3.1 visual overview of the app


3.2 Config and setup


This is how the App is instantiated: 
 window.App = angular.module('App', ['ngAnimate', 'ngResource','ngRoute'])

You can see the module loading and dependency injection 
in action right there!

The App also needs to be tied up to its markup "scope".
 <body ng-app="App">

This tells Angular that the whole "body" of the page 
is gonna be its playground!

This also means that you could restrain Angular's scope to only a certain part of your site (cart and checkout anyone ?).

3.2 (...)


Note: for a bigger app or if you create a "plugin", 
it is recommended to split your app into multiple modules.

There is no limit to the number of modules you can create,
so split it.

3.3 Routes


The router is basically like a big controller 
that is gonna react to a change in the URL. It looks like this:

  $routeProvider
    .when '/',
      name: "home"
      templateUrl: 'views/list.html' # yielded in the ng-view
      controller: 'PlayerListCtrl' # attached to ng-view

    .when '/edit/:id',
      name: "edit"
      templateUrl: 'views/edit.html' # yielded in the ng-view
      controller: 'PlayerEditCtrl'

    .otherwise
      redirectTo: '/'

Both the templateUrl and the controller 
are gonna be "attached" or yielded in the ng-view.

Note: ng-routes is now an optional module (and needs to be injected)

3.4 services and factories


For simplicity, let's treat them as the same thing
I can explain the difference but it is super technical.

Services are vital to the App and used to :

  1. Fetch data from the server
  2. Decorate the data
  3. Share that data between controllers

To use a service inside a controller, 
simply inject it as a dependency like so: 

App.controller 'PlayerListCtrl', ($scope, Players, $timeout) ->

  # Scope assignement
  $scope.players = Players.list

3.4 (...)


If you need to use a service inside a view, 
you will need to assign it to a $scope variable.
App.controller 'PlayerEditCtrl', ($scope, Players, $routeParams, $location, $route) ->

  (...)

  # New
  if $scope.route is 'new'

  # Edit
  else

    # Example of how to catch a promise
    Players.find($routeParams.id)

      # Success
      .then (player) ->

        # Do something

      # Error
      , (error) ->

        # Do something
(More on promises later)

3.4 (...)



Hey Max, listen, this is important!


What is awesome about services is 
that every controller that feeds on the service will see 
it's data refreshed when that data changes.

---

This is very satisfying.

Example ? New player creation!

3.5 Controllers


These guys are the hard workers of your App, as they:

  1. Consume the services' data
  2. Make the link between data and the views
  3. Take care of the interaction logic
  4. And more!

Not impressed ?

Controllers also:

  1. Can inherit everything from their parent controller
  2. Create their own scope (or $scope)
  3. Be used as many times as you want in any given page

3.5 (...)


How to use the controller object?

First, reference it in the markup where you wanna use it by either:

1. Adding it to the routes
 .when '/',
   name: "home"
   templateUrl: 'views/list.html'
   controller: 'PlayerListCtrl'
or

2. Attaching it to the markup
 <li class="m-list-item" ng-controller="PlayerListItemCtrl" ng-repeat="player in players">

So in this instance, PlayerListCtrl has a $scope on all the view, 
whereas PlayerListItemCtrl is limited to its <li> element.

3.5 (...)


Now, let's see how the two controllers interact with each other:
 <!-- Loop in the players array -->
 <li class="m-list-item" ng-class="{'is-active': player == active}" ng-controller="PlayerListItemCtrl" ng-repeat="player in players | filter: playerQuery">

    <!-- Bind the click with a $scope function in the controller -->
    <a ng-click="setActive(player)">

      <!-- Index -->
      <span class="m-list-index">{{itemIndex}}</span>

      <!-- Templating -->
      <img class="i-sqr" ng-src="{{player.pic}}">
      <span class="m-list-name">{{player.firstname}} {{player.lastname}}</span>
    </a>

 </li>

The "parent" controller exposes two things to the $scope:

  1. $scope.players (the collection of players from the service)
  2. $scope. setActive(param) (a method to change the selected player)

3.5 (...)


What is this "ng-repeat" wizardry ?

Basically, it is a directive (be patient) 
built by Angular to loop over a collection.

So, it says: 
  • for each players in the list
  • attach a PlayerListItemCtrl to it

All those "children" controller now 

  • create their own $scope
  • have access to the setActive() method
  • use the data in the player object
    to render the template with the {{ }} (more on that later)

3.6 templates


To render data into a template, 
simply put your valid content inside 
a quadruple mustachio like so {{ }}.

In our example, the player object data looked like that:
 {
   id: 8382
   firstname: "Andre"
   lastname: "Valle"
   nickname: "Rattle Snake"
   pic: "http://api.adorable.io/avatars/500/Andre@godynamo.com"
   spinner: true
   stats:
     wins: 32
     defeats: 21
     goals: 123
     trouts: 10
 }

And thus we used it like that:
 <span class="m-list-name">{{player.firstname}} {{player.lastname}}</span>

3.6 Directives


The directives can be seen as:

  • independent modules (almost like a plugin) 
  • that need to be attached to a DOM element
  • that (should) live on their own
  • that may feed off a controller


Examples of built-in directives are:

  • ng-show/ng-hide (show or hide a DOM element if truthy/falsy)
  • ng-repeat (iterate over a collection)
  • ng-if (render something or not)
  • ng-include (insert HTML content in a DOM element)
  • ng-class (add/remove a class if a condition is met)
  • Etc.

3.6 (...)


Directives would be boring if you couldn't build your own!

In our app, we have one custom directive. And it's cool:

  • It has options
  • It has dynamic template loading
  • It feeds off the controller data

Here how it is called in the HTML (two formats):
<anim-percentage 
  one="active.stats.wins" 
  two="active.stats.defeats" 
  template="views/templates/number.html">
</anim-percentage>

<anim-percentage 
  one="active.stats.goals" 
  two="active.stats.trouts" 
  template="views/templates/bar.html">
</anim-percentage>

3.6 (...)

 App.directive 'animPercentage', ->

  # Will apply only to elements (not data-attributes, etc.)
  restrict: 'E'

  # Inner scope of the directive ties to the model in the view
  scope:
    one: '=one'
    two: '=two'

  # Link function
  link: (scope, element, attr) ->

    scope.getFinal = () ->
      Math.floor 100 * scope.one / (scope.one + scope.two)

    scope.incNumber = () ->
      if scope.number < scope.final
        scope.number++
        scope.$apply()
      else
        clearInterval scope.interNumber

    scope.animateNumber = () ->
      scope.number = 0
      scope.final = scope.getFinal()
      scope.interNumber = setInterval scope.incNumber, 5

    # $watch
    scope.$watch 'one', (newVal, oldVal) ->
      if newVal
        scope.animateNumber()

  templateUrl: (element, attr) ->
    attr.template
 <div>{{number}}</div>
 <div class="m-bar-wrp">
   <div class="m-bar-el" style="width: {{number}}%;">&nbsp;</div>
 </div>

3.7 Filters


Very quickly, they are helper methods that 
will help with the presentation of data.

For example:

  • changing this "5" into this "$5.00"
  • filter out a collection according
    to a certain predicate, like so:

<li class="m-list-item m-list-item--search">
  <input type="text" ng-model="playerQuery" placeholder="Filter" />
</li>
<li class="m-list-item" ng-class="{'is-active': player == active}" ng-controller="PlayerListItemCtrl" ng-repeat="player in players | filter: playerQuery">

Of course, you can also build your own... on your own.

4. specifics


Now let's see some of the specific-ish concepts of Angular!


4.1 $Scope


A tricky aspect of JS development has always been the scope.

In my words, the scope means the virtual area 
in which specific values, functions and methods are taking effect.

Angular tries to simplify this concept 
by assigning a unique scope to every controller and directive.

You beg my pardon ?

Easiest example of this is that a variable named "foo" 
inside the scope of a controller will not be:

  • accessible outside the scope of this controller (unless parent/child ...)
  • will not be the same as another variable "foo" in another controller

4.1 (...)


To achieve this, Angular gives you a $scope object that you need to
 
  1. inject in your controller
  2. use for all your assignations
 App.controller 'PlayerListCtrl', ($scope, Players, $timeout) ->
  $scope.players = Players.list
  $scope.active = $scope.players[0]
<li 
  class="m-list-item" 
  ng-class="{'is-active': player == active}" 
  ng-controller="PlayerListItemCtrl" 
  ng-repeat="player in players | filter: playerQuery">
</li>

Tip: 
if you have private method/function/value that is not gonna 
be used in the templates or by a child controller, you don't need to 
bloat the $scope object with it.

4.1 (...)


Note: 
you don't need to specify the $scope bit 
when you reference a value in the template since
it "knows" which $scope it encompasses.

Caveat:
$scope inheritance trickles down (parent to child) 
but doesn't bubble up. 

Tip:
If you need to share a lot of values 
between child and parent, you might consider
refactoring it into a service.

4.2 promises


This is where it gets tricky :)

In async app flow, the data fetched 
from the server may take time to get received.

This would lead to weird situation, 
since controllers (mainly) depend on this data to do their biz.

And so the promise paradigm was born! And it works like this:

  1. the service makes the API request
  2. the service then returns a promise
  3. the controller receives the promise
  4. once the promise is resolved (response from the server is received),
    the controller deals with a success or an error

4.2 (...)


The service looks like (fake API call):
 App.service 'Players', ($q, $http) ->

  service =

    # Fetch for the whole list of players
    init: () ->
      q = $q.defer()
      service = this
      $http.get "/" # Faking a GET for demo purposes
        .success (data) ->
          service.list = fixtures
          q.resolve service
        .error (response) ->
          q.reject response.errors
      return q.promise

    # Find a specific player
    find: (id) ->
      q = $q.defer()
      service = this
      found = null
      id = Number(id)
      for player in service.list
        found = player if player.id is id
      if found isnt null
        q.resolve found
      else
        q.reject "Player Not Found"
      return q.promise

$q is the promise object.
q.resolve is used on success, while
q.reject when there is an error.

4.2 (...)


promise is always caught like so:
 Object

  .method() # The promise is received

  .then (data)-> # The 'then' chained method has two methods as callbacks, success and error
    
    # If it succeeds, it will have the 'data' as a param
    (...)

  , (error) ->
    
    # If it fails, it will receive the 'error' object as a param
    (...)

4.2 (...)


In the controller :
 App.controller 'PlayerEditCtrl', ($scope, Players, $routeParams, $location, $route) ->

  (...)

    # Example of how to catch a promise
    Players.find($routeParams.id)

      # Success
      .then (player) ->

        # Scope assignement
        $scope.current = player
        $scope.editing = angular.copy player

      # Error
      , (error) ->

        # Redirect to home
        $location.path "/"

4.3 Form validation


Angular has built-in form validation 
directives and controllers.

You are not forced to use them
but they can be pretty cool depending on the situation!

---

4.3.1 Basic features

With any given form, 
Angular will toggle various classes 
on the form and also on its elements.


4.3 (...)


Here is the markup:
<form name="PlayerForm" ng-submit="process()" novalidate>
 <input 
  class="m-frm-input" 
  name="firstname" 
  type="text" 
  placeholder="Firstname" 
  ng-model="editing.firstname" 
  required 
  ng-minLength="5" 
  ng-class="{'is-invalid': PlayerForm.firstname.$invalid && PlayerForm.firstname.$dirty}"
 >

For a new player, at runtime
Angular will add those classes to the form and the input:
 ng-pristine ng-invalid ng-invalid-required ng-valid-minlength
ng-pristine ng-untouched ng-invalid ng-invalid-required ng-valid-minlength

You can then style your elements accordingly!

4.3 (...)


4.3.2 Advanced features

You can also access 
the form object from your controller.

All you need to do is reference 
the form name from the $scope object.

Example: $scope.PlayerForm


4.3 (...)


With this object, 
you can now access a bunch of 
methods straight from the controller.

Let's log the object in the console 
and see what's in store!

You can see you now have access to 
the whole form and to each of its elements!

---

Example: 

The PlayerForm.$setDirty method
let's you dirtify/undirtify the form.

4.4 animations


If you have a bit of experience in CSS animations,
you know it is a pain to :

  • animate stuff like display: block; to display: none;
  • know when the animation has started or stopped
  • Etc.

---

Angular tries to solve this for a few of its interactions:

ngRepeatngViewngInclude, 
ngSwitch, ngIf, ngClass, ngShow/ngHide, etc.

4.4 (...)


For example, when using ngShow,
upon showing/hiding of the element,
Angular adds these classes in sequence:

  • .ng-hide-add
  • .ng-hide-add.ng-hide-add-active
  • .ng-hide-remove
  • .ng-hide-remove.ng-hide-remove-active


It all happens really fast, 
but this allows you to hook into 
those classes to trigger animations.

4.4 (...)


Example of usage:

<div 
  class="m-profile"
  ng-show="active"
  ng-controller="PlayerShowCtrl"
>
[ng-show],
[ng-hide]
  transition: transform 0.15s ease-in-out 0s, opacity 0.15s ease-in-out 0s
  &.ng-hide-add
    opacity: 1
    transform: scale(1, 1)
  &.ng-hide-add.ng-hide-add-active
    opacity: 0
    transform: scale(0.5, 2)
  &.ng-hide-remove
    opacity: 0
    transform: scale(0.5, 2)
  &.ng-hide-remove.ng-hide-remove-active
    opacity: 1
    transform: scale(1, 1)

4.4 (...)


Note: ng-animate is now an optional module (and needs to be injected)

---

Sanity warning:

ng-animate is wise enough to check 
in your CSS to see if you have are using its classes.

If you don't reference those classes in the CSS, 
ng-animate won't do its magic.

Also, thanks Angular for not saying 
this anywhere in your documentation.

4.5 jquery


Hey, what about jQuery, pal?

Angular is bundled 
with a jQuery subset called jQlite.

Instead of referencing the $ namespace,
you have to use angular.element, like so:

 items = angular.element(".m-items")
 items.addClass("my-class")

If jQuery is available, angular.element 
is an alias for the jQuery function. 

If jQuery is not available, angular.element delegates to 
Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."

4.5 (...)


Contrary to beliefs, you can absolutely use jQuery with Angular 
(even though in 2015, you might not have any reason to...)


BUT

You mustn't use jQlite or jQuery to alter the DOM
as it could get tangled with Angular's own actions.

Takeaway: 
Let Angular take care of the DOM manipulation.

have any questions, 

fears or needs?

that's it!




Thanks guys :)



Github
https://github.com/simonwalsh

Twitter
https://twitter.com/sim_walsh

Email
simon@godynamo.com

Intro to Angularjs

By Simon Walsh

Intro to Angularjs

A quick introduction to the pros of using Angularjs in your project

  • 1,024