Advanced UI, Plugins and Components in AngularJS

Matias Niemelä (@yearofmoo)
http://www.yearofmoo.com

What are we talking about today?

The role of the UI

Making Plugins

Popular UI Libraries, Tools and Plugins

Debugging & Testing

The role of the UI in Angular

The HTML Template

How the scope interacts with the template

Directives

Animations

What is good UI

Simple Template code

Composeable Directives

Using the right tool for the job

Small / Loose controllers

What is bad UI

Bad UI involves coupling

Terse HTML template code

Abusing the scope

Fat controllers

Be Expressive

The UI is all about directives

Directives should extend HTML

The Prime Directive

Separate Data from Function

Use directives as best possible

Example Validator

Let's create a validation example

Isolation is the answer

Split the code between data and function

Follow this method always with code

Tic-tac-toe

Let's create a tic-tac-toe example


<div ng-controller="TicTacToeCtrl">
  <div ng-repeat="row in rows">
    <div ng-repeat="col in row.cols">
      {{ col }} 
    </div>
  </div>
</div>

.controller('TicTacToeCtrl', function($scope) {
  $scope.rows = [
    { cols : [ 1, 2, 3 ] },
    { cols : [ 4, 5, 6 ] },
    { cols : [ 7, 8, 9 ] }
  ];
});

Tic-tac-toe (1)

Pro: Simple HTML Code

Con: Scope data is form-fitted

Con: Code is too terse

Con: Unable to extend


.directive('ticTacToe', function(createMatrix) {
  return {
    controller : function($scope, $attrs) {
      var rows = $scope.$eval($attrs.ticTacToe);
      $scope.rows = createMatrix(rows);
    },
    template : '<div ng-repeat="row in rows">' +
               '  <div ng-repeat="col in row.cols">' +
               '    {{ col }}' +
               '  </div>' +
               '</div>'
  }
});

  <div ng-controller="TicTacToeCtrl"
     tic-tac-toe="rows"></div>

.controller('TicTacToeCtrl', function($scope) {
  $scope.rows = [
    1, 2, 3,
    4, 5, 6,
    7, 8, 9
  ];
});

Tic-tac-toe (2)

Pro: Compact HTML Code

Con: Mix of JS and HTML code

Con: No real benefit


<div ng-controller="TicTacToeCtrl"
     tic-tac-toe="9">

  {{ cells[index] }}

</div>

.controller('TicTacToeCtrl', function($scope) {
  $scope.cells = [
    1, 2, 3,
    4, 5, 6,
    7, 8, 9
  ];
});

.directive('ticTacToe', function() {
  return {
    transclude : 'element',
    terminal : true,
    link : function($scope, $element, $attrs, ctrl, transcludeFn) {
      var cells = $scope.$eval($attrs.ticTacToe);
      var anchor = $element;
      for(var i=0;i<cells.length;i++) {
        var scope = $scope.$new();
        scope.index = i;
        transcludeFn(scope, function(element) {
          element.after(anchor);
          anchor = element;
        });
      }
    },
  };
});

Tic-tac-toe (3)

Pro: High-level language

Pro: Simple Scope Data

Con: Complexity of the directive

Moving On...

Separate Construction from Data

Isolate the construction into a directive

Keep yourself up to date

Angular keeps improving

Use the most modern features

Tutorials may be expired

Patterns emerge

Embrace the Template

Use the template to drive communication

Use controllerAs

Or use directives with controllers

ControllerAs


.controller('CommentCtrl', function() {
  this.content = comment.content;
  this.date = comment.date;
  this.destroy = function() {
    //remove the comment
  }
});

<div ng-controller="CommentCtrl as comment"></div>

Directive Controllers


.directive('commentCtrl', function() {
  return {
    controller : function($scope, $attrs) {
      this.content = comment.content;
      this.date = comment.date;
      this.destroy = function() {
        //remove the comment
      }
      $scope[$attrs.as] = this;
    }   
  }
});

<div comment-ctrl as="comment"></div>

Multi Level Form Example

Let's create a multi-level form example

Example 1...


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

  //One Big Fat Ctrl

})

<form ng-controller="FormCtrl">
  
  ...
</form>

Multi Level Form (1)

Pro: Simple HTML

Con: Fat Controllers

Con: Breaks the Law of Demeter

Example 2...


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

})

.controller('SubFormCtrl', function($scope, $attrs) {
  var color = $scope.$eval($attrs.color);
})

Example 2...


<form ng-controller="FormCtrl">
  ...
  <div ng-controller="ColorFormCtrl" color="color">
  </div>
</form>

Multi Level Form (2)

Pro: More Isolation

Con: Strict Inheritance

Con: Weird naming conventions

Example 3...


<form ng-controller="FormCtrl as form">
  ...
  <div ng-controller="ColorFormCtrl as colorForm" color="color">
  </div>
</form>

Multi Level Form (3)

Pro: Communication in the template

Con: Verbose Controller Attributes

Example 4...


<form form-ctrl as="form">
  ...
  <div color-form-ctrl="color" as="colorForm">
  </div>
</form>

Multi Level Form (4)

Pro: Inheritance in the template and directive

Con: Obscure JS code

Directive/Controller Hybrids

best level of flexibility

access to controller and DOM

isolation of the controller

requiring other controllers

Making a Plugin

Use a proper naming convention

Store everything inside of one module

Expose all template code as custom directives

CSS classes/code should have a naming convention as well

File Structure


my-plugin/
  plugin.html
  plugin.css
  plugin.js

Where to save it

Bower

ngmodules.org

Popular Tools

UI Libraries

Breeze & Restangular

RequireJS & Browserify

UI Libraries

AngularUI

Angular-Strap

KendoUI

Difficult to find something good

AngularUI (ui and bootstrap)

Updated Frequently

Native-Angular code

Might not be compatible with non-angular code

Uses its own animation code

Angular-Strap

Acts as a passthrough with Angular

Bootstrap is independent

Uses ngAnimate

KendoUI

Pricing model

Mobile Support

Wider selection of widgets

Angular integration mixed

Debugging Components

Use debugger or breakpoints

Hope into the console

Use angular.element() to gain access

Testing Components

Easy to test code when the structure is separate

Reduce the overall template footprint

Karma has some tricks you can use

Thank you

It was a pleasure

Matias Niemelä
@yearofmoo