A History of Data Management in Meteor

It’s funny, the way we subscribe to data in Meteor has changed so much over time. From the beginnings with Iron Router and waitOn calls, to template level subscriptions and now with React and Angular in the mix, the confusion is even greater! We have tutorials doing this in every different flavor…and that is the PROBLEM.

What are subscriptions?

For us to work with data on the client, we need to subscribe to “publications”, which publishes this data over the DDP connection and dumped into a local cache/local database called Minimongo.

Let’s travel through time and let me show you all the different patterns we’ve used to achieve data flow in Meteor Apps.

waitOn, subscriptions: Iron Router — The Dark Ages

We at Workpop leveraged Iron Router heavily, like the many Meteor developers before us, to get our product to market. It wasn’t until our app hit a certain scale did we see the troubles with reactive routers and subscriptions in those routers.

Router.route('/post/:_id', {
// this template will be rendered until the subscriptions are ready
loadingTemplate: 'loading',

waitOn: function () {
// return one handle, a function, or an array
return Meteor.subscribe('post', this.params._id);
},

action: function () {
this.render('myTemplate');
}
});

This approach works great for small apps. Your data is relatively low. You can subscribe on the client and your view will be rendered once DDP signals to the client that the data is ready. So why is this bad?

Well, the problem with Meteor subscriptions is that they persist until stopped. And for most, it wasn’t very clear until their apps reached some scale that stopping subscriptions was necessary to prevent data overload on the client.

Aside from the lack of transparent stop functions baked into the router (seriously who remembered onStops for the routes), reactive parameters constantly would invalidate Route functions causing reruns of the route proving to be a very UNPREDICTABLE atmosphere for development.

That being said, your mileage may very. I did enjoy Iron Router when my app was smaller! And I’m sure you will too!

Blaze Template Level Subscriptions — The Silver Age

This also gave users a new method on Template instances called subscriptionsReady , a reactive function that returns true when all of the subscriptions made in the template come back as ready.

Template.exampleComponentContainer.onCreated(function () {
const template = this;
template.autorun(()=> {
template.subscribe(‘exampleSubscription’);
});
});
Template.exampleComponent.helpers({
exampleData() {
return ExampleData.findOne({_id: ‘1’}, {fields: {‘name’: 1}});
}
});
// HTML// Container Component
<template name=”exampleComponentContainer”>
{{#if Template.subscriptionsReady}}
{{> Template.contentBlock}}
{{else}}
{{> loading }}
{{/if}}
</template>
// Example Component
<template name=”exampleComponent”>
<h1>{{exampleData.name}}</h1>
</template>
// Page Component
<template name=”examplePage”>
{{#exampleComponentContainer}}
{{> exampleComponent}}
{{/exampleComponentContainer}}
</template>

See how nice that is? Definitely an improvement from an abstract routing layer. Now you can replace the word template with the component in your development dictionary and start approaching them as functional units of your UI.

Angular Controllers Subscriptions — The Golden Age

Angular Meteor ships with a $meteor service that allows you to take advantage of the reactivity of Meteor’s front end.

angular.module(‘simple-todos’).controller(‘TodosListCtrl’, [‘$scope’, ‘$meteor’, function ($scope, $meteor) {    $scope.$meteorSubscribe(‘tasks’); 
$scope.tasks = $meteor.collection(function() {
return Tasks.find($scope.getReactively(‘query’), {sort:{createdAt: -1}}) });
$scope.addTask = function (newTask) {
$meteor.call(‘addTask’, newTask);
};
};

You can see that we can subscribe via component $scope

Also a similar theme to Blaze’s template subscriptions, calling $scope.$meteorSubscribe will automatically stop the subscription when the scope is destroyed.

React getMeteorData — The Future

getMeteorData will reactively rerun when the accessed data changes. getMeteorData must return an object, and the properties of the object will be copied onto the component’s this.data

Subscriptions that you make from getMeteorData using Meteor.subscribe will be automatically maintained across reruns of getMeteorData, and cleaned up when the component unmounts.

If you’d like to write ES2015 component syntax and still use these mixins, get the react-mixin using browserify. See below:

// COMPONENT CONTAINERExampleContainer = class ExampleContainer extends React.Component {
constructor() {
super();
this.onBtnClick = this.onBtnClick.bind(this);
}
getMeteorData() {
const handle = Meteor.subscribe('exampleSubscription');
const isReady = handle.ready()
const exampleData = Example.findOne();
return {handle, isReady, exampleData};
}

onBtnClick() {
alert("You clicked me!");
}
render() {
if (!this.data.isReady} {
return <Loading/>;
}
return (
<ExampleComponent
onBtnClick={this.onBtnClick}
item={this.data.exampleData}/>
)
}
}
reactMixin(ExampleContainer.prototype, ReactMeteorData);// STATELESS COMPONENT FUNCTIONExampleComponent = ({name, description, onBtnClick}) => {
return (
<div>
<h1>{name}</h1>
<p>{description}</p>
<button onClick={onBtnClick}>Click Me!</button>
</div>
)
}

Conclusion

If you enjoyed this article, send me a recommend! Let’s chat this out further in the comments below!

--

--

Software Engineer at Workpop, Inc.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store