Mongo Document Methods
Adds helpful methods to each of your documents to easily update/duplicate/remove them.
Instead of writing:
MyCollection.update(this._id, { $set: { a: 1 } });
Now you can write:
this.$set({ a: 1 })
Where this
is a document that was findOne
'd.
There's more stuff you can do, see below.
Installation
meteor add lai:document-methods
Note
If you have certain collection definitions with transforms applied to them by other means other than collection helpers (i.e CollectionFS), then those collections won't have these methods.
This uses dburles:collection-helpers
to get the job done. All of these methods are injected to all of your collections as collection helpers. Thanks @dburles for your packages, without them this would not be possible, or rather, it would've made this super hard to make!
Examples
1Stuff = new Mongo.Collection('stuff'); 2 3var thing = Stuff.findOne(); 4 5// Set individual properties 6thing.$set({ foo: 'bar' }); 7 8// Or multiple properties 9thing.$set({ foo: 'bar', bar: 'baz' }); 10thing.$set({ foo: 'bar', bar: 'baz', testArray: [1, 2, 3] }); 11 12// You can also modify them by setting new properties 13// Note: setting properties/modifying them does not trigger reactivity) 14thing.newProperty = 'that means absolutely nothing'; 15thing.anotherOne = 'yea!'; 16// Then saving it (this will update and trigger reactivity) 17thing.$save(); 18 19// The following methods mimic the Mongo functions of $push, $pushAll, 20// $unset, $addToSet, $pull, $pullAll, and $pop 21// (Note: they will apply the update immediately) 22thing.$push({ myArrayProperty: 1 }); 23thing.$pushAll({ myArrayProperty: [2, 3, 4, 5] }); 24thing.$addToSet({ myPropertySet: { a: 1, b: 2, c: 3} }); 25thing.$pull({ myArrayProperty: 2 }); 26thing.$pullAll({ myArrayProperty: [1, 3, 4] }); 27thing.$pop({ myArrayProperty: 1 }); 28thing.$unset({ myArrayProperty: '' }); 29 30// If you think this is too limiting, you can have full control of the modifier, 31// but only on the modifier and not the criteria because remember, 32// these are methods that correspond to one specific document 33thing.$update({ 34 $set: { 35 a: '1', 36 b: '2' 37 }, 38 $addToSet: { 39 myPropertySet: { 40 foo: 'bar', 41 bar: 'baz' 42 } 43 }, 44 ... 45}); 46 47// If you wanted to create a duplicate of this document 48var newThing, newThingId = thing.$duplicate(); 49if (newThingId) { 50 newThing = Stuff.findOne(newThingId); 51 // do new things with this newThing 52} 53 54// And once you've realized that you created a 55// completely meaningless document, you can remove it 56thing.$remove(); 57newThing.$remove();
A more practical example
1// app.js 2Todos = new Mongo.Collection('todos'); 3 4if (Meteor.isClient) { 5 Template.todos.helpers({ 6 todos: function () { 7 return Todos.find(); 8 }, 9 checked: function () { 10 return this.done ? 'checked' : ''; 11 } 12 }); 13 Template.todos.events({ 14 'change .todo-chk': function (event, template) { 15 this.$set({ done: !this.done }); 16 }, 17 'submit .add-tag': function (event, template) { 18 event.preventDefault(); 19 var tag = $.trim($(event.target).find('.tag-input').val()); 20 tag && this.$addToSet({ tags: tag }); 21 }, 22 'click .duplicate': function (event, template) { 23 // You can change the properties before duplicating it too! 24 this.title = this.title.concat(' - new title'); 25 this.$duplicate(); 26 }, 27 'click .delete': function (event, template) { 28 this.$remove(); 29 } 30 }); 31}
1<template name="todos"> 2 {{#each todos}} 3 <div class="todo"> 4 <input type="checkbox" class="todo-chk" {{checked}}> 5 {{title}} - <button class="duplicate">duplicate</button> - <button class="delete">delete</button> 6 <br> 7 Tags: 8 <br> 9 {{#each tags}} 10 {{.}} 11 {{/each}} 12 <br> 13 <form class="add-tag"> 14 <input type="text" class="tag-input"> <button>Add</button> 15 </form> 16 </div> 17 <hr> 18 {{/each}} 19</template>
API
Where document
is a document that was findOne
'd.
For all update-related callbacks, If present, called with an error object as the first argument and, if no error, the number of affected documents as the second.
For the duplicate callback, if present, called with an error object as the first argument and, if no error, the _id as the second.
For the remove callback, if present, called with an error object as its argument.
If no callbacks were provided, they should function exactly as an insert, update, remove as specified in the Meteor documentation (insert will return the _id, update will return 1, remove will remove nothing, etc).
document.$update(modifier [, callback])
Mimics MyCollection.update(this._id, modifier)
.
document.$save([callback])
Updates the current document with its current properties.
document.$duplicate([callback])
Does an insert of the document, returns _id if no callback was provided, otherwise, it's returned in the second argument of the callback.
document.$remove([callback])
Mimics MyCollection.remove(this._id)
document.$set(propertiesToSet [, callback])
Mimics MyCollection.update(this._id, { $set: { prop1: 1, prop2: 2 } })
document.$unset(propertiesToUnset [, callback])
Mimics MyCollection.update(this._id, { $unset: { prop1: '' } })
document.$addToSet(setToAdd [, callback])
Mimics MyCollection.update(this._id, { $addToSet: { mySetOfThings: { a: 1, b: 2 } } })
document.$push(thingsToPush [, callback])
Mimics MyCollection.update(this._id, { $push: { myList: 1 } })
document.$pushAll(allThingsToPush [, callback])
Mimics MyCollection.update(this._id, { $pushAll: { myList: [1, 2, 3, 4] } })
document.$pull(thingToPull [, callback])
Mimics MyCollection.update(this._id, { $pull: { myList: 1 } })
document.$pullAll(allThingsToPull [, callback])
Mimics MyCollection.update(this._id, { $pullAll: { myList: [1, 2, 3, 4] } })
document.$pop(thingToPop [, callback])
Mimics MyCollection.update(this._id, { $pop: { myList: 1 } })
FAQs
Won't this override my collection helpers?
No it doesn't, these helpers will be added alongside your current collection helpers, just make sure don't have collection helpers that have the same name as these methods.
Now What?
I wrote some tests but I would love your feedback and for you guys to test it out as well.
Also, when I created this, I had intially named them without the $
prefix, but then I figured that you might run into name conflicts and that's why I decided to add the $
prefix. Let me know your thoughts.
Thoughts
I guess it is obvious that you shouldn't use this in Blaze even though you can, and perhaps you're thinking that collection helpers are supposed to be only used for Blaze!
But this brings up a discussion of:
Shouldn't documents have a nice API to manipulate them, at least at the individual document level?
License
MIT