Note - this project is deprecated, please use yeoman
A Seed project for large CoffeeScript AngularJS projects
- I needed a seed project so that I could get a real AngularJS application up and running quickly, with all the fixin's
- I wanted to share some conventions that have worked well for a large CoffeeScript AngularJS project
- Pure CoffeeScript
- Leverages bootstrap
- Grunt-based build script that:
- Starts a server and immediately deploys changes as you code
- Allows for a clean seperation between the organization of your source code and that of your served files
- A minified and concatenated distribution
- Incorporates most major aspects of AngularJS
- Modules, directives, views, services, controllers, filters, dependency injection, etc.
- Demonstrates 3rd party library integration using a 'global' library wrapped in a module, in this case toastr).
- Ability to customize the app for each environment (dev, qa, prod, etc.)
- Opinionated conventions for:
- naming Modules and Angular 'Items'
- sharing services, directives, filters, etc across views
Building and Running
- Ensure that you have node, npm, grunt, and coffeescript installed and in your path
- Clone this repository
npm installto pull down all build-time dependencies
Start server and watch for changes
The default grunt task will start the server and watch for changes. When changes are made the project is rebuilt, pushed to the server, and all unit tests are run through Testacular.
- To run the default grunt task, run
- Point the browser to
http://localhost:9876to connect the Testacular server
- Point another browser window to
http://localhost:8000to view the app
When you run the default Grunt task, you will see this output in your project's directory.
target/build/main is served by the webserver.
target/build main/ # all application files and code index.html # your old friend index.html js/ app.js # all of your application's code, concatenated lib.js # all 3rd party library code, concatenated style/ app.css # all of your application's css, concatenated lib.css # all 3rd party library css, concatenated ... # a directory per 'package', with all the static files (html, imgs, etc) test/ # all tests
All of these are demonstrated in the example code.
Directories & Files
src/ main/ # application root index.html # good ol' index.html app/ # contains all application files app.coffee # handles routing, module wiring, and bootstrapping ... # a directory per 'package'. Described later lib/ # all 3rd party code ... # a directory per 3rd party lib test/ # all test files and code config # testacular configs lib # test-time libraries ... # directory per package, generally with a Spec file per package profile/ # environment-specific overrides. See 'Profiles' prod # overrides for prod env ... dev # overrides for dev env, the default ...
Related files are grouped together. Best explained by example:
- All files for the app's 'details view' will go into the detailsView directory. None of the files in this directory will be used outside of this view
- 'Common' code that's shared between views are grouped under the 'common' directory
The structure you use for these groupings is up to you, but I've found that the 'xxxView' and 'common' structure works well.
Modules and 'Angular Items'
An 'Angular Item' is, for instance, a Vaule, Factory, Directive, Filter, Controller, etc. There's no good term for these things, unfortunately.
The key, then, becomes the naming convention we use for both our modules and the Angular Items.
- We use one module per Angular Item. We don't group Items together (I don't see the benefit).
- Module name matches the name and path of the file in which it's defined. So the module for the app's common Data service would be
- Item name depends on the type of the item.
- For values and factories, which are generally used to provide Controllers, Services, and wrappers around 3rd parties libs, the name of my Item is the exact same name as the module.
- For directives and filters, we do not namespace. Because they have to be referenced in the DOM, short names are best for directives and filters. See the code for examples.
Having one module per Item makes wiring easy - We just list all of our modules in
- Never use a global function for a controller, always a module
- Always use the 'array' format for dependency injection to avoid minification issues
src/profiles is a directory per 'profile' (currently dev and prod, but add as many as you need). During the build, these directories are overlayed directly on top of the main code, overriding anything that may be there. By default the build will use the 'dev' profile.
common.services.envProvider module is where most of the environment-specific aspects of your application will be defined. At the very least each environment must provide an instance of this module. The module is responsible for:
- Defining the
common.services.envProviderprovider. This provider can optionally have an
appConfigfunction, which is run in the config block of the application module. Since it is a provider it must have a $get function which defines how to instaniate the
- Defining the
common.services.envservice. This service will generally store any environment-specific data, and optionally an
appRunfunction, which is run in the run block of the application module.
If you take a look at the prod module, you'll see that the provider simply instantiates the service, and the service provides the production URL.
The dev module, however, uses $httpBackend to act as a fake server. Since $httpBackend is set up in a run block, we put the setup code in the
Having the ability to run custom code in the application module's config and run blocks as well as having an injectable service that abstracts away all environment specific aspects of your application gives you the option to set up your application's enviroments in whatever ways you need.
To run an enviroment specific build, add an
profile flag to your command line as follows:
grunt --profile prod
A distribution build minifies your js and css. Run
Some of the ideas here are my own but many are built upon the work of others: