The simplest no-conflict way to use AngularJS with Meteor, Meteorite and Atmosphere Smart Packages.
JavaScript CSS
Latest commit 20b97a0 Aug 1, 2014 @loneleeandroo Update smart.json
Permalink
Failed to load latest commit information.
lib Updated to AngularJS v1.2.14 Mar 10, 2014
modules fix tabs Jul 30, 2014
.gitignore Updated .gitignore Feb 6, 2014
README.md Update README.md Mar 30, 2014
ngMeteor.js Updated compatibility with Iron Router Mar 10, 2014
package.js Update package.js Feb 11, 2014
smart.json Update smart.json Aug 1, 2014

README.md

ngMeteor

The simplest no-conflict way to use AngularJS with Meteor, Meteorite and Atmosphere Smart Packages. Currently implementing a Demo Page. Also, I made this little app using ngMeteor so that I could calculate the type effectiveness quickly while I was playing Pokemon Y, PokeLab.

ngMeteor v0.1.22+ works with Blaze.

Quick start

  1. Install Meteor curl https://install.meteor.com | /bin/sh
  2. Install Meteorite npm install -g meteorite
  3. Create a new meteor app using meteor create myapp or navigate to the root of your existing app.
  4. Install ngMeteor mrt add ngMeteor

Usage

Table of Contents

New Data-Binding to avoid conflict

To prevent conflicts with Handlebars, ngMeteor has changed the default AngularJS data bindings from {{foo}} to [[foo]]. For example:

<h2>Todo</h2>
<div ng-controller="TodoCtrl">
  <span>[[remaining()]] of [[todos.length]] remaining</span>
  [ <a href="" ng-click="archive()">archive</a> ]
  <ul class="unstyled">
    <li ng-repeat="todo in todos | filter:todoText">
      <input type="checkbox" ng-model="todo.done" ng-change="saveTodo()">
      <span class="done-[[todo.done]]">[[todo.text]]</span>
    </li>
  </ul>
  <form ng-submit="addTodo()">
    <input type="search" ng-model="todoText" size="30" placeholder="search/add new todo here">
    <input class="btn btn-primary" type="submit" value="add">
  </form>
</div>

Using Meteor Collections

I'm working on improving the way the $collection service works in the next version of ngMeteor. The improvements will slightly change the way you currently use $collection in your application, more details on the changes will be available upon the release of the new $collection service. The changes will give you greater flexibility and control over how you declare, subscribe and publish collections using ngMeteor without deviating too much from the current implementation.

ngMeteor uses the default Meteor collections, so you would declare a new collection like this:

todos = new Meteor.Collection("todos");

To preserve the reactivity of the Meteor collection in your AngularJS controllers and directives, you should inject and use the $collection service instead of calling the Meteor collection directly. The $collection service will automatically subscribe to your published Meteor collection so you will not need to use Meteor.subscribe beforehand. This is how you would use the $collection service:

$collection(name, scope, selector, options, publisher)

Where the name argument refers to the name of the Meteor Collection, scope refers to the $scope you would like to add the collection to, and the arguments selector and options are the same as they are for Meteor.Collection.find. The selector and options arguments will be passed to Meteor.publish on the server. Additionally, the publisher argument allows you to optionally pass additional arugments to Meteor.publish on the server (see the example publish function below). For example, if I have a Meteor collection called "todos", which I want to add into the scope of my controller, $scope, then I would do this:

$collection("todos", $scope)

which would create a model called "todos" in $scope, and I would refer to it in my controller using:

$scope.todos

I would also be able to refer to it in my html using:

[[todos]]

and use it as if it was an ordinary AngularJS model:

[[todos.length]]

and it would also work for things like ng-repeat:

<ul class="unstyled">
  <li ng-repeat="todo in todos">
    <input type="checkbox" ng-model="todo.done">
    <span class="done-[[todo.done]]">[[todo.text]]</span>
  </li>
</ul>

If you want a model to be accessible across controllers, so that changes made to the model in one controller are reflected immediately in a different controller, you could use the $rootScope:

$collection("todos", $rootScope)

Furthermore, AngularJS models defined using the $collection service will have access to all the methods available to a native AngularJS model with the addition of the following methods, which allows you to persist changes you have made in the AngularJS model to the Meteor collection:

$scope.todos.add(data)
$scope.todos.delete(data)

Where the add method is a replacement for both Meteor.Collection.insert and Meteor.Collection.update, which is also considered the persistent version of the AngularJS push method, and the delete method is a replacement for Meteor.Collection.remove. All changes made will be based on the _id property.

There is also a ready method available, you need to remove the autopublish smart package for it to work:

$scope.todos.ready(function(){ ... })

Remember that you must first publish the collection from the server to the client before you can access it on the client if you have removed the autopublish and insecure packages:

Meteor.publish("todos", function (selector, options, publisher) {
  return todos.find(selector, options);
});

todos.allow({
  insert: function(){
    return true;
  },
  update: function(){
    return true;
  },
  remove: function(){
    return true;
  }
});

The current way to use Meteor.users is to do this:

Users = Meteor.users;

if(Meteor.isClient){
    ngMeteor.controller("TodoCtrl, ['$scope','$collection', 
        function($scope,$collection){
            $collection('Users', $scope);
        }
    ]);
}

Adding controllers, directives, filters and services

It is best practice to not use globally defined controllers like they do in the AngularJS demos. Always use the exported package scope ngMeteor as your angular module to register your controller with $controllerProvider. Furthermore, to prevent errors when minifying and obfuscating the controllers, directives, filters or services, you need to use Dependency Injection. For example:

ngMeteor.controller('TodoCtrl', ['$scope', '$collection',
  function($scope, $collection) {
    $collection("todos", $scope);

    $scope.addTodo = function() {
      $scope.todos.add({text:$scope.todoText, done:false});
      $scope.todoText = '';
    };

    $scope.saveTodo = function(){
      $scope.todos.add($scope.todos);
    }

    $scope.remaining = function() {
      var count = 0;
      angular.forEach($scope.todos, function(todo) {
        count += todo.done ? 0 : 1;
      });
      return count;
    };

    $scope.archive = function() {
      angular.forEach($scope.todos, function(todo) {
        if (todo.done) $scope.todos.delete(todo);
      });
    };
  }
]);

Creating and inserting template views

A template is defined using the template tags (this could be in a standalone file or included in another file).

<template name="foo">
  <h1>Hello, World!</h1>
</template>

You can render this template using handlebars as you would for any other Meteor app:

{{> foo}}

Templates will also be added to the $templateCache of the ngMeteor angular module. To invoke the template in AngularJS you could use ng-view and specify the template in the $templateCache when defining your routes using the $routeProvider or your could use the ng-template directive to render your template like this:

<ANY ng-template="foo"></ANY>

<!--Add the ng-controller attribute if you want to load a controller at the same time.-->    
<ANY ng-template="foo" ng-controller="fooCtrl"></ANY>

Templates with names starting with an underscore, for example "_foo", will not be put into the $templateCache, so you will not be able to access those templates using ng-template, ng-include or ng-view.

Routing

The ngRoute module developed by the AngularJS team is included in ngMeteor, which will satisfy those with simple routing needs. For example, if you want to call a template called 'foo' and a controller called 'TodoCtrl' when someone lands on your home page you would define your route like this:

ngMeteor.config(['$routeProvider', '$locationProvider',
  function($routeProvider, $locationProvider) {
    $routeProvider.when('/',{
      templateUrl: 'foo',
      controller: 'TodoCtrl'
    });

    $locationProvider.html5Mode(true);
  }
]);

For larger applications with more complexed routes, it would be wise to consider using the meteor-angular-ui-router smart package for ngMeteor, which exposes the popular ui-router module to ngMeteor. For those of you that have grown accustomed to the Meteor methods of routing, ngMeteor is compatibile with Iron Router.

Module Injection

If you have a module called myModule, for example:

myModule = angular.module('myModule',[]);

it can be easily injected into ngMeteor like this:

ngMeteor.requires.push('myModule');

Using this method, additional functionality has been provided to ngMeteor in the form of separate atmosphere smart packages that expose and inject angular modules into ngMeteor. These packages have been developed by either the ngMeteor Team and/or by third parties. The following is a non-exhaustive list of these packages:

Feel free to make ngMeteor module smart packages, and please contact loneleeandroo if you would like your package to be listed here as well.