1

I have a Questions collection, which I have defined using SimpleSchema in a file "collections.js", which is in questions > lib.

This collection has an askedBy field. It is an array of Strings that stores the userIds of the people who asked the questions.

askedBy: {
  type: [String],
  optional: true
},

In another file, questions > server > publish.js, I have:

Meteor.publish("questions", function () {
  questionList = Questions.find(
    {},
    {
      fields: {
        "_id": 1,
        "questiontext": 1,
        "createdAt": 1,
        "askedBy": 1,
        "slug": 1
      }
    }
  );
  return questionList;
});

My objective is to make the names of the users who asked the question available, i.e. the user names of the users corresponding to the Ids in the 'askedBy' array.

How do I do this?

Things I have tried so far:

In collections.js:

Questions.helpers({
  askedByUsers: function() {
    console.log("askedBy:"); 
    console.log(Meteor.users.findOne(this.askedBy[0]));
    return Meteor.users.findOne(this.askedBy[0]);
  },

This fails because Meteor.users seems to only have access to the currently logged in user details. For all questions asked by other users, it returns a null value.

So then I tried adding this to publish.js:

Meteor.publish("allUsers", function () {
  return Meteor.users.find({},
  {
    "name": 1,
  });
});

and Meteor.subscribe("allUsers"); to both collections.js as well as questions.js (which have all my template helpers and event handlers). I also changed the collection helper to:

Questions.helpers({
  askedByUsers: function() {
    console.log("askedBy:"); 
    console.log(allUsers.findOne(this.askedBy[0]));
    return allUsers.findOne(this.askedBy[0]);
  },

But even that doesn't work.

What am I doing wrong, and how can I fetch the usernames of not-logged in users?

edit: just in case this is important - folder structure

├── client
├── modules
│   ├── admin
└── questions
    ├── client
    │   ├── questions.html
    │   └── questions.js
    ├── lib
    │   ├── collections.js
    │   └── methods.js
    └── server
        └── publish.js
...
Stephen Woods
  • 4,049
  • 1
  • 16
  • 27
Samudra
  • 1,013
  • 1
  • 14
  • 21

1 Answers1

3

The answer to this question has a lot of layers to it. Fundamentally, you'll want the users published for the question(s) being viewed.

One approach is to subscribe to all of the users. This won't scale, but it should give you a result. Let's fix the code above to make that work.

When you publish allUsers it will publish the user documents to Meteor.users on the client - not to a collection called allUsers. Also, I'm assuming askedByUsers should return an array or a cursor of users and not just a single user. You can rewrite your helper like this:

Questions.helpers({
  askedByUsers: function () {
    return Meteor.users.find({ _id: { $in: this.askedBy } });
  },
});

Then in a template you could do something like:

<template name='question'>
  <ul>
  {{#each askedByUsers}}
    <li>{{name}}</li>
  {{/each}}
  </ul>
</template>

It's good that you applied a filter to your allUsers publisher. I'm going to assume users actually have a name field, and it's not profile.name or something else. Please correct your code as necessary.

Once you get this working, check out my answer to this question to explore how to publish a join (reactive or otherwise) so you can avoid publishing all of the users to the client.

David Weldon
  • 63,632
  • 11
  • 148
  • 146
  • I tried this, but it brought me back to my original problem of being able to access only the user who is logged in, and not the other userIds in the array. Is the problem that I am defining this collection helper in the lib folder > collections.js? The publish.js is in the server folder, and the collections.js doesn't seem to be aware of the published 'allUsers'? How do I subscribe in collections.js to the published allUsers? – Samudra Feb 28 '16 at 21:03
  • 2
    Make sure the `Meteor.subscribe` call is somewhere under the `client` directory. Alternatively, you can replace `Meteor.publish("allUsers", function () {` with `Meteor.publish(null, function () {` and that will auto-subscribe for all your users without the need for a call to `Meteor.subscribe`. – David Weldon Feb 28 '16 at 21:08
  • That solved it! (I don't know when I had commented it out - that was stupid of me :) ) I am not sure I understood clearly what I was doing wrong earlier - could you help if I understood incorrectly? Right now, I am creating a new publication 'allUsers', and then subscribing to it on the client. This exposes a larger version of Meteor.users to my entire app? Then, when I call Meteor.users.find in my askedByUsers helper, it can access this entire extended set? I'm sure this is going to create problems later when I have real users - is this the best approach to do this? – Samudra Feb 28 '16 at 21:21
  • Or should I be taking a look more serious look at publish-composite? – Samudra Feb 28 '16 at 21:22
  • Btw, thanks a lot for your help! Truly appreciate it! – Samudra Feb 28 '16 at 21:22
  • Your summary is accurate. When you subscribe for some set of documents, the server transmits the documents to the client (which then get cached in minimongo). Once that happens, the client can access the documents locally. Now that you got something working, you should consider using another approach because publishing the names of all your user's won't scale (if you have a lot of users). Alternatives include a wide array of reactive and non-reactive solutions with different performance characteristics. For now, I'd go with publish-composite and circle back later if necessary. – David Weldon Feb 28 '16 at 21:31
  • Thank you very much for your help! Looking at publish-composite now. Have a great day! – Samudra Feb 28 '16 at 21:36