Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Thanks for prompting me to write a clearer explanation. Here's a fuller example with my comments. There were a few bugs and inconsistencies that I've cleaned up. Next docs release will use this.</p> <p><a href="http://docs.meteor.com/#meteor_publish" rel="noreferrer"><code>Meteor.publish</code></a> is quite flexible. It's not limited to publishing existing MongoDB collections to the client: we can publish anything we want. Specifically, <code>Meteor.publish</code> defines a <em>set of documents</em> that a client can subscribe to. Each document belongs to some collection name (a string), has a unique <code>_id</code> field, and then has some set of JSON attributes. As the documents in the set change, the server will send the changes down to each subscribed client, keeping the client up to date. </p> <p>We're going to define a document set here, called <code>"counts-by-room"</code>, that contains a single document in a collection named <code>"counts"</code>. The document will have two fields: a <code>roomId</code> with the ID of a room, and <code>count</code>: the total number of messages in that room. There is no real MongoDB collection named <code>counts</code>. This is just the name of the collection that our Meteor server will be sending down to the client, and storing in a <em>client-side</em> collection named <code>counts</code>.</p> <p>To do this, our publish function takes a <code>roomId</code> parameter that will come from the client, and observes a query of all Messages (defined elsewhere) in that room. We can use the more efficient <code>observeChanges</code> form of observing a query here since we won't need the full document, just the knowledge that a new one was added or removed. Anytime a new message is added with the <code>roomId</code> we're interested in, our callback increments the internal count, and then publishes a new document to the client with that updated total. And when a message is removed, it decrements the count and sends the client the update.</p> <p>When we first call <code>observeChanges</code>, some number of <code>added</code> callbacks will run right away, for each message that already exists. Then future changes will fire whenever messages are added or removed.</p> <p>Our publish function also registers an <code>onStop</code> handler to clean up when the client unsubscribes (either manually, or on disconnect). This handler removes the attributes from the client and tears down the running <code>observeChanges</code>.</p> <p>A publish function runs each time a new client subscribes to <code>"counts-by-room"</code>, so each client will have an <code>observeChanges</code> running on its behalf.</p> <pre><code>// server: publish the current size of a collection Meteor.publish("counts-by-room", function (roomId) { var self = this; var count = 0; var initializing = true; var handle = Messages.find({room_id: roomId}).observeChanges({ added: function (doc, idx) { count++; if (!initializing) self.changed("counts", roomId, {count: count}); // "counts" is the published collection name }, removed: function (doc, idx) { count--; self.changed("counts", roomId, {count: count}); // same published collection, "counts" } // don't care about moved or changed }); initializing = false; // publish the initial count. `observeChanges` guaranteed not to return // until the initial set of `added` callbacks have run, so the `count` // variable is up to date. self.added("counts", roomId, {count: count}); // and signal that the initial document set is now available on the client self.ready(); // turn off observe when client unsubscribes self.onStop(function () { handle.stop(); }); }); </code></pre> <p>Now, on the client, we can treat this just like a typical Meteor subscription. First, we need a <code>Mongo.Collection</code> that will hold our calculated counts document. Since the server is publishing into a collection named <code>"counts"</code>, we pass <code>"counts"</code> as the argument to the <code>Mongo.Collection</code> constructor.</p> <pre><code>// client: declare collection to hold count object Counts = new Mongo.Collection("counts"); </code></pre> <p>Then we can subscribe. (You can actually subscribe before declaring the collection: Meteor will queue the incoming updates until there's a place to put them.) The name of the <em>subscription</em> is <code>"counts-by-room"</code>, and it takes one argument: the current room's ID. I've wrapped this inside <code>Deps.autorun</code> so that as <code>Session.get('roomId')</code> changes, the client will automatically unsubscribe from the old room's count and resubscribe to the new room's count.</p> <pre><code>// client: autosubscribe to the count for the current room Tracker.autorun(function () { Meteor.subscribe("counts-by-room", Session.get("roomId")); }); </code></pre> <p>Finally, we've got the document in <code>Counts</code> and we can use it just like any other Mongo collection on the client. Any template that references this data will automatically redraw whenever the server sends a new count.</p> <pre><code>// client: use the new collection console.log("Current room has " + Counts.findOne().count + " messages."); </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload