Using publish/subscribe reactively using an external API


#1

Hi All,

I relatively new to meteor so still getting to grips with all the publish/subscribe and the reactivity bits.

The scenario:

  1. A user has a board which they can add and remove items to and from.
  2. This all connects to an external API I’ve developed - this is all working.
  3. I’m connecting this all together using publish/subscribe - this is all working.
  4. Now this is where I run into a bit of trouble on how to achieve what I’m after. I want to update the board without the need to refresh the page as this is the only way I’m able to see the newly added/removed items from the board.

This is my code:

Helpers:

Products = new Mongo.Collection('products');

Tracker.autorun(function () {
  Meteor.subscribe("getProducts");
});

Template.productsList.helpers({
  products(){
	return Products && Products.find();
  }
});

Server side

Meteor.publish('getProducts', function () {
	const self = this;
	try {

	  const response = HTTP.get(MyAPICall);
	  const boardData = response.data.board[0];

	  _.each(boardData.products, function (item) {

		var doc = {
		  boardId: boardData._id,
		  id: item._id,
		  name: item.name,
		  url: item.url,
		  image: item.imagePath,
		  price: Number(item.price).toFixed(2),
		  valueChange: item.valueChanged,
		  percentageChange: item.percentageChanged,
		  dateAdded: item.createdAt
		};

		self.added('products', item._id, doc);
	  });

	  self.ready();
	} catch (error) {
	  console.log(error);
	}
  });

Events:

Meteor.call('trackProduct', Session.get('productData'), targetPrice, (error, results) => {
			if (error) {
			  throwError('trackProduct event', error);
			} else {			  
			  document.location.reload(true); // This is what I need to do to see the changes.
			}
		  });

The issue is probably something so simple I’ve missed it in the docs, but googling it I’ve still not managed to find a solutions.

Any help/advice would be greatly appreciated.
Thanks in advance.


#2

The issue I think you have is that publications are not inherently reactive, which means your publication only runs once.

There are a few things you could do to resolve this:

  1. You are using a Meteor.autorun to subscribe to your publication. At the moment that autorun is not really needed, as there is no reactive data inside the autorun to cause it to re-run. Having said that, you could introduce reactive data when the board changes to force a resubscription and so re-run your publication.
  2. You could poll your endpoint on a suitable interval inside your publication and track changes to your board data when appropriate.
  3. You could move your endpoint fetching out into a Meteor.method and use a board change event to refetch data with a Meteor.call (that almost looks what you’re trying to do with your call code).
  4. Instead of assembling your own publication with this.added etc, you could insert your document into another collection which has a simple publication based on find(). Changes to that cursor will be sent to the client with the appropriate subscription.
  5. Some combination of any or all of those, or something I’ve not thought to mention :wink:

You also have a couple of places in your code which you may want to look at.

  1. You would be better advised to use a template level subscription in your Template.productList.onCreated, rather than a global Meteor.subscribe. Template level subscriptions are cleaned up for you when the template is destroyed.
  2. You don’t need return Products && Products.find(); in your helper - return Products.find(); is enough.
  3. I don’t know when you would ever need to use document.location.reload() in a Meteor application. The standard reactive “pipeline” takes care of updating relevant UI data for you.

I’ve likely missed a bunch of stuff which may be helpful to you - shout up if you need more! :slight_smile:


#3

This is my main issue in making it reactive.

I’ll try the suggestions mentioned and made the fixes recommended.

Thanks,
Tom