Setting Up Yeoman and Angular Using Coffeescript

Hey! it’s been a while I know, but I’ve been into the deep gaming world and I basically burnt all my spare time playing Dota2 (which btw is pretty awesome!).

Well, actually I also had the chance to study Angular.js especially after I saw that the Yeoman’s generator-angular has introduced the --coffee option to bootstrap a whole angular application written with the beloved CoffeeScript.

Unfortunately, the s**t doesn’t work. I mean… there are still a few things to setup.

Initial setup

Be sure to have installed Yeoman and his generator-angular so just basically copy this line to the shell

1
npm install -g yo generator-angular

put a sudo in front of it if you have the global NPM packages under /usr/lib/node_modules/ (be aware that using sudo npm is not the right way to install global packages, but whatever).

Let’s rock the generator on a directory using

1
mkdir testapp && cd testapp && yo angular Test --coffee

Yeoman will ask you a series of question, you can just take the default answers by pressing Enter.

Using the latest Angular.js library

So here’s the first problem. The version that is included with the generator is actually outdated (1.0.8 right now, while Angular is at 1.2.0rc with the latest stable 1.1.5).

OMFG you are a retard, just use the f**king 1.0.8

Nice observation. Except that it’s stupid and you sureley dont want to start a new project with an outdated library. I also need the latest version to make it easily work using my API backend (Tastypie). I need in particular the transformResponse option introduced in the 1.1.x branch as seen here.

To fix this issue, you have to change the bower.json file that you find in the root of the project that you’ve just created.

1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "name": "Test",
  "version": "0.0.1",
  "dependencies": {
    "angular-latest": "latest",
    "json3": "~3.2.4",
    "jquery": "~1.9.1",
    "bootstrap-sass": "~2.3.1",
    "es5-shim": "~2.0.8"
  },
  "devDependencies": {
  }
}

Then launch this in the shell to remove the outdated libraries and install the new one:

1
2
3
4
5
6
7
bower uninstall angular angular-cookies angular-mocks angular-resource angular-sanitize angular-scenario
bower install
# These lines are used to compile angular
cd app/bower_components/angular-latest/
npm install
grunt package
cd ../../

We also need to fix the paths in a couple of files, open up app/index.html and update the new path for the angular libraries

1
2
3
4
5
6
7
8
9
10
11
12
<script src="bower_components/jquery/jquery.js"></script>
<script src="bower_components/angular-latest/build/angular.js"></script>

<!-- build:js scripts/plugins.js -->
...
<!-- endbuild -->

<!-- build:js scripts/modules.js -->
<script src="bower_components/angular-latest/build/angular-resource.js"></script>
<script src="bower_components/angular-latest/build/angular-cookies.js"></script>
<script src="bower_components/angular-latest/build/angular-sanitize.js"></script>
<!-- endbuild -->

Open also the karma.conf.js and update the paths aswell

1
2
3
4
5
6
7
8
9
// list of files / patterns to load in the browser
files: [
  'app/bower_components/angular-latest/build/angular.js',
  'app/bower_components/angular-latest/build/angular-mocks.js',
  'app/scripts/*.coffee',
  'app/scripts/**/*.coffee',
  'test/mock/**/*.coffee',
  'test/spec/**/*.coffee'
],

Setting up the tests

Somehow the e2e tests are not contemplated with this generator. We need to set them up opening the Gruntfile.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
karma: {
  unit: {
    configFile: 'karma.conf.js',
    singleRun: true
  }
},
// Substitute this piece of code with this
karma: {
  e2e: {
    configFile: 'karma-e2e.conf.js',
  },
  unit: {
    configFile: 'karma.conf.js',
  }
},

And at the bottom of the file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  // REMOVE THIS TASK
  grunt.registerTask('test', [
    'clean:server',
    'concurrent:test',
    'autoprefixer',
    'connect:test',
    'karma'
  ]);

  // Change that piece of code with these two, to enable the test:unit and test:e2e tasks
  grunt.registerTask('test:unit', [
    'clean:server',
    'concurrent:test',
    'autoprefixer',
    'connect:test',
    'karma:unit'
  ]);

  grunt.registerTask('test:e2e', [
    'clean:server',
    'concurrent:test',
    'autoprefixer',
    'connect:livereload',
    'karma:e2e'
  ]);

This operation will enable two different tasks that you can launch with grunt test:unit and grunt test:e2e with their respective behaviour.

Basic example of a RESTful Angular service calling Tastypie API

So, if you have followed the steps you should be able to run the application using grunt server and see the typical Yeoman greeting:

The Yeoman base view – click to zoom

Let’s get and display a list via AJAX then. First, we have to create a new service using yo angular:factory MyObject. This will create two files, one is the real service, the other is the corresponding test file.

Open the new file under app/scripts/services/MyObject.coffee and substitute it with this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
'use strict';

angular.module('TestApp.services', ['ngResource']).factory 'MyObject',['$resource', '$http', ($resource, $http) ->
    $resource 'http://localhost:8000\:8000/your/api/endpoint/', {}, {
        query:
            method: 'GET'
            isArray: true
            transformResponse: $http.defaults.transformResponse.concat [(data, headersGetter) ->
                if data? and data isnt ''
                    result = data.objects
                    result.meta = data.meta
                    result
            ]
    }
]

This will: * Inject the ngResource object inside the factory, allowing us to define a new $resource * Inject the $http object inside the factory to allow us to extend the transformResponse method (mandatory if you are using Tastypie as your API backend) * Define your new resource as MyObject * Define the module TestApp.services where you will can all the services of this app

This is what the main controller has become:

1
2
3
4
5
6
'use strict'

angular.module('TestApp.controllers', []).controller 'MainCtrl', ['$scope', 'MyObject', ($scope, MyObject) ->
    MyObject.query().$then (objects) ->
        $scope.objects = objects.data
]

This code will inject our factory object inside the main controller. The controller will then query the object and then inject in the $scope the resulting items.

Finally, we can add some super simple code in our main controller view (HTML) to dump the list:

1
2
3
4
5
6
<div class="hero-unit">
  <h1>Here's my list of objects</h1>
  <ul>
      <li ng-repeat="object in objects">{{object.name}}</li>
  </ul>
</div>

Running the application with a proper Tastypie API, will return you the list of objects name:

The Yeoman base view – click to zoom

THE WHOLE CODE CAN BE FOUND HERE: https://github.com/vshjxyz/angulartestapp

This is pretty straightforward and I know that the tests are lacking but I’M WORKING ON IT OK??!

:) well, I’ll try to update / post more stuff about AngularJS as soon as I discover new ‘things’. Peace out.

Comments