nghellostyle

AngularJS seed with Google Closure Compiler
Author: 1132354?v=2 zemirco
Submitted by: 4888819?v=3 BijayRai

A radical new approach to developing AngularJS apps

Build Status

An AngularJS seed for Google Closure Compiler.

Concept

This is not a 'one size fits all' solution. There is no CoffeeScript/etc. compiler or CSS preprocessor involved. That's up to you. It's already complicated enough without them :)

What you get is a clean and scalable project structure for your AngularJS apps.

Google Closure Compiler

Most of you might use uglifyjs for minification. At least that's what I did for a long time and there is nothing wrong with it. It is an awesome tool.

However Closure Compiler can do more than that.

  1. Minify code knowing about AngularJS
  2. Inject dependencies automatically
  3. Create namespaces
--angular-pass

Generate $inject properties for AngularJS for functions annotated with @ngInject

Example controller.

The above example after minification.

No more

--externs

The file containing JavaScript externs. You may specify multiple

Example

The above example after minification without --externs.

That does not work as angular does not have a d() method. module(), directive(), service() and so on have to stay as they are. That's why we need an AngularJS externs file with the AngularJS global namespace.

--generate_exports

Generates export code for those marked with @export

Closure Compiler throws away all unnecessary code. So if you declare a function but never call it the compiler doesn't include it in app.min.js. Problem here is that we declare our controllers and controller properties in our xyz-controller.js files and use them in your template files xyz.html. Closure Compiler doesn't look into our HTML template and doesn't know which methods we'd like to keep.

@export and @expose to the rescue. Simply use these annotations in your code to prevent variable names from being mangled.

So what's the difference between @export and @expose?

  • use @export for constructor functions
  • use @expose for properties inside constructor functions

Important!

The style guide from Google says that

if you are using @export for properties, you will need to add the flags:

Both of them didn't work for me because the Closure Compiler threw Errors.

Namespaces

Closure Compiler modularization works very similar to Node.js modules. You can create namespaces by using goog.provide() and goog.require(). It is a bit like module.exports and require() from Node.js.

xyz-controller.js

xyz-module.js

No more, what's the difference between angular.module('app', []) and angular.module('app') ?!?

Be careful when providing and requiring submodules. Inside our main app.js we can require submodules and inject them into our main module. However you must use the name and not the whole module.

'controller as' syntax

Since Angular version 1.1.5 you can use an alternative syntax for your controllers. The old way was simply using the controller name. Either in your HTML

or inside the router.

By using the alternate so called 'controller as' syntax you have fine grained control over your app.

You can now reference the controllers in your templates.

As opposed to the standard syntax.

The 'controller as' syntax provides a much better overview in templates. It is easy to see where things come from.

Everything is a JavaScript class

Yes, with prototype and new.

  • Controller

  • Service

    Use module.service instead of module.factory or module.provider.

  • Directive

  • Filter

Angular UI Router

nuff said

Everything is grouped in 'states' and 'components'

Directives, Filters and Services belong into the components/ folder. Each file should have a -directive, -filter or -service suffix. As you can see we have a version Directive and a version Service. With the suffixes they are easy to differentiate.

All states belong into the states/ folder. Give each folder the appropriate state name, i.e. home/ for home state. Put child states inside their parent directories, i.e. one/ inside third for third.one state.

As all components and states have their unit tests right next to them the test/unit/ folder only contains our karma.conf.js and the Istanbul code coverage reports. The same goes for the unit/e2e/ folder. It only contains the test scenarios for our main app.js file.

Important!

compiler.jar (6,8 MB) is not in this repo. You have to download it manually and place it inside the closure/ directory.

'states' are independent modules

Yes, with square brackets []. They live inside the xyz-module.js files. They have their own config() function and they all define their own routes.

Each state has its own controller. They live in the xyz-controller.js files.

Each state creates an own namespace. Inside the main app.js we use goog.require() to pull in all submodules and inject them into our main app.

Child states don't bubble up to our main app.js. Load them in their parent state, i.e. third.one and third.two are required in third state which is itself required by our main app.js.

This structure makes it much easier to understand data coming from resolve.

Karma unit tests with Istanbul code coverage

Using code coverage with AngularJS is really easy. Though only few projects actually include Istanbul. This seed has it built-in.

minimatch is magic. The pattern includes all *.js files in our states/ directory. It ignores all *.pageobject.js, *.scenario.js and *.spec.js files. They would pass and show up green in the coverage report but they create noise. So leave them out.

To increase readability inside your Karma tests use the $injector service instead injecting services inline.

Good

Better

That one line beforeEach(inject(function(...) { can become pretty long and hard to read.

Protractor tests using Page Objects

From Organizing Real Tests: Page Objects

When writing real tests scripts for your page, it's best to use the Page Objects pattern to make your tests more readable

xyz.pageobject.js

xyz.scenario.js

Grunt shell instead multiple plugins

  • Why do you use grunt (or gulp or any other build tool)? Why no plain npm?

    npm run karma would create annoying npm-debug.log files when tests fail.

  • Why don't you use plugins for karma and protractor?

    The less plugins the better. Starting karma and protractor is a one liner.

Test

Karma

grunt karma

Protractor

  • start the local server

  • start protractor

Build

The default task

grunt

creates app.min.js in app/js/.

References

License

MIT

Plea

@google why you not making your nghellostyle public?

comments powered by Disqus
This page was last updated almost 3 years ago.