Skip to content

Instantly share code, notes, and snippets.

@fredrikekelund
Last active August 29, 2015 14:04
Show Gist options
  • Save fredrikekelund/9cccb560192fddde8570 to your computer and use it in GitHub Desktop.
Save fredrikekelund/9cccb560192fddde8570 to your computer and use it in GitHub Desktop.
Angular factory to keep a Meteor collection and an Angular model in sync. You should probably use https://atmospherejs.com/urigo/angular instead.
"use strict";
// The basis for the following code is taken from https://medium.com/@zfxuan/the-wonderful-duo-using-meteor-and-angularjs-together-4d603a4651bf
var app = angular.module("DataTool.factories", [])
.factory("autorun", ["$window", function($window) {
/**
* Keeps the Angular model (attached to scope) in two-way sync with the Meteor collection.
* Attaches wrapper methods for inserting and removing items from the scope object.
* Also attaches a ready property to the scope object that changes when (you guessed
* it) the collection has loaded.
*
* @param {Object} scope Angular scope to attach the model to
* @param {String} name Name of the Meteor collection to subscribe to and of the model property that's set on the scope
* @param {String} collectionName If the name of the Meteor collection should be different than the name of model on the scope, this property will represent the name of the collection
* @param {Object} sorting Object passed to Collection.find for sorting the collection
* @return {Object} Meteor Deps.autorun object
*/
return function(scope, name, collectionName, sorting) {
collectionName = (!!collectionName) ? collectionName : name;
var runScopeDigest = function(scope) {
// Wrap $apply in setTimeout to avoid conflict with other digest cycles
setTimeout(function() {
scope.$apply();
}, 0);
},
bindCollectionToModel = function() {
Meteor.subscribe(collectionName, function() {
scope[name].ready = true;
// The scope doesn't react to the change above without a manual apply call
runScopeDigest(scope);
});
var fetchedCollection = $window[collectionName].find({}, sorting || {}).fetch();
if (!angular.equals(scope[name], fetchedCollection)) {
var ready = (scope[name]) ? scope[name].ready : false;
scope[name] = fetchedCollection;
Object.defineProperties(scope[name], {
insert: {
configurable: true,
value: function(item) {
$window[collectionName].insert(item);
}
},
justChanged: {
configurable: true,
writable: true,
value: true
},
// The "ready" property will be changed in the collections onready callback.
// It can be helpful for knowing whether a collection is empty or just
// currently loading (and empty because of that)
ready: {
configurable: true,
writable: true,
value: ready
},
remove: {
configurable: true,
value: function(item) {
var id = (item._id) ? item._id : item;
$window[collectionName].remove(id);
}
}
});
}
},
// Wrapping around Deps.autorun
autorun = Deps.autorun(function(computation) {
bindCollectionToModel();
// This is run immediately for the first call but after that, we need to $apply to start Angular digest
if (!computation.firstRun) {
runScopeDigest(scope);
}
});
// Bind Model to Collection
scope.$watch(name, function(newValue, oldValue) {
// If the change comes from the Collection (ie. the network), and not the DOM,
// we don"t need to check for what has changed in Angular"s model
if (newValue.justChanged) {
delete scope[name].justChanged;
return false;
}
for (var i = 0; i < newValue.length; i++) {
var object = newValue[i];
if (!object) {
break;
}
delete object.$$hashKey;
// Key was updated
if (object._id) {
$window[collectionName].update(object._id, object);
}
}
}, true);
// Stop autorun when scope is destroyed
scope.$on("$destroy", function() {
autorun.stop();
});
// return autorun object so that it can be stopped manually
return autorun;
};
}]);
@fredrikekelund
Copy link
Author

angular-meteor is (at version 0.6) a rapidly maturing package that you should look into using if you're interested at all in combining Meteor and Angular. The collection-to-model integration is a lot better than here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment