2014-02-21 5 views
2

У меня есть коллекция, которая использует преобразование для создания экземпляров документов из классов. Затем эти экземпляры устанавливают новые атрибуты, данные извлекаются из 3-й части apis и становятся реактивными.Сортировка коллекции после преобразования

Теперь мне нужно отсортировать эти объекты на основе метода, который извлекает реактивные данные. Но я не могу выполнить сборку find.sort или использовать hook-hook, потому что он работает с документом перед его преобразованием, поэтому метод недоступен.

Поэтому мне кажется, что единственный способ сортировать эту коллекцию на основе этих данных, которая не находится в монго, - это переопределить элемент UI.each и добавить туда сортировку. Но я довольно недавно знаком с Meteor и не знаю, как работает UI.each и как переопределить его для реализации этого метода сортировки.

Ниже упрощенный пример из моего кода:

модели

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() 

коллекция

@Addresses = new Meteor.Collection "addresses", 
    transform: (doc) -> 
     doc = BaseCrypto doc.address 
     doc.set_balance url, lambda_balance 
    return doc 

хелперов

Template.coinsManager.helpers 
    donationAddresses: -> 
     Addresses.find {} 

шаблон

template(name="coinsManager") 

    div 
     div.addresses 
      {{#each donationAddresses}} 
      {{> addressItem}} 
      {{/each}} 

Как я могу получить {{#each}} сортировать свои адреса в зависимости от их метода get_balance()?

Редактировать

Мы можем сделать fetch() на запрос коллекции в шаблоне для получения преобразованных элементов. Как вы используете функцию watch() в этом случае? Потому что в этом случае реактивность теряется и адреса не обновляются.

Перед:

no sorting

donationAddresses: -> 
    coinsManager = Meteor.users.findOne 
    "emails.address": "[email protected]" 
    if coinsManager 
    Addresses.find 
     userId: coinsManager._id 

после: sorting

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 
+0

Не будучи слишком знакомым с API, можно каким-то образом разместить аргументы в вашем .find в помощнике. Таким образом, сортировка может происходить на стороне сервера, а не на том, что может быть беспорядочным видом на стороне клиента. Пока ваша модельная реализация доступна с сервера, который является –

+0

. Самая большая проблема, которую я вижу при таком подходе, заключается в том, что ваша функция преобразования выполняет вызов AJAX. Каждый из документов, возвращаемых курсором, получит свой собственный вызов AJAX, что очень неэффективно. Вы всегда можете «сортировать» в своем помощнике шаблонов, что может показаться неэффективным, но не таким, как проблема AJAX. – sbking

+0

Не могли бы вы предоставить дополнительную информацию о том, какие данные API сторонних разработчиков вы извлекаете? – sbking

ответ

0

Я, наконец, получил его!

success

А вот код:

code

Так в основном,

  • Я переместил инициализацию коллекции курсора из шаблона к маршрутизатору, и я return a отбирается (массив)
  • Я извлекаю эту коллекцию в моем помощнике шаблона и выполняю сортировку по массиву.
  • Я передаю отсортированный массив в UI.each.

Не знаю, как мне удалось справиться с реактивностью, не наблюдая и не наблюдая за Хэндлером ... но это работает. Вероятно, магия обрабатывается от Iron router, чтобы сохранить данные реактивными.

+1

Это должно быть реактивным - единственная проблема заключается в том, что, поскольку вы извлекаете весь набор результатов, изменение любого результата приведет к тому, что весь блок будет недействителен/повторно рендеринга. –

+0

Да, не удалось найти лучшего решения. Извлечение из шаблона, по-видимому, убивает реактивность. Мы будем очень признательны, если кто-то сможет сделать PR для CoinsManager с оптимизированной версией! – Fandekasp

+1

Метеор должен [осуществлять сортировку после преобразования в Minimongo] (https://github.com/meteor/meteor/issues/1852) <- GitHub issue –

1

Minimongo doesn't support sorting on virtual fields., так Addresses.find({...}, {sort: {balanceVirtualField: 1}} обыкновение»работа.

У вас есть fetch() коллекция find() Результаты и сортировать массив? Чтобы сохранить реактивность, вы можете восстановить его и воссоздать. Медленная, но может быть мерой остановки, пока Метеор не выполнит эту функцию.

+0

Вы правы, элементы в шаблоне были изменены. Обновлено мое сообщение, чтобы спросить, как использовать наблюдение в этом случае, поскольку потеря реактивности – Fandekasp

+0

Не удалось найти способ использования наблюдения на моем курсоре коллекции из данных маршрутизатора, и я не могу получить данные в моем помощнике шаблонов. Код может быть протестирован на https: // github.com/CoinsManager/CoinsManager/pull/41 – Fandekasp

1

Следующий код синхронизирует исходную коллекцию (myCollection) с локальной коллекцией (myLocalCollection) и применяет преобразования для новых/обновленных документов.

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)) 

Если вы хотите, чтобы выполнить ваши преобразования задачи при изменении конкретных полей, вы можете создать две функции преобразования и использовать observeChanges, чтобы проверить, если определенное поле было 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())) 

После того как вы отдельная коллекция, заполненная преобразованными документами - вы можете делать обычные реактивные & отсортированные запросы, например. myLocalCollection.find({},{sort:['a','b','c']})

+0

Звучит как хорошее решение :) Будет проверять его. Почему мы используем преобразование через наблюдение, а не нормальное преобразование в коллекции? – Fandekasp

+0

Извините, но это не делает то, что я хочу. Я хочу, чтобы моя коллекция была преобразована, а затем отсортирована по новому аргументу, добавленному в преобразование, и оставаться реактивной. ваше решение по-прежнему наблюдает за оригинальной коллекцией, в которой docs не имеют методов get_balance и get_value. Код можно протестировать на странице https://github.com/CoinsManager/CoinsManager/pull/41 (cf https://www.youtube.com/watch?v=XxjvJjltHYo для установки и запуска приложения) – Fandekasp

+1

Вы должны использовать зеркальное отображение сборник из моего примера в ваших помощниках шаблонов ('myLocalCollection') - потому что вы зеркалировали исходную коллекцию, но с применяемыми преобразованиями у него будут все поля, которые нужно сортировать по и т. д. и по-прежнему должны быть связаны с изменениями в исходные данные. - но свойства должны быть VALUES, а не функции. –

Смежные вопросы