Meteor Publish-Composite nesting issue

416 Views Asked by At

Problem: I have a number of groups that each have members that belong to different groups. Each member has a title (role) in each group. I’m trying to list all the groups and display each member in the group and their title. I’m using reywood:publish-composite, and everything is working except I can’t get the title of each member to display. I think the problem is in the Template.groupMembers.helpers file

 title: function() {
        console.log(this.roleId); // this shows up in the console for each member
        return Titles.findOne({titleId: this.roleId}); // but this doesn’t work 
      },

Collections:

groups {
    "_id" : "xFSzAHBEps2dSKcWM",
    "name" : "Generic Group",
    "logo" : "generic-logo-hi.png"
}


members {
    "_id" : "vyDtiaKKukZYQdFvs",
    "groupId" : "xFSzAHBEps2dSKcWM",
    "memberId" : "hRx8GBTyB5X8iQQ52",
    "roleId" : "1"
}


Meteor.users {
    "_id" : "hRx8GBTyB5X8iQQ52",
    "profile" : {
        "name" : "Bob Lorros"
    },
 }


titles {
    "_id" : "bYsKpsyYtyKR8NYpm",
    "titleId" : 1,
    "title" : "Staff (non-voting)"
}

server/publications/publications.js

Meteor.publishComposite('groupMembers', {
  find: function() {
      return Groups.find({}, {
        sort: {name: 1}
      });
    },
    children: [
        {
            find: function() {
                return Titles.find();
            },
            find: function(group) {
              return Members.find({groupId: group._id});
            },
            children: [
              {
                find: function(member) {
                    return Meteor.users.find({_id: member.memberId});
                }
              },
            ]
        },
    ]
});

client/templates/test/test.js

Template.groupMembers.helpers({
  groupMembers: function() {
    return Groups.find({}, {
      sort: {name: 1}
    });
  },
  members: function() {
    return Members.find({groupId: this._id});
  },
  title: function() {
    console.log(this.roleId); // this shows up in the console for each member
    return Titles.findOne({titleId: this.roleId}); // but this doesn’t work 
  },
  memberName: function() {
    return Meteor.users.findOne(this.memberId);
  },
});

client/templates/test/test.html

<template name="groupMembers">
  <h4>Group - Members</h4>
  {{#each groupMembers}}
    <b>{{name}}</b><br>
    {{#each members}}
       &nbsp;{{memberName.profile.name}}
        - title = {{title.title}}
        <br>
    {{/each}}
    <br>
  {{/each}}
</template>

Output : This is the ouput

3

There are 3 best solutions below

1
On BEST ANSWER

Looking at this from a completely different perspective, I actually think you could use alanning:roles to accomplish exactly what you're looking for. You can use the role as the 'title' in this case and the 'group' to replace your groups. Here's the documentation:

https://github.com/alanning/meteor-roles

1
On

Not sure but I think your second find may be overriding your first. Instead of:

find: function() {
  return Titles.find();
},
find: function(group) {
  return Members.find({groupId: group._id});
},

Try returning an array of cursors.

find: function() {
  return [
    Titles.find(),
    Members.find({groupId: group._id})
  ];
},

I don't understand however why Titles is a child of GroupMembers when the query for titles is all titles. Did you mean to have a query there?

0
On

I think your publishComposite is causing the problem, each object in the children array should have only one find and zero or more children. Also the second parameter in your publication must be a function and not a JSON object. Try this,

 Meteor.publishComposite('groupMembers', function () {
     return {
        find: function() {
            return Groups.find({}, {
               sort: {name: 1}
            });
        },
        children: [{
           find: function() {
              return Titles.find();
           }
        },
        {
           find: function(group) {
              return Members.find({groupId: group._id});
           },
           children: [{
              find: function(member) {
                 return Meteor.users.find({_id: member.memberId});
              }
           }]
        }]
     };
 });

You can also improve performance by moving Titles.find to the root level

    Meteor.publishComposite('groupMembers', function () {
     return [{
        find: function() {
           return Titles.find();
        }
     }, {
        find: function() {
            return Groups.find({}, {
               sort: {name: 1}
            });
        },
        children: [{
           find: function(group) {
              return Members.find({groupId: group._id});
           },
           children: [{
              find: function(member) {
                 return Meteor.users.find({_id: member.memberId});
              }
           }]
        }]
     }];
 });