How to design my subscriptions?

Hi!

I have an app with events. Users can add theirself to an event to participate to it. I can make the subscriptions to work…
For the moment, the first subscription subscribe to all events that the current user participate. But I have a problem when the user add himself to a new event. It is not showing unless the user refresh the page (and the all sbscription rerun)…

So I look for the best practices for subscriptions. Now I subscribe to the event when it is display but when I switch to another event it do not subscribe…

It’s like the first subscription is never overrided. What is the best practice for subscriptions when the data to subscribe can change for every page?

Thanks.
Sorry I have problem to be very clear with my problem (surely because I don’t understant it) but I will answer all question to be more precise.

Hi. I too am working on an app where users can join different events. I have multiple different supscriptions depending on what the user wants to see. Sometimes he wants to see which events he’s joined, other times he might want to se all events nearby, or which events his friends is participating in.

Inside my “events”-collection I have a field which is an array called participants.
I’ve listed out 3 of the fields in a document in my collection, creatorId, creatorName and the participants field (which is what we think is interesting here)

“createdBy”: “maqMe7E75T8i25QgN”,
“creatorName”: “test3”,
“participants”: [
{
“userId”: “bx6MuZHgdotH3nJzn”,
“hasRide”: true,
“tickets”: 1
},
{
“userId”: “REH42ye7XpptZJNXC”,
“hasRide”: true,
“tickets”: 1
},
{
“userId”: “numegLhk5HSjDdwCx”,
“hasRide”: true,
“tickets”: 1
}
],
Each user gets a position in the array and has 3 fields.

  1. userId (duh)
  2. if the user has a ride or not (app is targeted to old people who may struggle to get to somewhere)
  3. tickets (if they’re bringing friends that does not have an account they can add up to 5 extra tickets)

so… in this use case we have 3 users that has joined the specific event. the subsription code I run to let the users see every event they have joined is as following:

Meteor.publish('joinedEvents', function (per_page) {
    check(per_page, Number);
    if (!this.userId) {
        return this.ready();
    }
    let options = {sort: {datetime: 1}, limit: per_page};
    return Events.find({'participants.userId': this.userId},
        options
    );
});

This works for me… There might be a better way (in which case I’d love to hear it).

What’s key to know if you’re new to subscriptions is that if you try to join on other collections inside the subscription it’ll stop being reactive.
I believe the following article should still be relevant (please correct me if I’m wrong)
Reactive Joins in Meteor

I’ve needed reactive joins a couple of times and I’ve ended up using the Publish Composite package, although this may be a good deal ‘heavier’ than normal subscriptions.

If you post a code snippet of your subscription that could may help people see what you’ve done and if there is a better way.

Please bear in mind that I’m no expert. I’ve still got a lot to learn so take what I say with a grain of salt :wink:

EDIT: I forgot to mention… As you can see, my subscription fetches from the ‘events’ collection. This may not be the best option if there’s hundreds of thousands of events which each has thousands upon thousands of participants (unlikely in my case)

It may be a good idea to also store the eventIds inside the user-document so that you know exactly which events you’re going to fetch. If the user already has the id’s of the events he’s attending you could send a list of these to the subscription and just fetch the events with ID corresponding to what it received. Note that this may result in a bigger ‘cleanup-job’ so if you go with this method, ensure you clean up your fields in your different collections if you were to delete an event or a user…

You get the client to re-subscribe by wrapping the subscription inside an autorun and using a dependent reactive variable. Check the Meteor Guide here. In your case, you could make the event a reactive variable which you pass as a parameter to the subscription. Put that inside an autorun and it will re-subscribe whenever the event changes.

1 Like

Thanks both of you!

@robfallows I am using the autorun. I will post part of my code as @Kolbjorn said.

In my router.js:

Router.route('/event/:_ID', {
    name: 'event.display',
    template: 'event',
    data: function () {
      if (this.ready()) {
        return Events.findOne();
      }
    }
});

And in my eventTemplate:

Template.event.onCreated(function() {
  this.getEventId = () => Router.current().params._ID;
  this.autorun(() => {
    this.subscribe( 'events.one', this.getEventId() );
    this.subscribe( 'roles.all' );
    this.subscribe( 'users.all', this.getEventId() );
  });
});

Looking at my code, I’m wondering if the data router will work :thinking:

I think you’ve made a reactive variable (Router.current().params._ID) behave in a non-reactive way by hiding it in another function. Maybe try this:

Template.event.onCreated(function() {
  this.autorun(() => {
    this.getEventId = () => Router.current().params._ID;
    this.subscribe( 'events.one', this.getEventId() );
    this.subscribe( 'roles.all' );
    this.subscribe( 'users.all', this.getEventId() );
  });
});

or this (which is more understandable, imo):

Template.event.onCreated(function() {
  this.autorun(() => {
    const eventId = Router.current().params._ID;
    this.subscribe( 'events.one', eventId );
    this.subscribe( 'roles.all' );
    this.subscribe( 'users.all', eventId );
  });
});
3 Likes

Seems to works!!
I say seems because I made a lot of changes to others publications. I will clean all the subscriptions to make them in the template where it belongs and I will confirm that your solution is good!!

Thanks! Wait and see!

Just wondering something…

I have a general layout for all the pages. In this layout I have a menu that contains links to all the events available for the user. My question is:

  • Do I need to resubscribe to an event if the layout already subscribe to all events?

Then I assume that the events are subscribed… ? :thinking:

Or it’s best to subscribe to the data the template need even if an other template in the same page already subscribed to it?

And a last last question:

If I put the subscription in template, can I let the data recuperation in the route or do I have to move it to the template too?

Can I let that if I put the subscription in the template:

Router.route('/event/:_ID', {
    name: 'event.display',
    template: 'event',
    // waitOn: function () {
    //   moved to the template
    },
    data: function () {
      if (this.ready()) {
        return Events.findOne({  "_id": this.params._ID });
      }
    }
});

Thanks for your help, both :slight_smile:

Really good questions!

I would say that for code readability and modularity, subscriptions should sit where they’re used, even if that means “duplicating” them in multiple templates - and even if that includes parent templates.

I don’t understand what you’re asking.

1 Like

Thanks for your reply!

For the data, I mean in the router, I used to have the WaitOn (for subscriptions) and the data function (to send the data to the template). If I move the subscription part to the template, do I need to move the data (the function that send the data to be treated by the template) to the template ?

CF my last comment with the code…

Thanks

Ah - you’re using Iron Router? I’ve not used that - too many moving parts. I would put the subscription in the template.

1 Like

Use Flow Router. I made the painful switch a while ago, I much prefer it.

Regarding your data subscriptions, I followed the “Wordpress Way”. I have 1 large collection that has different publications moving in and out of it.

So rather than 15 collections with 5 datas, I have 1 collection with 75 datas.

Hi!

:thinking: I didn’t understand the Wordpress Way you describe…

Are you familiar with Wordpress? Their setup is really nice.

EVERYTHING is supposed to be a Post, or PostMeta.

In my case I did away with PostMeta, so I ended up with

Post -> {type: player_ship, parent_id:(some id from mongo)}

The nice thing about Wordpress is it gives that scaffold to get started. Where as Meteor… you gota build it.