CoffeeScript and AngularJS

Written by Alexander Hill

12. July 2013

AngularJS and CoffeeScript are a great combination, despite the lack of popularity of CoffeeScript in the AngularJS community. Here are a few ways CoffeeScript makes writing AngularJS code a breeze.

Short Function Definitions

Angular uses a lot of anonymous functions - as such, short function definitions are the biggest timesaver when writing Angular code in CoffeeScript. As an example, lets write an ‘enter key’ directive that evaluates an expression when the enter key is pressed, and after a delay. Not particularly useful, but this should give you a sense of how CoffeeScript and AngularJS work together.

Here’s how you’d use it:

<input enter-key="submit()" enter-key-delay="10" />

And here’s the JavaScript:

app.directive('enterKey', function ($timeout) {
  return function (scope, elem, attrs) {
    elem.bind('keydown', function (e) {
      if (e.keyCode === 13) {
        $timeout(function () {
          scope.$apply(attrs.enterKey);
        }, +attrs.enterKeyDelay);
      }
    });
  }
});

Compared to the CoffeeScript:

app.directive 'enterKey', ($timeout) ->
  (scope, elem, attrs) ->
    elem.bind 'keydown', (e) ->
      if e.keyCode is 13
        $timeout ->
          scope.$apply attrs.enterKey
        , +attrs.enterKeyDelay

In CoffeeScript, the arrow -> is used to define a function, with the parameters in brackets in front. If a function has no parameters, the brackets are optional, as seen in the function passed to $timeout. You can also see how brackets are optional for calling functions, so long as the function has one or more parameters.

In this short example, we’ve removed the need for the function keyword four times in ten lines of code, and made the code a lot more readable and faster to write. There’s also no need for return statements, as CoffeeScript (like Ruby) automatically returns the value of the last expression in a function.

Automatic Returns

Automatic (or implicit) returns are another feature that makes writing Angular code easier and faster. Here are a few examples where automatic returns come in handy. The differences are not major, but it adds up in a large codebase.

Filters

app.filter('capitalise', function (str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
});
app.filter 'capitalise', (str) -> str.charAt(0).toUpperCase() + str[1..]

You can also see the handy array slicing syntax with str[1..]

Factories

app.factory('urlify', function (text) {
  // nb: don't use this to make things url safe in real applications
  return text.toLowerCase().replace(" ", "");
});
app.factory 'urlify', (text) -> text.toLowerCase().replace " ", ""

Directives (again)

In this case, we’re using a directive definition object. CoffeeScript’s automatic returns, YAML style object syntax, and short function definitions makes this a lot more readable. This example is taken from the directive guide in the AngularJS docs.

app.directive('directiveName', function factory(injectables) {
  return {
    priority: 0,
    template: '<div></div>',
    templateUrl: 'directive.html',
    replace: false,
    transclude: false,
    restrict: 'A',
    scope: false,
    controller: function ($scope, $element, $attrs, $transclude, otherInjectables) { ... },
    compile: function compile(tElement, tAttrs, transclude) {
      return {
        pre: function preLink(scope, iElement, iAttrs, controller) { ... },
        post: function postLink(scope, iElement, iAttrs, controller) { ... }
      }
    },
    link: function postLink(scope, iElement, iAttrs) { ... }
  }
});
# coffeescript uses YAML style syntax for object literals.
app.directive 'directiveName', (injectables) ->
  priority: 0
  template: '<div></div>'
  templateUrl: 'directive.html'
  replace: false
  transclude: false
  restrict: 'A'
  scope: false
  controller: ($scope, $element, $attrs, $transclude, otherInjectables) -> ...
  compile: (tElement, tAttrs, transclude) ->
    pre: (scope, iElement, iAttrs, controller) -> ...
    post: (scope, iElement, iAttrs, controller) -> ...
  link: (scope, iElement, iAttrs) -> ...

You can see we use the automatic returns and object syntax for both the directive definition object and within the compile function.

Classes

CoffeeScript’s classes are one of the nicest inclusions the language offers. Here’s what they look like:

class Animal
  constructor: (@name) ->

  move: (meters) ->
    alert @name + " moved #{meters}m."

snake = new Animal('snake')

snake.move(10) # alerts "snake moved 10m."

In CoffeeScript, the @ symbol is shorthand for this. So @name becomes this.name. Additionally, prefixing an @ symbol to a function parameter automatically attaches it to this, as seen in the Animal constructor.

Here’s the equivalent handwritten JavaScript. This isn’t identical to the CoffeeScript compiler output, but is functionally equivalent.

function Animal(name) {
  this.name = name;
}

Animal.prototype.move = function (meters) {
  alert(this.name + "moved" + meters + "m.")
}

var snake = new Animal('snake')

snake.move(10) // alerts "snake moved 10m.", as before

As you can see, CoffeeScript creates a named function and attaches methods to its prototype. In Angular, this is useful in two places - Services, and with the new Controller as syntax.

Services

Whilst a factory function will be injected directly into another function, a service will first be instantiated before injection. See this article for a more detailed explanation of the difference.

In the article mentioned above, the following is used as an example of a service:

var gandalf = angular.module('gandalf', []);

function Gandalf() {
  this.color = 'grey';
}
Gandalf.prototype.comeBack = function () {
  this.color = 'white';
}

gandalf.service('gandalfService', Gandalf);

var injector = angular.injector(['gandalf', 'ng']);

injector.invoke(function (gandalfService) {
  console.log(gandalfService.color);
  gandalfService.comeBack()
  console.log(gandalfService.color);
});

This looks just like the JavaScript that comes from a CoffeeScript class - so instead of having to directly modify the function’s prototype, we can use CoffeeScript’s classes:

gandalf = angular.module 'gandalf', []

gandalf.service 'gandalfService',
  class Gandalf
    constructor: -> @color = 'grey'
    comeBack: -> @color = 'white'

injector = angular.injector ['gandalf', 'ng']

injector.invoke (gandalfService) ->
  console.log gandalfService.color
  gandalfService.comeBack()
  console.log gandalfService.color

Here, we pass the class to the service function. You could also define it above the service and then pass it in, but I prefer the class to be exclusively accesible via the service.

To show how dependencies are injected, lets make a gandalf that ‘comes back’ after a delay using the $timeout service.

gandalf.service 'gandalfService',
  class Gandalf
    constructor: (@$timeout) -> @color = 'grey'
    comeBack: (time) ->
      # the 'fat arrow' in coffeescript binds the function to the existing
      # value of 'this', so @color is the same variable as above.
      @$timeout =>
        @color = 'white'
      , time

The name of the variable in the arguments will be $timeout, so Angular’s dependency injection will still work. Attaching dependencies to the object allows us to access them in methods - such as comeBack.

CoffeeScript classes make dealing with prototypes a lot easier, and that’s without factoring in inheritance.

Controllers

In AngularJS 1.1.5, new syntax for controllers has been introduced. You can watch the egghead.io video about it here, but essentially it allows a class to be passed in as the controller instead of a function with a scope. So, the todolist example on the AngularJS homepage would have the following HTML:

<body ng-app="todoApp">
  <div ng-controller="TodoCtrl as todos">
    <span>{{todos.remaining()}} of {{todos.list.length}} remaining</span>
    [<a href="" ng-click="todos.archive()">archive</a>]
    <ul>
      <li ng-repeat="todo in todos.list">
        <input type="checkbox" ng-model="todo.done">
        <span class="done-{{todo.done}}">{{todo.text}}</span>
      </li>
    </ul>
    <form ng-submit="todos.addTodo()">
      <input type="text" ng-model="todos.input" placeholder="add a new todo">
      <input type="submit" value="add">
    </form>
  </div>
</body>

And would use this CoffeeScript class as a controller:

app = angular.module 'todoApp', []

app.controller 'TodoCtrl',
  class TodoCtrl
    list: [
      text: "learn coffescript"
      done: false
    ,
      text: "learn angular"
      done: true
    ]

    addTodo: ->
      @list.push
        text: @input
        done: false
      @input = ''

    remaining: ->
      count = 0
      for todo in @list
        count += if todo.done then 0 else 1
      count

    archive: ->
      oldList = @list
      @list = []
      for todo in oldList
        unless todo.done
          @list.push todo

Conclusion

As you can see, CoffeeScript is a great companion to AngularJS. Whilst not for everyone, the ease of writing CoffeeScript far outweighs the downsides.

There are a number of other useful CoffeeScript features that I haven’t mentioned in this article, such as default arguments, the existential operator and array and object comprehension. All together, these make CoffeeScript an enjoyable and productive language to write. If you haven’t considered it before, take a look at the official site for more details.

You are welcome to tweet me abuse or praise as you see fit.