流星:如何在模板中对结果进行分组?

Meteor: how to group results in template?

我有一个应用程序,用户可以在不同的日期时间签到。然后我的目标是显示谁在不同的日期在那里。

类似的东西:

11/05 : Mat, Lisa, Andrew

14/05 : Lisa

etc.

我看了一下 $groupby 聚合,但对于我的简单用法来说它似乎真的很重。 我也可以在 db.find().fetch() 上循环,但我觉得我会失去反应性。

我设法让它与一个简单的模板助手一起工作:

resultDate = null;

Template.result.helpers({
    date: function(){
        var date = moment(this.datetime).endOf("day").fromNow();

        if(date == resultDate)
            return false;

        resultDate = date;

        return date;
    }
}

但如您所见,这并不是真正的经典方式,例如,如果我需要调用 {{date}} 两次,一切都会崩溃。

我已经寻找了几个小时的合适解决方案,但一无所获。有什么想法吗?

谢谢!

下面是一个示例,说明如何在不丢失反应性的情况下使用您的集合执行此客户端。

关键问题是将反应式数据源(集合)转换为对象数组,这些对象反映了所需的日期输出以及签到人员的姓名。 这个数组看起来像:

[
  {date: '2015-05-11', names: ['Mat', 'Lisa', 'Andrew']},
  {date: '2015-05-14', names: ['Lisa']},
]

我假设您的集合称为 Checkin,每个文档中有 2 个字段,datename

您的集合是一个反应性数据源。您可以通过 observeChanges 获得有关集合更改的通知。 通过触发添加、更改和删除事件,您可以通过每天签到来重建阵列。这些事件都调用 rebuildCheckinPerDay 来构建实际的数组。 每次重建时,数组都会保存在 ReactiveVar checkinPerDateVar 中。而且因为 ReactiveVar 也是一个反应式数据源,模板助手 checkinPerDate 每次更改 reactiveVar 时都会得到 re运行。

构建数组是由几个underscorejs 函数完成的。首先,集合按日期排序并在数组中获取,然后按日期分组(时间被 .endOf('day') 删除),然后映射到最终结构。因为 _.groupBy returns 项作为 {id, name, date} 你必须使用 _.pluck 来提取名称。

为了转换日期和方便的模板助手来格式化日期,我使用了 momentjs。实际上 运行 这个例子你必须包含包 momentjs:momentreactive-var。 在示例中显示了完整的日期。但如果您愿意,您可以轻松地将其转换为 moment(this.datetime).endOf("day").fromNow()

完整示例:

检查-in.html.

<template name="checkinPerDate">
    {{#each checkinPerDate}}
        <div>
            {{formatDate date}} - {{names}}
        </div>
    {{/each}}
</template>

检查-in.js.

Template.registerHelper('formatDate', function(date) {
    return moment(date).format('DD-MM-YYYY');
});

// Reactive variable to hold the array with names per check-in day
var checkinPerDateVar = new ReactiveVar([]);

// Build the array with check-ins per day that mirror the final view in the template
var rebuildCheckinPerDay = function() {
    var checkinPerDateArray = _.map(
        _.groupBy(Checkin.find({}, {sort: {date : 1}}).fetch(), function(item) {
            return moment(item.date).endOf('day');
        }), function(dateItem, date) {
            return {date: date, names: _.pluck(dateItem, 'name')};
        });
    checkinPerDateVar.set(checkinPerDateArray);
}

Template.checkinPerDate.onCreated(function() {
    this.subscribe('checkin', function() {
        var cursor = Checkin.find();
        var handle = cursor.observeChanges({
            added: function (id, checkin) {
                console.log('added ', id, JSON.stringify(checkin));
                rebuildCheckinPerDay();
            },
            changed: function (id, checkin) {
                console.log('changed ', id, JSON.stringify(checkin));
                rebuildCheckinPerDay();
            },
            removed: function (id) {
                console.log('removed ', id);
                rebuildCheckinPerDay();
            }
        });
    });
});

Template.checkinPerDate.helpers({
    // This helper function is reactive because of the use of reactive variable checkinPerDateVar.
    // It gets rerun each time the variable was changed
    checkinPerDate: function () {
        var checkinPerDateArray = checkinPerDateVar.get();
        return checkinPerDateArray;
    }
});

谢谢 Jos 的回答,但对于我来说,它看起来仍然很沉重,因为我的简单需求与 HTML 格式化比对象操作更相关。我认为您的回答非常适合对请求进行复杂处理,但就我而言,我只需要显示日期排序记录的日期,而无需在同一天重复。

所以,我制定了我最初的辅助函数,我想我找到了更好的方法来做到这一点:

var resultDateVar = [];

Template.result.helpers({
    date: function(){
        if(!resultDateVar[this._id]){
            var date = moment(this.datetime).endOf("day").fromNow();

            resultDateVar[this._id] = (resultDateVar.last == date)? false : date;
            resultDateVar.last = date;
        }

        return resultDateVar[this._id]
    }
});

希望对您有所帮助:)