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.
- Install Meteor
curl https://install.meteor.com | /bin/sh
- Install Meteorite
npm install -g meteorite
- Create a new meteor app using
meteor create myapp
or navigate to the root of your existing app. - Install ngMeteor
mrt add ngMeteor
- New Data-Binding to avoid conflict
- Using Meteor Collections
- Adding controllers, directives, filters and services
- Creating and inserting template views
- Routing
- Module Injection
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>
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);
}
]);
}
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);
});
};
}
]);
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.
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.
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:
- angular-ui-router empowers ngMeteor with the ui-router module.
- spiderable-ui-router integrates the Meteor spiderable smart package with the angular-ui-router smart package.
- angular-bootstrap empowers ngMeteor with the ui-bootstrap framework.
- ionic empowers ngMeteor with the Ionic Framework.
- angular-leaflet empowers ngMeteor with the Leaflet directive.
- angular-nojquery prevents jQuery from interfering with ngMeteor.
- angular-ui-utlis empowers ngMeteor with the ui-utlis utiliy package.
- ngStorage empowers ngMeteor with the ngStorage module.
Feel free to make ngMeteor module smart packages, and please contact loneleeandroo if you would like your package to be listed here as well.