Meteor JS:使用相同订阅数据的子集

时间:2014-04-03 02:57:55

标签: javascript pagination meteor publish-subscribe

我构建了一个自定义分页脚本来显示我的应用程序的数据。它运作得很好。但是,在尝试找出如何获取相同分页订阅的子集时,我遇到了一个小问题。

Meteor.startup(function(){
  Session.setDefault('page', 1);
  Session.setDefault('recordCount', 0);
  Session.setDefault('recordsPerPage', 10);
  Session.setDefault('currentIndustry', null);
  Session.setDefault('currentMapArea', null);
  Session.setDefault('gmapLoaded', false);
});

Deps.autorun(function () {

  Meteor.call('getJobsCount', Session.get('currentIndustry'), Session.get('currentMapArea'), function (err, count) {
    Session.set('recordCount', count);
  });

  Meteor.subscribe('pagedRecords', Session.get('page'), Session.get('recordsPerPage'), Session.get('currentIndustry'), Session.get('currentMapArea'));

});


Template.gmap.rendered = function() {
  if(!Session.get('gmapLoaded'))
    gmaps.initialize();
}

var templateName = "jobs";

function plotCities(jobs) {
  var addresses = _.chain(jobs)
              .countBy('address')
              .pairs()
              .sortBy(function(j) {return -j[1];})
              .map(function(j) {return j[0];})
              .slice(0, 99)
              .value();

  gmaps.clearMap();
  $.each(_.uniq(addresses), function(k, v){
    var addr = v.split(', ');

    Meteor.call('getCity', addr[0].toUpperCase(), addr[1], function(error, city){
      if(city) {
        var opts = {};
        opts.lng = city.loc[1];
        opts.lat = city.loc[0];
        opts.population = city.pop;
        opts._id = city._id;
        gmaps.addMarker(opts);
      }
    });
  });
}

Template[templateName].helpers({
  selected: function(){
    return Session.get('recordsPerPage');
  }
});

Template[templateName].pages = function() {
  var numPages = Math.ceil(Session.get('recordCount') / Session.get('recordsPerPage'));
  var currentPage = Session.get('page');
  var totalPages = Session.get('recordCount');
  var prevPage = Number(currentPage) - 1;
  var nextPage = Number(currentPage) + 1;

  var html = '<div class="pagination-cont"><ul class="pagination">';
  if (numPages !== 1) {
    if (currentPage > 1) {
      html += '<li><a href="#" data-page="' + prevPage + '" class="pageNum">«</a></li>';
    }
    for (var i = currentPage; (i <= numPages) && (i - currentPage < 4); i++) {
      if (i < 1) continue;
      if (i !== currentPage)
        html += '<li><a href="#" data-page="' + i + '" class="pageNum">' + i + '</a></li>';
      else
        html += '<li class="active"><a href="#" data-page="' + i + '" class="pageNum">' + i + '</a></li>';
    }
    if (currentPage < numPages) {
      html += '<li><a href="#" data-page="' + nextPage + '" class="pageNum">»</a></li>';
    }
  }
  html += '</ul></div>';
  return html;
}

Template[templateName].jobs = function() {
  var options = {};
  var cursor;

  if(!Session.get('currentMapArea')) {
    cursor = Jobs.find({}, {limit: 500});
    plotCities(cursor.fetch());
  }

  return Jobs.find({}, { limit: Session.get('recordsPerPage') });
}

Template[templateName].rendered = function(){
  var select = $('#perPage');
  var option = select.attr('_val');
  $('option[value="' + option + '"]').attr("selected", "selected");

  select.selectpicker({
    style: 'btn-info col-md-4',
    menuStyle: 'dropdown-inverse'
  });
}

Template[templateName].events({
  'click div.select-block ul.dropdown-menu li': function(e){
    var selectedIndex = $(e.currentTarget).attr("rel");
    var val = $('select#perPage option:eq(' + selectedIndex + ')').attr('value');
    var oldVal = Session.get('recordsPerPage');

    if(val != oldVal)
      Session.set('recordsPerPage', Number(val));
  },
  'click .pageNum': function(e){
    e.preventDefault();
    var num = $(e.currentTarget).data('page');
    Session.set('page', Number(num));
  }
});

目前,默认情况下,每页只显示10条记录(除非用户从下拉菜单中选择不同的金额)。我有一个plotCities函数用于尝试绘制返回的子集中的前100个城市,但是,我无法获得前100名,因为一次只显示10个。

无论如何要做我所描述的事情吗?

1 个答案:

答案 0 :(得分:0)

好的,jobsPerCityjobs是两个完全不同的东西,所以我会为第一个使用单独的动态集合。数据库中不会存储任何内容,但客户端会“认为”实际上存在jobsPerCity集合,您可以使用该集合来绘制地图。您可以实现此目的的方法是定义另一个命名集合:

// only on the client !!!
var jobsPerCity = new Meteor.Collection("jobsPerCity");

在服务器上,您需要定义自定义publish方法:

Meteor.publish('jobsPerCity', function (options) {
  var self      = this;
  var cities    = new Meteor.Collection(null);
  var jobToCity = {};
  handle1 = Jobs.find({/* whatever condition you want */}).observeChanges({
    added: function (id, fields) {
      jobToCity[id] = fields.address.split(',')[0].toUpper();
      cities.upsert({ _id: jobToCity[id] }, { $inc: { jobsCount: 1 } });
    },
    removed: function (id) {
      cities.upsert({ _id: jobToCity[id] }, { $inc: { jobsCount: -1 } });
      delete jobToCity[id];
    },
    changed: function (id, fields) {
      // left as an exercise ;)
    },
  }); 
  handle2 = cities.find({}, {sort: {jobsCount: -1}, limit: 100}).observeChanges({
    added: function (id, fields) {
      self.added('jobsPerCity', id, fields);
    },
    changed: function (id, fields) {
      self.changed('jobsPerCity', id, fields);
    },
    removed: function (id) {
      self.removed('jobsPerCity', id);
    },
  });
  self.ready();
  self.onStop(function () { handle1.stop(); handle2.stop(); });
});

你的好处:)

EDIT(更多静态数据的简单解决方案)

如果数据不会经常更新(正如@dennismonsewicz在他的一条评论中所建议的那样),publish方法可以用更简单的方式实现:

Meteor.publish('jobsPerCity', function (options) {
  var self = this, jobsPerCity = {};

  Jobs.find({/* whatever condition you want */}).forEach(function (job) {
    var city = job.address.split(',')[0].toUpper();
    jobsPerCity[city] = jobsPerCity[city] !== undefined ? jobsPerCity[city] + 1 : 1;
  });

  _.each(jobsPerCity, function (jobsCount, city) {
    self.added('jobsPerCity', city, { jobsCount: jobsCount });
  });

  self.ready();
});