6

I'm using the profile field in Meteor.users system defined collection to store information about the last message user read on every communication channel with the following structure:

profile : {
    lastMsgRead : [
        {channelId: 'YYEBNEI7894K', messageCreatedAt: '14578970667"}
        {channelId: 'GERYUE789774', messageCreatedAt: '14578999845"}
    ]
}

I discovered that reading lastMsgRead field fails because of on the client the array in still empty at the time of read. I correctly publish this field to the client through:

Meteor.publish(null, function() {
    return Meteor.users.find({}, {fields: {_id: 1, username: 1, emails: 1, profile :1}});

and I read its value from a client library located in the lib directory, in this way:

var chHistory = Meteor.user().profile.lastMsgRead;

Debugging my code it looks like the changes I make to the profile field have not been propagated to all the client at the time I read them. So I need to wait for the subscription Meteor.users gets ready, but I don't have its handle ─ you get this automatically from the framework.

How can I wait for the Meteor.users subscription gets ready?

massimosgrelli
  • 121
  • 1
  • 7
  • Would an answer addressing your specific issue (making sure the sub-field is populated before doing work with it) fit you? I'm not sure your current question (making sure an unnamed subscription is ready) is answerable with anything else than "Meteor does not currently provide such a feature". – Kyll May 17 '15 at 12:37

4 Answers4

6

Because meteor doesn't provide you with a handle for the current user's subscription, there isn't an obvious way to wait on the data. Here are some options:

use a guard

The typical way to handle this problem is to add guards to your code. In your template(s) which are experiencing this issue you could write something like:

var user = Meteor.user();
var lastMsgRead = user && user.profile && user.profile.lastMsgRead;

If you find you write that code a lot, you could factor it out into a shared function:

var extractProfileValue = function(key) {
  var user = Meteor.user();
  return user && user.profile && user.profile[key];
};

And use it like this:

var lastMsgRead = extractProfileValue('lastMsgRead');

show a spinner

You can test for the existence of the user's profile in the template itself:

<template name='myTemplate'>
  {{#unless currentUser.profile}}
    // show spinner or loading template here
  {{else}}
    // rest of template here
  {{/unles}}
</template>

If you want this experience on all of your pages, you could add it to your layout template(s).

redundant publisher

WARNING: I have not tried this

One way to get a user's subscription handle is just to add a redundant publisher and subscribe to it:

Meteor.publish('myProfile', function() {
  return Meteor.users.find(this.userId, {fields: {profile: 1}});
});

Then in your router:

waitOn: function () {
  return Meteor.subscribe('myProfile');
}
David Weldon
  • 63,632
  • 11
  • 148
  • 146
  • Thanks! I'm trying the **redundant publisher** option, but I have a couple of questions for you: (a) wouldn't be better to avoid the profile field of users collection? I've heard its behavior it's going to be changed soon, so maybe a new Profiles collection would be better; (b) I need to be sure the collection is synchronized in a library (/lib directory). Can I call `handle = Meteor.subscribe('myProfile');` in that library and then check for `handle.ready()` before suing `Profiles.find()`? – massimosgrelli May 18 '15 at 14:10
  • (a) I don't have any inside knowledge on this. I'm guessing that near term something could happen where the permission rules are modified for `profile` (right now it can be updated on the client by default but probably shouldn't be). I doubt MDG would drop the field altogether anytime soon. (b) Yes, it's a normal subscription so I don't see any reason why that wouldn't work. – David Weldon May 18 '15 at 16:19
0

You publication exposes too much unnecessary data. You should only publish want you need. Meteor.user() returns a user object if a user is logged in. In your null publication you filter using this.userId. You can wait on this publication at template level or using Iron Router.

Mário
  • 1,603
  • 14
  • 24
0

Since you are using a null or "automatic" publication, there is one really simple fix for this issue and that's to add meteorhacks:fast-render to your application. It will send down all automatically published data with the initial html of the app so that it is available immediately upon page load.

Kelly Copley
  • 2,990
  • 19
  • 25
-1

You need to read http://docs.meteor.com/#/full/meteor_user again.

On the client, this will be the subset of the fields in the document that are published from the server (other fields won't be available on the client). By default the server publishes username, emails, and profile (writable by user). SeeMeteor.users for more on the fields used in user documents.

You can add a helper which will return the data.

I would also remove your publication as it is already taken care of by the framework.

Meteor.user().profile.blah

Felix
  • 341
  • 2
  • 10
  • It won't publish all the fields, the remaining fields you have to publish them yourself. – Mário May 17 '15 at 14:19
  • If you have Meteor.user().profile.fullname, it gets published. Check it out before down voting – Felix May 17 '15 at 14:23
  • Some fields are published and other don't. If you add your own fields you need to publish them. – Mário May 17 '15 at 14:55
  • Your example also throws an error when the user object isn't available, correct version: Meteor.user() && Meteor.user().profile && Meteor.user().profile.blah – Mário May 17 '15 at 14:57