Dopdownlist is being populated with limited values

2019-03-05 11:49发布

问题:

I have a dropdownlist as filters and then there's the total posts with a limit of displaying only 4 at a time unless a load more button is clicked to show another 4 records. problem is the dropdownlist is also loading values only from that first 4 records.

Here is my publication

Meteor.publish('allJobs', function(limit){
 if(!limit){
 return Jobs.find();
}
 return Jobs.find({}, {limit: limit});
});

my main subscription in the controller

  waitOn: function() {
  return Meteor.subscribe("allJobs",4,this.findOptions());
  },

helpers for the template

Template.jobsList.helpers({
'allJobs': function(){
  var filter ={};
  var category = Template.instance().selectedCategory.get();
  var city = Template.instance().selectedCity.get();
  var jtype = Template.instance().selectedJobType.get();
  if(jtype)
    filter.jtype = jtype;
  if(city)
    filter.city = city;
  if(category)
    filter.ccategory = category;
  return Jobs.find(filter);
     },
  'moreResults': function(){
    return !(Jobs.find().count() < Session.get("jobsLimit"));
  }
});

Template.jobsList.onRendered(function(){
    Session.setDefault("jobsLimit", 4);
    this.autorun(function(){
        Meteor.subscribe("allJobs", Session.get("jobsLimit"));
    });
});

i tried to make another subscription for this but it didn't work out. Please help with a best solution.

Template

<template name="jobsList">
  <div class="col s12 filter-holder">
        <div class="col m4 s4 filter-box">
          {{> categoryFilter}}
        </div>
        <div class="col m4 s4 filter-box">
         {{> cityFilter}}
        </div>
</div>

<div class="col s12">
  <ul class="collection" id="listings">
    {{#each allJobs}}
    <li>
      {{> jobItem}}
   </li>
   {{/each}}
  </ul>
  {{#if moreResults}}
  <button class="load-more" id="showMoreResults" type="submit">Load More
    <i class="mdi-content-send right"></i>
  </button>
  {{/if}}
</div>
</template>

Please help i am stuck in this situation

Updated js file

var limit = new ReactiveVar;

var filterAndLimitResults = function (cursor, limit) {

if (!cursor) {
    return [];
}

var chosenCategory = limit.get("chosenCategory");

var raw = cursor.fetch();

// filter category
var filtered = [];
if (!chosenCategory || chosenCategory == "") {
    filtered = raw;
} else {
    filtered = _.filter(raw, function (item) {
        return item.category === chosenCategory;
    });
}

if (limit) {
    filtered = _.first(filtered, limit);
}
return filtered;
};

//template created function

Template.jobsList.onCreated(function(){
limit.set(4);
limit.set("chosenCategory");
});

// Template helper function

Template.jobsList.helpers({
  "displayedJobs": function() {
  return filterAndLimitResults(Jobs.find(), (limit.get()));
   },
  'moreResults': function(){
    return !(Jobs.find().count() < limit.get());
      }
    });

Dropdown click filtering

Declaring in rendered function

  Template.jobsList.onRendered(function(){
  limit.set(4);
  chosenCategory.set();
});

And the click event is as follows

 "click .categoryselection": function(e, t){
        e.preventDefault();
      chosenCategory.set(chosenCategory.get());

回答1:

First, just to be sure I understand your situation, let me rephrase it: You have a collection of documents that you want to sort using categories (which are stored for each document in a dedicated field).

On one hand, you want to build a dropdown using all the possible categories in your collection documents in order to allow the user to select a category and filter the results.

On the other hand, you want to display only 4 items at a time. These 4 displayed items (and those who could be loaded after them) are the 4 first items matching the filtered category.

Your choice of displaying items 4 by 4 is only to avoid to put too much content in user interface

What I advise you is:

You don't need to limit the items you load using your publication. First, it will slow down your user interface (you have to request to the server each time), second you wont be able to achieve the filtering as you may have noticed.

Step by step:

  1. replace your publication with a simple return Jobs.find(); This is not the place where you need to do your filtering or enforce limit to the number of displayed items. It means that now, if you add a console.table(Job.find().fetch()); you will get all the available jobs for display. That will make the dropdown display all the categories you need, using the _.uniq I advised you to use in your precedent question
  2. You create a Session variable (or a reactive variable using ReactiveVar) to store your current limit. you initate it in your template rendered function, or even in the template created function: pageSession.set("limit", 4); and you modify it as you need in your "load more" button click event.

  3. You will create a function in your client code in order to enforce your rules (limit to displayed items number and categry filtering). Let's call it filterAndLimitResults. The helper that you use to return the displayed jobs will become something like this:

    "displayedJobs": function() { return filterAndLimitResults(Job.find()); }

  4. You don't need a "moreResults" helper. Get rid of that. You just create an event on click on the more results button where you update your ReactiveVar

    Template.jobsList.events({ "click #showMoreResults": function(e, t) { e.preventDefault(); limit.set(limit.get()+4); },

  5. You create a Session variable (or a reactive variable using ReactiveVar) in order to store your currently selected category. you initate it in your template rendered function, or even in the template created function: pageSession.set("chosenCategory", ""); and you modify it as you need in your dropdown items click event.

  6. You need now to write your filterAndLimitResults. Here is an untested example:

    var limit = new ReactiveVar;
    var chosenCategory= new ReactiveVar;
    var filterAndLimitResults = function (cursor) {
    
        if (!cursor) {
            return [];
        }
    
        var raw = cursor.fetch();
        var currentChosenCategory = chosenCategory.get();
    
        // filter category
        var filtered = [];
        if (!currentChosenCategory || currentChosenCategory == "") {
            filtered = raw;
        } else {
            filtered = _.filter(raw, function (item) {
                return item.category === currentChosenCategory ;
            });
        }    
    var currentLimit =limit.get();
    // enforce your limit
    if (currentLimit ) {
        filtered = _.first(filtered, currentLimit );
    }
    
    return filtered;
    };
    

And you should be good to go :-) You get your results almost instantly and they are reactive.

ps: following the same logic, you should have no trouble to filter by cities too and/or any other field.



回答2:

Short answer in reply to your comment. If you want to filter the collection you need to do it on the server AND the client. Otherwise as I said before your client database will only contain the first 4 natural order records - the filtering you are currently doing is only looking at those 4 records. You want to publish anything that matches the filter (up to the limit) so that this is available on the client. You filter again on the client to ensure you're getting the correct documents.

TL;DR:

Meteor.publish('allJobs', function(limit){
  if(!limit){
    return Jobs.find();
  }
  return Jobs.find({}, {limit: limit});
});

should be something like

Meteor.publish('allJobs', function(limit, filter){
  if(!filter) {
    filter = {};
  }
  if(!limit) {
    return Jobs.find(filter);
  }
  return Jobs.find(filter, {limit: limit});
});

Also make sure that this.findOptions() in the line;

Meteor.subscribe("allJobs",4,this.findOptions());

is actually returning the filter object otherwise you'll still have the same issue.