Skip to content

saleswise/angular-meteor

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

This repo modifications

  • Angular 1.3

The power of Meteor and the simplicity and eco-system of AngularJS

Quick start

  1. Install Meteor curl https://install.meteor.com | /bin/sh
  2. Create a new meteor app using meteor create myapp or navigate to the root of your existing app.
  3. Install urigo:angular meteor add urigo:angular

Getting started tutorial

http://angularjs.meteor.com/tutorial

Mailing list

https://groups.google.com/forum/#!forum/angular-meteor

Package

urigo:angular

Usage

Table of Contents

App initialization

If you have a module called myModule, for example:

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

More in step 0 in the tutorial

New Data-Binding to avoid conflict

To prevent conflicts with Meteor's Blaze live templating engine, angular-meteor has changed the default AngularJS data bindings from {{...}} to [[...]]. For example:

<div>
    <label>Name:</label>
    <input type="text" ng-model="yourName" placeholder="Enter a name here">
    <hr>
    <h1>Hello [[yourName]]!</h1>
</div>

More in step 2 of the tutorial

Using Meteor Collections

angular-meteor provides an AngularJS service called $collection, which is a wrapper for Meteor collections to enable reactivity within AngularJS. The $collection service no longer subscribes to a publisher function automatically, so you must explicitly subscribe to a publisher function before calling the $collection service.

$collection(collection, selector, options)
Arguments Type Description Required
collection Meteor Collection Object The Meteor Collection Yes
selector Mongo Selector (Object or String) Same as Meteor Collection Find No
options Object Same as Meteor Collection Find No

bind, which is used to bind the collection to an Angular model so that you can use it in your scope:

bind(scope, model, auto, publisher)
Arguments Type Description Required Default
scope Scope The scope the collection will be bound to. Yes
model String The name of the model that the collection will be bound to. Yes
auto Boolean By default, changes in the model will not automatically update the collection. However if set to true, changes in the client will be automatically propagated back to the collection. A deep watch is created when this is set to true, which will degrade performance. No false
publisher Boolean/String By default, bind method will not automatically subscribe to the collection. However if set to true, bind will call Meteor.subscribe on the current collection. you can also set publisher to a string and then bind will call Meteor publish with that string. No false
paginate Boolean By default, bind method will not automatically add support to paginate the collection. However if set to true, the bind method will also add pagination functionality to the scope. No false

Paginate will use the following scope properties to implement pagination:

Property Type Description Required Default
perPage Number The number of items on each page
page Number The current page number (1 based). A $watch is setup to re-fetch the collection on change Yes

Once a collection has been bound using the bind method, the model will have access to the following methods for upserting/removing objects in the collection. If the auto argument has been set to true, then the user will not need to call these methods because these methods will be called automatically whenever the model changes.

Method Argument Type Description
save(docs) docs Object or Array of Objects Upsert an object into the collection. If no argument is passed, all the objects in the model of the collection will be upserted.
remove(keys) keys _id String or Array of _id Strings Removes an object from the collection. If no argument is passed, all the objects in the collection will be removed.

More in step 3 of the tutorial

For example:

/**
 * Assume autopublish package is removed so we can work with multiple publisher functions.
 * If insecure package is also removed, then you'll need to define the collection permissions as well.
 **/

// Define a new Meteor Mongo Collection
Todos = new Mongo.Collection('todos');

if (Meteor.isClient) {
    app.controller("mainCtrl", ['$scope', '$collection',
        function($scope, $collection){

            // Subscribe to all public Todos
            Meteor.subscribe('publicTodos');

            // Bind all the todos to $scope.todos
            $collection(Todos).bind($scope, 'todos');

            // Bind all sticky todos to $scope.stickyTodos
            $collection(Todos, {sticky: true}).bind($scope, 'stickyTodos');

            // todo might be an object like this {text: "Learn Angular", sticky: false}
            // or an array like this [{text: "Learn Angular", sticky: false}, {text: "Hello World", sticky: true}]
            $scope.save = function(todo) {
                $scope.todos.save(todo);
            };

            $scope.saveAll = function() {
                $scope.todos.save();
            };

            $scope.toSticky = function(todo) {
                if (angular.isArray(todo)){
                    angular.forEach(todo, function(object) {
                        object.sticky = true;
                    });
                } else {
                    todo.sticky = true;
                }

                $scope.stickyTodos.save(todo);
            };

            // todoId might be an string like this "WhrnEez5yBRgo4yEm"
            // or an array like this ["WhrnEez5yBRgo4yEm","gH6Fa4DXA3XxQjXNS"]
            $scope.remove = function(todoId) {
                $scope.todos.remove(todoId);
            };

            $scope.removeAll = function() {
                $scope.todos.remove();
            };
        }
    ]);
}

if (Meteor.isServer) {

    // Returns all objects in the Todos collection with public set to true.
    Meteor.publish('publicTodos', function(){
        return Todos.find({public: true});
    });

    // Returns all objects in the Todos collection with public set to false.
    Meteor.publish('privateTodos', function(){
        return Todos.find({public: false});
    });

}

bindOne - used to bind a single model to your scope:

bindOne(scope, model, id, auto)
Arguments Type Description Required Default
scope Scope The scope the model will be bound to. Yes
model String The scope property the model will be bound to. Yes
id String The id used to look up the model from the collection Yes
auto Boolean By default, changes in the model will not automatically update the collection. However if set to true, changes in the client will be automatically propagated back to the collection. A deep watch is created when this is set to true, which will degrade performance. No false
publisher Boolean/String By default, bindOne method will not automatically subscribe to the collection. However if set to true, bind will call Meteor.subscribe on the current collection. you can also set publisher to a string and then bind will call Meteor publish with that string. No false

More in step 6 of the tutorial

Subscribe

angular-meteor provides an AngularJS service called $subscribe, which is a wrapper for Meteor.subscribe to subscribe the client to a Meteor.publish Method within AngularJS with promises.

$subscribe.subscribe(name, subscribeArguments)   

Returns a promise when subscription is ready.

$subscribe.subscribe('todos').then(function(){
              console.log($scope.todos);
            });

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 angular-meteor 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:

app.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 angular-meteor 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 you 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.

meteor-include

You can include Meteor templates with (loginButtons is the template name).

<template name="phones">
  <meteor-include src="loginButtons"></meteor-include>
    <ul>
      <li ng-repeat="phone in phones">
        [[phone.name]]
        <p>[[phone.snippet]]</p>
      </li>
    </ul>
</template>

Routing

It would be wise to consider using the urigo:angular-ui-router Meteor package for angular-meteor, which exposes the popular ui-router module to angular-meteor. For those of you that have grown accustomed to the Meteor methods of routing, angular-meteor is compatible with Iron Router.

More in step 5 of the tutorial

User

angular-meteor adds to your application 2 $rootScope variables -

  • $rootScope.currentUser - The current logged in user and it's data. it is undefined if the user is not logged in.

  • $rootScope.loggingIn - True if a login method (such as Meteor.loginWithPassword, Meteor.loginWithFacebook, or Accounts.createUser) is currently in progress.

    if ($rootScope.currentUser)

    if (!$rootScope.currentUser)

also available in the templates:

ng-if="$root.curentUser"

More in step 8 of the tutorial

Meteor methods with promises

angular-meteor introduces the $methods service which wraps up Meteor.methods with AngularJS promises.

Simply call $methods.call function and instead of sending the function of handling success and error as a parameter, handle the success and error in the AngularJS way with the 'then' method:

$scope.invite = function(party, user){

  $methods.call('invite', party._id, user._id).then(
    function(data){
      // Handle success
      console.log('success inviting', data.userId);
    },
    function(err){
      // Handle error
      console.log('failed', err);
    }
  );
  
};

Additional packages

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

Feel free to make angular-meteor module smart packages, and please contact urigo if you would like your package to be listed here as well. Be sure to be compatible with Meteor 0.9.0 and above and it's packaging system!

About

Combining the simplicity and power of AngularJS and Meteor

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages

  • CSS 97.8%
  • JavaScript 1.3%
  • Shell 0.9%