The Wonderful Duo — Using Meteor and AngularJS Together


Over the last two months, I’ve been working on a personal project, Remonit, using Meteor, AngularJS and node-webkit. It’s a multi-site monitoring app featuring real time synchronization and responsive dashboards. The nature of this app — a lot of data synchronization and being frontend-heavy — makes it a perfect fit for using Meteor and AngularJS, two of the best web frameworks out there. After two months of development, I can say that using Meteor and AngularJS has been a godsend, and I want to share some of the experience I gained from using them.

Update (Oct 29, 2014): I’ll explain how and why to integrate Meteror and AngularJS in this article. At the end of the article, I also keep a short list of libraries that can help the integration. If you are here to find such libraries, scroll to bottom.


Why Meteor or AngularJS?

For those who don’t know, Meteor is a “full stack” Javascript framework that tries to simplify a lot of things in web development. AngularJS is a frontend framework that makes HTML smarter with two-way bindings and reusable components.

For my project Remonit, I need two things: fast data synchronization, and a good UI for dashboard and dashboard designer. Of course it’s possible to do both from scratch by hand, and yes, I’ve done both in several previous projects (who hasn’t?). But trust me on this: you don’t want to do data synchronization or complex DOM manipulations by hand, they are much better done by someone else.

Meteor, IMO, is currently one of the best tools that deal with client/server data synchronization. It makes CRUD operations trivial (one line of code to create a functional model), and more complex data logic easy. Among quite a few newer data access solutions I tried out there, I would say only Firebase rivals Meteor in usability and functions, and they are ahead of others by quite a large margin. Meteor is harder to use than Firebase, but offers much more control over data.

AngularJS, on the other hand, is a more popular and well known library. It has arguably won the frontend library war against a bunch of other strong contenders such as Ember.js, Knockout.js, etc. Developing a non-trivial UI like Remonit dashboard designer is considerably easier and much more modular using AngularJS than other options.

But why both?

The short answer is simple: Meteor, especially its frontend engine, isn’t production ready yet, and AngularJS is currently the best at this.

Although they overlap and conflict with each other on quite a few things, Meteor and AngularJS complement each other extremely well. They together form a very complete and usable webdev system: good database API, flexible user account system, assets management, easy deployment, database-to-DOM data synchronization, reusable web component, html5 routing, and a lot more.


Now, we talk about their conflicts

Here is a list of things that I found Meteor and AngularJS don’t like each other about or don’t agree on:

  1. Script load order (namely, when angular.js is loaded)
  2. Bootstrapping of ngApp
  3. Loading of Angular template files, and template delimiter {{…}}
  4. Declaration and usage of Meteor collections
  5. Code minification

Before walking through each of this list together, I want to also briefly list my solutions to the problems (and you can skip the rest if you don’t care for technical details):

  1. Follow a runlevel-like convention of organizing files.
  2. Instead of using ng-app on <html> or <body>, use it on a page container <div>.
  3. Change Angular template delimiter to something else, organize template files under Meteor’s convention, and load all templates from Meteor’s Template at start.
  4. Declare Meteor collections at global scope, create a wrapping function around Meteor’s Deps.autorun for data binding.
  5. Follow Angular’s dependency injection convention on EVERY occasion.

Now let’s look at these conflicts one by one.

Load order

This is rather a Meteor problem than a conflict, but needs to be sorted out first. Currently, Meteor has an awkward convention of Javascript files load order: it concatenates all your .js files under project directory and load them before anything when your actual page loads. The order during concatenation depends on file names and their directory depths.

I used the approach of following a runlevel-like conventions for placing script files: all client side .js files are placed under /client; library files are placed under /client/lib; libraries that must be loaded before others are placed under numbered directories such as /client/lib/00_angular, /client/lib/01_jquery-ui, /client/lib/01_codemirror, etc.

This approach is very straightforward, but might feel a bit hackish. There’s another approach that is to use a Meteorite package containing the angular.js file. However, there is currently no such Meteorite package that provides updated angular.js file (you really only need this one file, pre-loading any other angular files is unnecessary and only decreases its flexibility). Creating your own package, however, is not hard, and actually works with Bower.

Bootstrapping Angular

Angular apps need to be bootstrapped by either including an ng-app attribute at a root node, or manually through code. However, Meteor currently errors if you use <html> tag or <body> tag with extra attributes. The solution to this is very simple: instead of doing this

<body ng-app="my-app">
...
</body>

Do this:

<body><div ng-app="my-app">
...
</div></body>

Angular template files

There are a few problems involved regarding template files. First, Meteor compiles all .html files (actually all your project files) and serve them indistinguishably to all requests, which makes retrieving a single template file impossible. Second, Meteor compiles .html files and change things between {{ and }} into code before serving them. Third, currently Meteor is tightly coupled with its UI engine and it’s very hard to change these behaviors.

My solution is inspired by ngMeteor, and is basically another structuring convention. I wrap each template files inside a HTML template block:

<template name="template_filename.html">
...
</template>

This makes Meteor save the contents of each template file into a property of Meteor’s Template object. Then, you can load these templates when Angular starts, and use them as usual for templateUrl:

https://gist.github.com/zefei/c00a5adacbf237e3a1b7

Update for Meteor 0.8.0: Meteor now uses a new template engine, and has deprecated the render() method. Use the following code instead to load templates:

https://gist.github.com/zefei/4ea6eeed89cf3a3dfb18

Then, we need to change Angular’s template delimiter, so that Meteor doesn’t try to compile them:

https://gist.github.com/zefei/17b29d6bf03417c2122d

This solution has a very handy side effect: it makes manually loading/compiling Angular templates very simple since all templates are loaded already in template cache. This is particularly useful if you want to create recursive directives.

There’s also another less hackish solution to this: placing all templates under /public and treat them as static files. The problem with this approach is that Meteor aggressively caches static files, and this can create a lot of headaches during development.

Meteor Collection

Using Meteor Collection with AngularJS is the single reason that we want to use these two frameworks together: we want data synchronization from database views straight to the DOM. There isn’t much of a conflict concerning Meteor Collection and Angular, but rather, the question of how to use them efficiently.

During my experimentation with both frameworks, I’ve tried quite a few ways of using Meteor Collection. The conclusion I reached is to use them in a very Meteor fashion: define collections at global scope, and use Meteor’s reactive system to handle synchronization, namely Deps.autorun.

I wrapped Deps.autorun in the following Angular factory, to make sure Angular is notified when data synchronization occurs:

https://gist.github.com/zefei/8a18059ecceebee45bee

With the above function, we can now bind Meteor Collection to Angular scopes very easily:

https://gist.github.com/zefei/a5df71d4b7e4402029a6

Update (Oct 14, 2014): as @qinfeng pointed out, this data binding may choke on very large collections as Angular will re-render DOM for every item in the collection, regardless of whether they are changed, because they all have new object references. In this case (very large fetched collections), we need to reuse unchanged old items, so that they can pass Angular’s dirty check. Use the method below to bind Meteor collections instead:

https://gist.github.com/zefei/db15affe9848ef00cd67

Updated examples: recently I was often asked about using other Meteor classes, so I decided to add a few more examples below to show how this autorun wrapper can be used to achieve different goals. Note that you can use Meteor classes directly if they are non-reactive, autorun is only needed for reactivity. Read more on Meteor reactivity.

Example 1: using Meteor accounts

https://gist.github.com/zefei/09b7622d1c8a928d5c58

Example 2: using Meteor sessions

https://gist.github.com/zefei/aa8721ff95affe0c253b

Example 3: using autorun with $scope.$watch

https://gist.github.com/zefei/e8c0e9c987a219c7b614

Code minification

This is rather an Angular gotcha than a problem, but magnified because of Meteor’s super easy deployment. AngularJS heavily relies on dependency injection (fancy Java word for runtime dependency resolution). If you don’t follow Angular’s best practice on declaring dependencies, it will break eventually.

When used with Meteor, Angular may run fine in development but fail to start in production. This is because once deployed, Meteor does all the heavy lifting of compiling and renaming asset files, meaning all .js files are minified. This can break if you use reflection based dependency declaration in Angular.

The correct way is to simply use the array notation, and it’s important to use it EVERYWHERE, including your controllers, directives, the controller function inside directives, configurations, services, factories, run scripts, and whatnot. Miss any one of those and you will get a very very hard to understand error message on your production site.


Good libraries to consider

Update (Oct 29, 2014): now Meteor 1.0 is released, it has become much more stable and usable. I think the need to easily integrate it with other libraries/frameworks will also grow along its adoption. Here I’ll keep an updated list of libraries that can help the integration:

  • angular-meteor: rich Meteor-Angular bridge as a Meteor package
  • asteroid: framework-agnostic Meteor client for frontend dev

The list is currently very short, please message me if you have other good libraries to add.


Conclusion

Even though Meteor and AngularJS together can give you some headaches and hiccups, they are very powerful tools and are each ahead of competitions in their sectors. So, kudos to both teams and hope they can keep up the great work.