In Meteor, when you retrieve a record from a database, it\'s only a record. So if I have a collection called Dogs
, a dog
might have fur: \'br
Astronomy is a new answer to this old question of Meteor models. The author has a lot more features planned that are in the works and the support of other prominent Meteor package authors. The only downside is perhaps it's a bit bleeding edge.
Astronomy is also highly modularized, a prime example for building in a meteor packaged/modularized style.
This is a relatively old question in terms of Meteor, but I think dburles:collection-helpers fits the bill of what you wanted to achieve, by implementing what Flavien Volken suggested. Perhaps it's useful for anyone wandering in here recently.
This is a start at overriding Meteor.Collection to support methods on objects.
Meteor.Kollection = Meteor.Collection;
Meteor.Kollection.extend = function(constructor) {
var parent = this;
var child = function() {
Meteor.Kollection.apply(this, arguments);
constructor.apply(this, arguments);
};
_.extend(child, parent);
function __proto__() { this.constructor = child; };
__proto__.prototype = parent.prototype;
child.prototype = new __proto__;
return child;
};
Meteor.Collection = Meteor.Kollection.extend(function(name, options) {
if (options && options.defaults) {
this.defaults = options.defaults;
}
});
Meteor.Collection.prototype.applyDefaults = function(attrs) {
return _.defaults(attrs, this.defaults);
};
Meteor.Collection.prototype.create = function(attrs) {
if (typeof attrs !== "object") attrs = {};
return this.applyDefaults(attrs);
};
Meteor.Collection.prototype.findOne = function(selector, options) {
var object = Meteor.Kollection.prototype.findOne.apply(this, arguments);
return this.applyDefaults(object);
};
You may notice the new collection.create method, and that collection.findOne has been overridden. I imagine all collection.* methods will need to be overridden, but this is a start.
So what can you do with this?
var Dogs = new Meteor.Collection("dogs", { defaults: {
barkSound: "ruff",
bark: function() {
console.log(this.barkSound);
}
}});
if (Meteor.isClient) {
var regularDog = Dogs.create();
regularDog.bark(); // ruff
var smallDog = Dogs.create({
barkSound: "yip"
});
smallDog.bark(); // yip
Dogs.insert(smallDog, function(error, id) {
Dogs.findOne(id).bark(); // yip
});
});
I'm not exactly sure how this happens, but any functions in an object are removed when they are inserted. Therefore, we can directly apply the methods to the object. First, you create your collection by passing in an object with the defaults
property. This property can include properties or methods. To create a new object for a given collection, use collection.create(attrs), where attrs is an option argument that includes additional or overridden properties and methods.
Additions to @Flavien Volken's answer you can pass arguments to the method you've added to the collection.
Car = new Mongo.Collection('car', {
transform: function(entry) {
entry.is_watched = function(userId) {
var is_watched = false;
if (entry.watchList) {
for (var i in entry.watchList) {
if (entry.watchList[i].userId == userId) {
is_watched = true;
break;
}
}
}
return is_watched;
};
return entry;
}
});
In template (passing id of the logged in user):
{{# if is_watched currentUser._id }}
Watched
{{ else }}
Not watched
{{/ if }}
While there might be an official model system in the works there are some things you can do now:
There is one by Mario Uhler which is activerecord like and quite nice, in coffeescript: https://coderwall.com/p/_q9b1w
There is also a community package made by Tom Coleman thats very helpful with models: https://github.com/tmeasday/meteor-models, you might need meteorite to add it as a package.
And of course as you suggested Backbone. I personally use js prototypes but not everyone may be comfy with them, I just used them so its easy to transition when meteor's model system is out, its also easy to share between the client and server without too many packages to add.
You can use the transform
parameter in the Collection to overload the object with custom functions
var Dogs = new Meteor.Collection("dogs",
{
transform:function(entry)
{
entry.bark = function(){ console.log(this.barkSound);};
return entry;
}
});
Then:
var aDogID = new Dogs.insert({barkSound: "ruff"})
Dogs.find(aDogID).bark(); // "ruff"
Bonus: If for any reason you would like to use a similar concept as proposed by Andrew Ferk, just use the _.defaults(object, *defaults) function.
var defaults = {
barkSound: "ruff",
bark: function() {
console.log(this.barkSound);
}
}
Dogs = new Meteor.Collection("dogs",
{
transform: function(object) {
return _.defaults(object, defaults);
}
});