Question

Working on my social app I've found a strange behavior in the collection Meteor.users, this problem does not occur with other Collections using the same methodologies

I would like to have an initial list of users downloading a minimum number of information for everyone and when I open the panel to a specific user I subscribe a different showing more information if the specified user is a friend of mine.

But after subscribe the client collection Meteor.users is not updated!

CLIENT

Meteor.startup(function() {

    Meteor.subscribe('usersByIds', Meteor.user().profile.friends, function() {

        //... make users list panel using minimal fields

    });

    //performed when click on a user
    function userLoadInfo(userId) {

        Meteor.subscribe('userById', userId, function() {

            var userProfile = Meteor.users.findOne(userId).profile;

            //...
            //make template user panel using full or minimal user fields
            //...

            //BUT NOT WORK!

            //HERE Meteor.users.findOne(userId) keep minial user fields!!
            //then if userId is my friend!

        });         
    }
});

SERVER

//return minimal user fields
getUsersByIds = function(usersIds) {

    return Meteor.users.find({_id: {$in: usersIds} },
                            {
                                fields: {
                                    'profile.username':1,
                                    'profile.avatar_url':1
                                }
                            });
};

//return all user fields
getFriendById = function(userId) {

    return Meteor.users.find({_id: userId},
                            {
                                fields: {
                                    'profile.username':1,
                                    'profile.avatar_url':1
                                    //ADDITIONAL FIELDS                         
                                    'profile.online':1,
                                    'profile.favorites':1,
                                    'profile.friends':1
                                }
                            });
};

//Publish all users, with minimal fields
Meteor.publish('usersByIds', function(userId) {

    if(!this.userId) return null;

    return getUsersByIds( [userId] );
});

//Publish user, IF IS FRIEND full fields
Meteor.publish('userById', function(userId) {

    if(!this.userId) return null;

    var userCur = getFriendById(userId),
        userProfile = userCur.fetch()[0].profile;

    if(userProfile.friends.indexOf(this.userId) != -1)  //I'm in his friends list
    {
        console.log('userdById IS FRIEND');
        return userCur;     //all fields
    }
    else
        return getUsersByIds( [userId] );   //minimal fields
});
Était-ce utile?

La solution

This is a limitation or bug in DDP. See this.

A workaround is to move data out of users.profile.

Like this:

//limited publish
Meteor.publish( 'basicData', function( reqId ){
  if ( this.userId ) {      

    return Meteor.users.find({_id: reqId },{
      fields: { 'profile.username':1,'profile.avatar_url':1}
    });
  } 
  else {
    this.ready();
  }
});

//friend Publish
Meteor.publish( 'friendData', function( reqId ){
  if ( this.userId ) {

    return Meteor.users.find( {_id: reqId, 'friendProfile.friends': this.userId }, {
      fields: {
        'friendProfile.online':1, 
        'friendProfile.favorites':1,
        'friendProfile.friends':1
      }
    });
  } 
  else {
    this.ready();
  }
});

//example user
var someUser = {
   _id: "abcd",
   profile: {
     username: "abcd",
     avatar_url: "http://pic.jpg"
   },
   friendProfile: {
     friends: ['bcde', 'cdef' ],
     online: true,
     favorites: ['stuff', 'otherStuff' ]
   }
}

Autres conseils

As given in a comment, this link reveals your problem. The current DDP Protocol does not allow publishing of subdocuments. One way to get around this is to create a separate collection with your data but a better way would probably to just remove some of the data and make it a direct object off of your user.

The best way to do this is add the data to your user's profile upon insert and then in the onCreateUser move the data onto the user directly:

Accounts.onCreateUser(function(options, user) {
    if (options.profile) {
        if (options.profile.publicData) {
            user.publicData = options.profile.publicData;
            delete options.profile.publicData;
        }
        user.profile = options.profile;
    }
    return user;
});

If you are allowing clients to perform user inserts make sure you validate the data better though. This way you can have the online, favorites, and friends in the profile and publish that specifically when you want it. You can then have username and avatar_url in the publicData object directly on the user and just always publish all-the-time.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top