Reputation: 1894
I have a collection that uses transform to instantiate the documents from classes. Those instances then set new attributes, data fetched from 3rd part apis and made reactive.
Now, I need to sort those objects based on a method that retrieve the reactive data. But I cannot do a collection find.sort, or use collection-hooks, because it operates on the document before transforming it, hence the method is not available.
Therefore, it seems to me that the only way to sort that collection based on that data which is not in mongo, is to override the UI.each element and adding sorting there. But I'm quite new with Meteor, and do not really know how UI.each works and how to override it to implement that sorting method.
Below a simplified example from my code:
model
class @BaseCrypto
constructor: (@address) ->
@keys =
balance: "Processing..."
@deps = {}
ensureDeps: (key) ->
if not @deps[key]
@deps[key] = new Deps.Dependency()
@set_balance()
get_balance: ->
"""Retrieve value set from @set_balance()"""
@ensureDeps "balance"
@deps.balance.depend()
return @keys.balance
set_balance: (url, lambda_balance) ->
cls = this
Meteor.call "call_url", url, (err, result) ->
if err
throw new Meteor.Error err.error, err.reason
else
cls.keys.balance = lambda_balance result
cls.deps.balance.changed()
collection
@Addresses = new Meteor.Collection "addresses",
transform: (doc) ->
doc = BaseCrypto doc.address
doc.set_balance url, lambda_balance
return doc
helper
Template.coinsManager.helpers
donationAddresses: ->
Addresses.find {}
template
template(name="coinsManager")
div
div.addresses
{{#each donationAddresses}}
{{> addressItem}}
{{/each}}
How can I get {{#each}}
to sort my addresses depending on their method get_balance() ?
We can do a fetch()
on the collection query in the template to retrieve transformed elements. How do you use observe() in this case ? Because in this case, reactivity is lost and addresses not updated.
before:
donationAddresses: ->
coinsManager = Meteor.users.findOne
"emails.address": "[email protected]"
if coinsManager
Addresses.find
userId: coinsManager._id
after:
donationAddresses: ->
coinsManager = Meteor.users.findOne
"emails.address": "[email protected]"
if coinsManager
addresses = Addresses.find
userId: coinsManager._id
addresses = addresses.fetch().sort (a, b) ->
a = a.get_balance()
b = b.get_balance()
if not _.isNumber a
a = -1
if not _.isNumber b
b = -1
b - a
return addresses
Upvotes: 2
Views: 942
Reputation: 151958
Minimongo doesn't support sorting on virtual fields., so Addresses.find({...}, {sort: {balanceVirtualField: 1}}
wont' work.
Can you fetch()
the collection's find()
result and sort the array? To preserve reactivity, you can observe()
it and recreate the array. Slow but might be a stop-gap measure until Meteor implements that feature.
Upvotes: 1
Reputation: 8865
The following code will sync a source collection (myCollection
) in to a local collection (myLocalCollection
), and apply transforms on new / updated documents.
myCollection = new Meteor.Collection('myCollection')
myLocalCollection = new Meteor.Collection(null)
_myTransform = (doc)->
doc.awesome = (doc.someProp > 3)
return
_syncWithTransform = (destination, xform)->
return {
added: (doc)->
destination.insert(xform(doc))
return
changed: (doc)->
destination.update(id, xform(doc))
return
removed: (doc)->
destination.remove(doc._id)
return
}
myCollection.find().observe(_syncWithTransform(myLocalCollection, _myTransform))
If you only want to perform your transform task when specific fields change, you can create two transform functions, and use observeChanges
to check if a specific field was updated-
myCollection = new Meteor.Collection('myCollection')
myLocalCollection = new Meteor.Collection(null)
_transformNew = (doc)->
doc.awesome = (doc.someProp > 3)
return
_transformUpdate = ($modifier)->
if $modifier.$set?.someProp?
$modifier.$set.awesome = ($modifier.$set.someProp > 3)
return $modifier
_syncWithTransform = (destination, xnew, xmod)->
return {
added: (id, fields)->
fields._id = id
destination.insert(xnew(fields))
return
changed: (id, fields)->
$modifier = {}
for key, value of fields
if value == undefined
unless $modifier.$unset?
$modifier.$unset = {}
$modifier.$unset[key] = true
else
unless $modifier.$set?
$modifier.$set = {}
$modifier.$set[key] = value
destination.update(id, xmod($modifier))
return
removed: (id)->
destination.remove(id)
return
}
myCollection.find().observeChanges(_syncWithTransform(myLocalCollection, _transformNew, _transformUpdate()))
Once you have the separate collection filled with the transformed documents - you can do regular reactive & sorted queries, eg. myLocalCollection.find({},{sort:['a','b','c']})
Upvotes: 1
Reputation: 1894
I finally got it working!
And here is the code:
So basically,
Not sure how I managed to handle the reactivity without observe or observeHandler... but it works. Probably the magic is handled from Iron router to keep the data reactive :)
Upvotes: 0