mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
1337 lines
48 KiB
HTML
1337 lines
48 KiB
HTML
<template name="api">
|
|
{{#better_markdown}}
|
|
|
|
<h1 id="api">The Meteor API</h1>
|
|
|
|
Your Javascript code can run in two environments: the <i>client</i>
|
|
(browser), and the <i>server</i> (a Node.js container on a server). For
|
|
each function in this API reference, we'll indicate if the function is
|
|
available just on the client, just on the server, or *Anywhere*.
|
|
|
|
<h2 id="meteor"><span>Meteor</span></h2>
|
|
|
|
{{> api_box is_client}}
|
|
{{> api_box is_server}}
|
|
{{> api_box startup}}
|
|
|
|
On a server, the function will run as soon as the server process is
|
|
finished starting. On a client, the function will run as soon as the DOM
|
|
is ready and any `<body>` templates from your `.html` files have been
|
|
put on the screen.
|
|
|
|
// On server startup, if the database is empty, create some initial data.
|
|
if (Meteor.is_server) {
|
|
Meteor.startup(function () {
|
|
if (Rooms.find().length === 0) {
|
|
Rooms.insert({name: "Initial room"});
|
|
}
|
|
});
|
|
}
|
|
|
|
{{> api_box setTimeout}}
|
|
{{> api_box setInterval}}
|
|
{{> api_box clearTimeout}}
|
|
{{> api_box clearInterval}}
|
|
|
|
On the server, native functions like `setTimeout()` that schedule future
|
|
callbacks need to be wrapped so that they create the right thread
|
|
context for your code. You don't have to worry about the details; just
|
|
use these equivalents instead of the native functions.
|
|
|
|
{{#warning}}
|
|
We will probably override the native server functions in a future
|
|
release, obviating the need for these wrappers.
|
|
{{/warning}}
|
|
|
|
{{> api_box publish}}
|
|
|
|
To publish data to clients, call `Meteor.publish()` on the server with
|
|
two parameters: the name of the data set, and a *publish function*
|
|
that Meteor will call each time a client subscribes to the name.
|
|
|
|
{{#warning}}
|
|
Meteor will emit a warning message if you call `Meteor.publish()` in a
|
|
project that includes the `autopublish` package. Your publish function
|
|
will still work.
|
|
{{/warning}}
|
|
|
|
If your publish function returns a collection cursor, Meteor will watch
|
|
publish each result of that cursor and then keep the client up to date
|
|
as the cursor's results change (by running `observe()` on your cursor).
|
|
When a client unsubscribes, Meteor will unset all of the client's data
|
|
for you.
|
|
|
|
// server: publish the rooms collection, minus secret info.
|
|
Meteor.publish("rooms", function () {
|
|
return Rooms.find({}, {fields: {secret_info: false}});
|
|
});
|
|
|
|
To explicitly <i>set</i> and <i>unset</i> attributes on a client, use
|
|
these methods provided by `this` in your publish function:
|
|
|
|
Example:
|
|
|
|
// server: publish the current size of a collection
|
|
Meteor.publish("messages-count", function (roomId) {
|
|
var self = this;
|
|
var uuid = Meteor.uuid();
|
|
var count = 0;
|
|
|
|
handle = Room.find({room_id: roomId}).observe({
|
|
added: function (doc, idx) {
|
|
count++;
|
|
self.set("messages-count", uuid, "count", count);
|
|
self.flush();
|
|
},
|
|
removed: function (doc, idx) {
|
|
count--;
|
|
self.set("messages-count", uuid, "count", count);
|
|
self.flush();
|
|
}
|
|
// don't care about moved or changed
|
|
});
|
|
|
|
// remove data and turn off observe when client unsubs
|
|
self.onStop(function () {
|
|
handle.stop();
|
|
self.unset("messages-count", uuid, "count");
|
|
self.flush();
|
|
});
|
|
});
|
|
|
|
{{> api_box subscription_set}}
|
|
{{> api_box subscription_unset}}
|
|
{{> api_box subscription_complete}}
|
|
{{> api_box subscription_flush}}
|
|
|
|
{{#warning}}
|
|
If you use observe() to watch changes to the database, be sure to call `this.flush()`
|
|
from inside your observe callbacks. Methods that update the database
|
|
are considered finished when the observe callbacks return.
|
|
{{/warning}}
|
|
|
|
{{> api_box subscription_onStop}}
|
|
{{> api_box subscription_stop}}
|
|
|
|
{{> api_box subscribe}}
|
|
|
|
When you subscribe to a set, the client will store the attributes
|
|
published by the server in local Minimongo collections, with the same
|
|
name as the `collection` argument to `set()`. Meteor will queue
|
|
incoming attributes until you declare the `Meteor.Collection()` on the
|
|
client with the matching collection name.
|
|
|
|
// okay to subscribe (and possibly receive data) before declaring
|
|
// the client collection that will hold it. assume "allplayers"
|
|
// publishes data from server's "players" collection.
|
|
Meteor.subscribe("allplayers");
|
|
...
|
|
// client queues incoming players documents until ...
|
|
...
|
|
Players = new Meteor.Collection("players");
|
|
|
|
If more than one subscription sends overlapping attributes (same
|
|
collection name, document ID, and attribute name), Meteor will *shadow*
|
|
all but one of the values. The value in Minimongo will be that from the
|
|
*first* subscription the client activated. (Even if it is not the first
|
|
to send the duplicated attribute.)
|
|
|
|
When you stop a subscription, Meteor removes all the data that came from
|
|
that subscription. If any attributes shadowed a different
|
|
subscription's data, Meteor chooses the next-highest priority
|
|
subscription.
|
|
|
|
{{#warning}}
|
|
In a future release we will give you better control over subscription
|
|
priorities.
|
|
{{/warning}}
|
|
|
|
If all of the attributes in a Minimongo document are removed, Meteor
|
|
will remove the (now empty) document. If you want to publish empty
|
|
documents, just use a placeholder attribute.
|
|
|
|
// Clicks.insert({exists: true});
|
|
|
|
{{> api_box autosubscribe}}
|
|
|
|
The easiest way to explain `autosubscribe()` is to show an example.
|
|
|
|
// Subscribe to the chat messages in the current room. Automatically
|
|
// update the subscription whenever the current room changes.
|
|
Meteor.subscriptions(function () {
|
|
Meteor.subscribe("chat", {room: Session.get("current-room");});
|
|
});
|
|
|
|
`func` will be run immediately, and while it runs,
|
|
records will be kept of the subscriptions it makes
|
|
(via [`Meteor.subscribe`](#subscribe)) and the data it uses
|
|
(including calls to [`Session.get`](#get)
|
|
and [`collection.find`](#find)).
|
|
|
|
Whenever the used data changes, the subscriptions will be cancelled
|
|
and `func` will be re-run to make replacement subscriptions.
|
|
|
|
{{#warning}}
|
|
It's not necessary to call `stop` on subscriptions made
|
|
from inside `Meteor.autosubscribe`.
|
|
{{/warning}}
|
|
|
|
{{> api_box methods}}
|
|
|
|
Example:
|
|
|
|
Meteor.methods({
|
|
foo: function (arg1, arg2) {
|
|
// .. do stuff ..
|
|
if (you want to throw an error)
|
|
throw new Meteor.Error(404, "Can't find my pants");
|
|
return "some return value";
|
|
}
|
|
});
|
|
|
|
Calling `methods()` on the server defines JavaScript functions that can
|
|
be called remotely by clients. When a client invokes a method, it sends
|
|
a message to the server to run the method, along with the parameters.
|
|
The server eventually sends back a result.
|
|
|
|
Methods are always written in synchronous style. Their caller will not
|
|
receive a response until they return (either normally, or by throwing an
|
|
exception.) But this is not a limitation, since we have fibers on the
|
|
server, so you can set up your control flow however you want.
|
|
|
|
Calling `methods()` on the client defines *stub* functions associated
|
|
with server methods of the same name. When a client invokes a server
|
|
method, it will run that method's stub in parallel, if one is defined.
|
|
Stubs are run for their side-effects: they are intended to *simulate*
|
|
the result of what the server's method will do, but without waiting for
|
|
the round trip delay. On the client, the return value of a stub is
|
|
ignored. (If a stub throws an exception, including a `Meteor.Error`
|
|
exception, that will be logged to the console.)
|
|
|
|
Inside your method invocation, <b>this</b> is bound to a method
|
|
invocation object, which provides the following:
|
|
|
|
* `is_simulation`: a boolean value, true if this invocation is a stub.
|
|
* `unblock()`: when called, allows the next method from this client to
|
|
begin running.
|
|
|
|
{{#warning}}
|
|
When authentication is available, the current user will be in
|
|
`this.user`.
|
|
{{/warning}}
|
|
|
|
{{> api_box error}}
|
|
|
|
Methods can throw any kind of exception, not just `Meteor.Error`. But
|
|
`Meteor.Error` is the only kind of error that can go across the wire. If
|
|
you throw a different exception, then it will be mapped to
|
|
`Meteor.Error(500, "Internal server error")` on the wire.
|
|
|
|
{{> api_box method_invocation_is_simulation}}
|
|
|
|
{{> api_box method_invocation_unblock}}
|
|
|
|
On the server, methods from a given client run one at a time. The N+1th
|
|
invocation from a client won't start until the Nth invocation
|
|
returns. However, you can change this by calling `this.unblock()`. This
|
|
will allow the N+1th invocation to start running in a new fiber.
|
|
|
|
{{> api_box call}}
|
|
|
|
On both the client and server, if you include a callback function as the
|
|
last argument (which can't be an argument to the method, since functions
|
|
aren't serializeable), the method will run asynchronously: it will
|
|
return nothing in particular and will not throw an execption. When the
|
|
method is complete (which may or may not happen before `Meteor.call`
|
|
returns), the callback will be called. If an error was thrown, then
|
|
`err` will be the exception object (possibly mapped to `Meteor.Error`,
|
|
just like above.) Otherwise, the return value (possibly undefined) will
|
|
be in `result`.
|
|
|
|
// async
|
|
Meteor.call('foo', 1, 2, function (err, result) { ... } );
|
|
|
|
If you do not pass a callback on the server, the method invocation will
|
|
block until the method is complete. It will eventually return the
|
|
return value of the method, or it will throw an exception if the method
|
|
threw an exception. (Possibly mapped to 500 Server Error if the
|
|
exception happened remotely and it was not a `Meteor.Error` exception.)
|
|
|
|
// sync
|
|
var result = Meteor.call('foo', 1, 2);
|
|
|
|
On the client, if you do not pass a callback and you are not inside a
|
|
stub, `call()` will return `undefined`, and you will have no way to get
|
|
the return value of the method. That is because the client doesn't have
|
|
fibers, so there is not actually any way it can block on the remote
|
|
execution of a method.
|
|
|
|
Finally, if you are inside a stub on the client and call another
|
|
method, the other method is not executed (no RPC is generated, nothing
|
|
"real" happens.) If that other method has a stub, that stub stands in
|
|
for the method and is executed. The method call's return value is the
|
|
return value of the stub function. The client has no problem executing
|
|
a stub synchronously, and that is why it's okay for the client to use
|
|
the synchronous `Meteor.call` form from inside a method body, as
|
|
described earlier.
|
|
|
|
You use this functionality all the time, because the database mutators
|
|
(`insert`, `update`, `remove`) are essentially methods. When you call
|
|
them on the client (whether from inside a method or at toplevel), you're
|
|
invoking their stub versions that update the local cache, instead of
|
|
their "real" versions that update the database (using credentials known
|
|
only to the server.)
|
|
|
|
{{> api_box apply}}
|
|
|
|
`Meteor.apply()` is just like `Meteor.call()`, but it takes a method
|
|
name and an array of arguments.
|
|
|
|
{{> api_box status}}
|
|
|
|
This method returns the status of the connection between the client and
|
|
the server. The return value is an object with the following fields:
|
|
|
|
<dl class="objdesc">
|
|
<dt><span class="name">connected</span>
|
|
<span class="type">Boolean</span></dt>
|
|
<dd>True if currently connected to the server. If false, changes will
|
|
be queued up until the connection is reestablished.</dd>
|
|
|
|
<dt><span class="name">status</span>
|
|
<span class="type">String</span></dt>
|
|
<dd>Text string describing the current reconnection status. The possible
|
|
values are "`connected`" (the connection is up and
|
|
running), "`connecting`" (disconnected and trying to open a
|
|
new connection), and "`waiting`" (failed to connect and
|
|
waiting to try to reconnect).</dd>
|
|
|
|
<dt><span class="name">retry_count</span>
|
|
<span class="type">Number</span></dt>
|
|
<dd>The number of times the client has tried to reconnect since the
|
|
connection was lost. 0 when connected.</dd>
|
|
|
|
<dt><span class="name">retry_time</span>
|
|
<span class="type">Number or undefined</span></dt>
|
|
<dd>The estimated time of the next reconnection attempt. To turn this
|
|
into an interval until the next reconnection, use
|
|
"`retry_time - (new Date()).getTime()`". This key will
|
|
be set only when `status` is `"waiting"`.
|
|
</dd>
|
|
</dl>
|
|
|
|
Instead of using callbacks to notify you on changes, this is
|
|
a <a href="#reactivity">reactive</a> data source. You can use it in a
|
|
<a href="#templates">template</a> or <a href="#meteordeps">invalidation
|
|
context</a> to get realtime updates.
|
|
|
|
{{> api_box reconnect}}
|
|
|
|
{{> api_box connect}}
|
|
|
|
By default, every Meteor client is connected to its server. When you
|
|
call `Meteor.subscribe`, `Meteor.status`, `Meteor.call`, and
|
|
`Meteor.apply`, you are referring to the connection back to that default server.
|
|
Your client and server can open additional connections to other Meteor
|
|
servers by calling `Meteor.connect()`, passing the URL of the
|
|
server endpoint. `Meteor.connect` returns an object which provides:
|
|
|
|
* `subscribe`
|
|
* `methods` (to define stubs)
|
|
* `call`
|
|
* `apply`
|
|
* `status`
|
|
* `reconnect`
|
|
|
|
{{#warning}}
|
|
`Meteor.connect` is not currently implemented on the
|
|
server, so you can't call remote Meteor methods on the server today.
|
|
This will change, though: you'll be able to connect to a remote server
|
|
and do synchronous method calls, and Meteor will use
|
|
fibers to run them synchronously for you.
|
|
{{/warning}}
|
|
|
|
{{> api_box flush }}
|
|
|
|
Normally, when you make changes (like writing to the database),
|
|
their impact (like updating the DOM) is delayed until the system is
|
|
idle. This keeps things predictable — you can know that the DOM
|
|
won't go changing out from under your code as it runs. It's also one
|
|
of the things that makes Meteor fast.
|
|
|
|
`Meteor.flush()` forces all of the pending reactive updates to complete
|
|
(for example, it ensures the DOM has been updated with your recent
|
|
database changes.) Call `flush()` to apply those pending changes
|
|
immediately. The main use for this is to make sure the DOM has been
|
|
brought up to date with your latest changes, so you can manually
|
|
manipulate it with jQuery or the like.
|
|
|
|
When you call `flush()`, any auto-updating DOM elements that are not on
|
|
the screen may be cleaned up (meaning that Meteor will stop tracking and
|
|
updating the elements, so that the browser's garbage collector can
|
|
delete them.) So, if you manually call `flush()`, you need to make sure
|
|
that any auto-updating elements that you have created with templates, or
|
|
by calling [`Meteor.ui.render`](#render), have already been inserted in
|
|
the main DOM tree.
|
|
|
|
Technically speaking, `flush()` calls the [invalidation
|
|
callbacks](#on_invalidate) on every [reactive context](#context) that
|
|
has been [invalidated](#invalidate), but hasn't yet has its callbacks
|
|
called. If the invalidation callbacks invalidate still more contexts,
|
|
flush keeps flushing until everything is totally settled. The DOM
|
|
elements are cleaned up because of logic in
|
|
[`Meteor.ui.render`](#render) that works through invalidations.
|
|
|
|
{{> api_box random}}
|
|
|
|
JavaScript's `Math.random()` returns a pseudorandom number between 0
|
|
(inclusive) and 1 (exclusive). But the language standard doesn't
|
|
guarantee the quality of the random numbers returned. Meteor provides
|
|
`Meteor.random()`, a good PRNG replacement. This function uses the
|
|
[Alea](http://baagoe.org/en/wiki/Better_random_numbers_for_javascript#Alea)
|
|
algorithm to generate random numbers, seeded with the value of `new
|
|
Date()`.
|
|
|
|
> Meteor.random()
|
|
0.7759382962249219
|
|
|
|
{{> api_box uuid}}
|
|
|
|
`Meteor.uuid()` generates an RFC 4122 version 4 UUID. It is built on
|
|
`Meteor.random()`.
|
|
|
|
> Meteor.uuid()
|
|
"0ec37060-0d1a-4ddd-b560-d886b891abba"
|
|
|
|
<h2 id="meteorcollection_header"><span>Meteor.Collection</span></h2>
|
|
|
|
Meteor stores data in *collections*. To get started, declare a
|
|
collection with `new Meteor.Collection()`.
|
|
|
|
{{> api_box meteor_collection}}
|
|
|
|
Calling this function is analogous to declaring a model in an
|
|
traditional ORM (Object-Relation Mapper)-centric framework. It sets up a
|
|
*collection* (a storage space for records, or "documents") that can be
|
|
used to store a particular type of information, like users, posts,
|
|
scores, todo items, or whatever matters to your application. Each
|
|
document is a JSON object. It includes an `_id` property whose value is
|
|
unique in the collection, which Meteor will set when you first create
|
|
the document.
|
|
|
|
// common code on client and server declares livedata-managed mongo
|
|
// collection.
|
|
Chatrooms = new Meteor.Collection("chatrooms");
|
|
Messages = new Meteor.Collection("messages");
|
|
|
|
The function returns an object with methods to [`insert`](#insert)
|
|
documents in the collection, [`update`](#update) their properties, and
|
|
[`remove`](#remove) them, and to [`find`](#find) the documents in the
|
|
collection that match arbitrary criteria. The way these methods work is
|
|
compatible with the popular Mongo database API. The same database API
|
|
works on both the client and the server (see below).
|
|
|
|
// return array of my messages
|
|
var my_messages = Messages.find({user_id:Session.get('my_user_id')}).fetch();
|
|
|
|
// create a new message
|
|
Messages.insert({text: "Hello, world!"});
|
|
|
|
// mark my first message as "important"
|
|
Messages.update(my_messages[0].id, {$set: {important: true}});
|
|
|
|
If you pass a `name` when you create the collection, then you are
|
|
declaring a persistent collection — one that is stored on the
|
|
server and seen by all users. Client code and server code can both
|
|
access the same collection using the same API.
|
|
|
|
Specifically, when you pass a `name`, here's what happens:
|
|
|
|
* On the server, a collection with that name is created on a backend
|
|
Mongo server. When you call methods on that collection on the server,
|
|
they translate directly into normal Mongo operations.
|
|
|
|
* On the client, a [`minimongo`](#minimongo) instance is
|
|
created. Minimongo is essentially an in-memory, non-persistent
|
|
implementation of Mongo in pure JavaScript. It serves as a local cache
|
|
that stores just the subset of the database that this client is working
|
|
with. Queries on the client ([`find`](#find)) are served directly out of
|
|
this cache, without talking to the server.
|
|
|
|
* When you write to the database on the client ([`insert`](#insert),
|
|
[`update`](#update), [`remove`](#remove)), the command is executed
|
|
immediately on the client, and, simultaneously, it's shipped up to the
|
|
server and executed there too. The [`livedata`](#livedata) package is
|
|
responsible for this.
|
|
|
|
If you pass `null` as the `name`, then you're creating a local
|
|
collection. It's not synchronized anywhere; it's just a local scratchpad
|
|
that supports Mongo-style [`find`](#find), [`insert`](#insert),
|
|
[`update`](#update), and [`remove`](#remove) operations. (On both the
|
|
client and the server, this scratchpad is implemented using Minimongo.)
|
|
|
|
By default, Meteor automatically publishes every document in your
|
|
collection to each connected client. Any time a client modifies a
|
|
document, and keeps those documents up to date as they change. To turn
|
|
this behavior off, remove the package:
|
|
|
|
$ meteor remove autopublish
|
|
|
|
{{#warning}}
|
|
Currently the client is given full write access to the collection. They
|
|
can execute arbitrary Mongo update commands. Once we build
|
|
authentication, you will be able to limit the client's direct access to
|
|
insert, update, and remove. We are also considering validators and
|
|
other ORM-like functionality.
|
|
{{/warning}}
|
|
|
|
// Create a collection called Posts and put a document in it. The
|
|
// document will be immediately visible in the local copy of the
|
|
// collection. It will be written to the server-side database
|
|
// a fraction of a second later, and a fraction of a second
|
|
// after that, it will be synchronized down to any other clients
|
|
// that are subscribed to a query that includes it (see
|
|
// Meteor.subscribe and autopublish)
|
|
Posts = new Meteor.Collection("posts");
|
|
Posts.insert({title: "Hello world", body: "First post"});
|
|
|
|
// Changes are visible immediately -- no waiting for a round trip to
|
|
// the server.
|
|
assert(Posts.find().count() === 1);
|
|
|
|
// Create a temporary, local collection. It works just any other
|
|
// collection, but it doesn't send changes to the server, and it
|
|
// can't receive any data from subscriptions.
|
|
Scratchpad = new Meteor.Collection;
|
|
for (var i = 0; i < 10; i++)
|
|
Scratchpad.insert({number: i * 2});
|
|
assert(Scratchpad.find({number: {$lt: 9}}).count() === 5);
|
|
|
|
Collections provide nearly the same API on both the client and server.
|
|
As a result, in many cases your server methods and client stubs can be
|
|
the same shared code.
|
|
|
|
{{#warning}}
|
|
In this preview, Minimongo has some limitations:
|
|
|
|
* `$elemMatch` is not supported in selectors.
|
|
* `$pull` in modifiers can only accept certain kinds
|
|
of selectors.
|
|
* In selectors, dot notation and ordinal indexing may not work correctly.
|
|
* `$` to denote the matched array position is not
|
|
supported in modifier.
|
|
* Sort does not support subkeys (you can sort on `a`,
|
|
but not `a.b`.)
|
|
* `findAndModify`, upsert, aggregate functions, and
|
|
map/reduce aren't supported.
|
|
* The supported types are String, Number, Boolean, Array,
|
|
and Object.
|
|
|
|
All of these will be addressed in a future release. For full
|
|
Minimongo release notes, see packages/minimongo/NOTES
|
|
in the repository.
|
|
{{/warning}}
|
|
|
|
{{#warning}}
|
|
Minimongo currently doesn't have indexes. This will come soon. It's
|
|
usually not an issue, since there usually isn't that much data in
|
|
the client — it is not that common for developers to implement
|
|
indexes in their client-side models anyway.
|
|
{{/warning}}
|
|
|
|
{{> api_box find}}
|
|
|
|
`find` returns a cursor. It does not immediately access the database or
|
|
return documents. Cursors provide `fetch()` to return all matching
|
|
documents, `map()` and `forEach()` to iterate over all matching
|
|
documents, and `observe()` to register callbacks when the set of
|
|
matching documents changes.
|
|
|
|
{{#warning}}
|
|
Collection cursors are not query snapshots. If the database changes
|
|
between calling `Collection.find()` and fetching the
|
|
results of the cursor, or while fetching results from the cursor,
|
|
those changes may or may not appear in the result set. This cursor
|
|
behavior mirrors MongoDB itself.
|
|
{{/warning}}
|
|
|
|
Cursors are a reactive data source. The first time you retrieve a
|
|
cursor's documents with `fetch()`, `map()`, or `forEach()` inside a
|
|
reactive context (eg, [`Meteor.ui.render`](#render),
|
|
[`Meteor.autosubscribe`](#autosubscribe), Meteor will register a
|
|
dependency on the underlying data. Any change to the collection that
|
|
changes the documents in a cursor will trigger a recomputation. To
|
|
disable this behavior, pass `{reactive: false}` as an option to
|
|
`find()`.
|
|
|
|
{{> api_box findone}}
|
|
|
|
Immediately return a single document without creating an explicit
|
|
cursor. Equivalent to `collection.find(selector, options).fetch()[0]`.
|
|
|
|
{{> api_box cursor_foreach}}
|
|
{{> api_box cursor_map}}
|
|
{{> api_box cursor_fetch}}
|
|
|
|
To access the documents in a cursor, use one of the above
|
|
methods. `forEach` will call a callback function with each
|
|
document in order, `map` returns an array containing the
|
|
results of applying the callback function to each document,
|
|
and `fetch` simply returns an array of the results.
|
|
|
|
Examples:
|
|
|
|
// Print the titles of the five top-scoring posts
|
|
var top_posts = Posts.find({}, {sort: {score: -1}, limit: 5});
|
|
var count = 0;
|
|
top_posts.forEach(function (post) {
|
|
console.log("Title of post " + count + ": " + post.title);
|
|
count += 1;
|
|
});
|
|
|
|
{{> api_box cursor_count}}
|
|
|
|
// Display a count of posts matching certain criteria. Automatically
|
|
// keep it updated as the database changes.
|
|
var frag = Meteor.ui.render(function () {
|
|
var high_scoring = Posts.find({score: {$gt: 10}});
|
|
return $("<p>There are " + high_scoring.length + " posts with " +
|
|
"scores greater than 10</p>");
|
|
});
|
|
document.body.appendChild(frag);
|
|
|
|
When called in a reactive context, `count()` registers a dependency on
|
|
the number of matching documents. (Updates that just change or reorder
|
|
the documents in the result set will *not* trigger a recomputation.)
|
|
|
|
{{> api_box cursor_rewind}}
|
|
|
|
To access the data in a cursor more than once, call `rewind` to reset
|
|
the cursor to its initial state.
|
|
|
|
{{> api_box cursor_observe}}
|
|
|
|
Establishes a *live query* that notifies callbacks on any change to the
|
|
query result.
|
|
|
|
`callbacks` may have the following functions as properties:
|
|
|
|
<dl class="callbacks">
|
|
|
|
{{#dtdd "added(document, before_index)"}}
|
|
Called when a new document enters the result set. It was inserted
|
|
immediately before the document currently at the
|
|
position `before_index`. Or if it was inserted at the end
|
|
of the list, `before_index` will be equal to the (prior)
|
|
length of the list.
|
|
{{/dtdd}}
|
|
|
|
{{#dtdd "changed(new_document, at_index, old_document)"}}
|
|
The contents of the document at position `at_index`
|
|
changed to `new_document`, was previously `old_document`.
|
|
{{/dtdd}}
|
|
|
|
{{#dtdd "moved(document, old_index, new_index)"}}
|
|
A document changed its position in the result set,
|
|
from `old_index` to `new_index`. For your
|
|
convenience, its current contents is `document`. (This will
|
|
only fire immediately after `changed`.
|
|
{{/dtdd}}
|
|
|
|
{{#dtdd "removed(old_document, at_index)"}}
|
|
The document at position `at_index`, which was previously
|
|
`old_document`, is no longer in the result set.
|
|
{{/dtdd}}
|
|
|
|
</dl>
|
|
|
|
`added` will immediately be called as necessary to
|
|
deliver the initial results of the query, if any.
|
|
|
|
`observe()` returns a live query handle, which is an object with a
|
|
`stop() method. Call this function with no arguments to stop calling
|
|
the callback functions and tear down the query. **The query will run
|
|
forever until you call this.**
|
|
|
|
Calling `observe` in a reactive context does *not* register a dependency
|
|
on the query.
|
|
|
|
Example:
|
|
|
|
// Keep track of how many administrators are online.
|
|
var count = 0;
|
|
var query = Users.find({admin: true, online_now: true});
|
|
var handle = query.observe({
|
|
added: function (user) {
|
|
count++;
|
|
console.log(user.name + " brings the total to " + count + " admins.");
|
|
},
|
|
removed: function () {
|
|
count--;
|
|
console.log("Lost one. We're now down to " + count + " admins.");
|
|
}
|
|
});
|
|
|
|
// After five seconds, stop keeping the count.
|
|
setTimeout(function () {handle.stop();}, 5000);
|
|
|
|
{{> api_box insert}}
|
|
|
|
Add a document to the collection. A document is just an object, and
|
|
its fields can contain any combination of JSON-compatible datatypes
|
|
(arrays, objects, numbers, strings, null, true, and false).
|
|
|
|
`insert()` will generate a unique ID for the object you pass, insert it
|
|
in the database, and return the ID.
|
|
|
|
On the server, if you don't provide a callback, then `insert` blocks
|
|
until the database acknowledges the write, or throws an exception if
|
|
something went wrong. If you do provide a callback, the server calls
|
|
that callback with the error and result of the insert command.
|
|
|
|
On the client, `insert` never blocks. If you do not provide a callback
|
|
and the insert fails on the server, then Meteor will log a warning with
|
|
`Meteor._debug()`. If you provide a callback, Meteor will call that
|
|
function with the error and result of the server's insert.
|
|
|
|
Example:
|
|
|
|
var groceries_id = Lists.insert({name: "Groceries"})._id;
|
|
Items.insert({list: groceries_id, name: "Watercress"});
|
|
Items.insert({list: groceries_id, name: "Persimmons"});
|
|
|
|
{{> api_box update}}
|
|
|
|
Modify documents that match `selector` as
|
|
given by `modifer` (see <a href="#modifiers">modifier
|
|
documentation</a>). By default, modify only one matching document.
|
|
If `multi` is true, modify all matching documents.
|
|
|
|
Instead of a selector, you can pass a string, which will be
|
|
interpreted as an `_id`.
|
|
|
|
{{#warning}}
|
|
The Mongo `upsert` feature is not implemented.
|
|
{{/warning}}
|
|
|
|
On the server, if you don't provide a callback, then `update` blocks
|
|
until the database acknowledges the write, or throws an exception if
|
|
something went wrong. If you do provide a callback, the server calls
|
|
that callback with the error and result of the update command.
|
|
|
|
On the client, `update` never blocks. If you do not provide a callback
|
|
and the insert fails on the server, then Meteor will log a warning with
|
|
`Meteor._debug()`. If you provide a callback, Meteor will call that
|
|
function with the error and result of the server's update.
|
|
|
|
Example:
|
|
|
|
// Give the "Superlative" badge to each user with a score greater than
|
|
// 10. If they are logged in and their badge list is visible on the
|
|
// screen, it will update automatically as they watch.
|
|
Users.update({score: {$gt: 10}},
|
|
{badges: {$addToSet: "Superlative"}},
|
|
{multi: true});
|
|
|
|
{{> api_box remove}}
|
|
|
|
Find all of the documents that match `selector`, and
|
|
delete them from the collection. Or instead of a selector, you may
|
|
pass a string, to delete the document with that `_id`.
|
|
Without any selector, remove all documents from the collection.
|
|
|
|
On the server, if you don't provide a callback, then `remove` blocks
|
|
until the database acknowledges the write, or throws an exception if
|
|
something went wrong. If you do provide a callback, the server calls
|
|
that callback with the error and result of the remove command.
|
|
|
|
On the client, `remove` never blocks. If you do not provide a callback
|
|
and the insert fails on the server, then Meteor will log a warning with
|
|
`Meteor._debug()`. If you provide a callback, Meteor will call that
|
|
function with the error and result of the server's remove.
|
|
|
|
Example:
|
|
|
|
// Delete all users with a karma of less than -2.
|
|
Users.remove({karma: {$lt: -2}});
|
|
|
|
// Delete all the log entries
|
|
Logs.remove();
|
|
|
|
// Show a list of posts that have been flagged, updating in realtime.
|
|
// Put a link next to each post that deletes the post if clicked.
|
|
var frag = Meteor.renderList(Posts, {
|
|
selector: {flagged: true},
|
|
render: function (post) {
|
|
// In real code it'd be necessary to sanitize post.name
|
|
return $("<div>" + post.name +
|
|
" <span class='delete'>Delete</span></div>");
|
|
},
|
|
events: {
|
|
'click .delete': function () {
|
|
Posts.remove(this._id);
|
|
}
|
|
}
|
|
});
|
|
document.body.appendChild(frag);
|
|
|
|
{{#api_box_inline selectors}}
|
|
|
|
In its simplest form, a selector is just a set of keys that must
|
|
match in a document:
|
|
|
|
// Matches all documents where deleted is false
|
|
{deleted: false}
|
|
|
|
// Matches all documents where the name and cognomen are as given
|
|
{name: "Rhialto", cognomen: "the Marvelous"}
|
|
|
|
// Matches every document
|
|
{}
|
|
|
|
But they can also contain more complicated tests:
|
|
|
|
// Matches documents where age is greater than 18
|
|
{age: {$gt: 18}}
|
|
|
|
// Also matches documents where tags is an array containing "today"
|
|
{tags: "popular"}
|
|
|
|
// Matches documents where fruit is one of three possibilities
|
|
{fruit: {$in: ["peach", "plum", "pear"]}}
|
|
|
|
See the <a href="http://www.mongodb.org/display/DOCS/Advanced+Queries" target="_blank">complete documentation.</a>
|
|
|
|
{{/api_box_inline}}
|
|
|
|
{{#api_box_inline modifiers}}
|
|
|
|
A modifer is an object that describes how to update a document in
|
|
place by changing some of its fields. Some examples:
|
|
|
|
// Set the 'admin' property on the document to true
|
|
{$set: {admin: true}}
|
|
|
|
// Add 2 to the 'votes' property, and add "Traz" to the end of the
|
|
// 'supporters' array
|
|
{$inc: {votes: 2}, $push: {supporters: "Traz"}}
|
|
|
|
But if a modifier doesn't contain any $-operators, then it is
|
|
instead interpreted as a literal document, and completely replaces
|
|
whatever was previously in the database.
|
|
|
|
// Find the document with id "123", and completely replace it.
|
|
Users.update({_id: "123"}, {name: "Alice", friends: ["Bob"]});
|
|
|
|
See the <a href="http://www.mongodb.org/display/DOCS/Updating#Updating-ModifierOperations" target="_blank">full list of modifiers.</a>
|
|
|
|
{{/api_box_inline}}
|
|
|
|
{{#api_box_inline sortspecifiers}}
|
|
|
|
Sorts may be specified using your choice of several syntaxes:
|
|
|
|
// All of these do the same thing (sort in ascending order by
|
|
// key "a", breaking ties in descending order of key "b")
|
|
|
|
[["a", "asc"], ["b", "desc"]]
|
|
["a", ["b", "desc"]]
|
|
{a: 1, b: -1}
|
|
|
|
The last form will only work if your JavaScript implementation
|
|
preserves the order of keys in objects. Most do, most of the time, but
|
|
it's up to you to be sure.
|
|
|
|
{{/api_box_inline}}
|
|
|
|
<h2 id="session"><span>Session</span></h2>
|
|
|
|
`Session` provides a global object on the client that you can use to
|
|
store an arbitrary set of key-value pairs. Use it to store things like
|
|
the currently selected item in a list.
|
|
|
|
What's special about `Session` is that it's reactive. If
|
|
you call [`Session.get`](#get)`("current_list")`
|
|
from inside a template, the template will automatically be rerendered
|
|
whenever [`Session.set`](#set) is called.
|
|
|
|
{{> api_box set}}
|
|
|
|
Example:
|
|
|
|
Meteor.autosubcribe(function () {
|
|
Meteor.subscribe("chat-history", {room: Session.get("currentRoomId")});
|
|
});
|
|
|
|
// Causes the function passed to Meteor.autosubscribe to be re-run, so
|
|
// that the chat-history subscription is moved to the room "home".
|
|
Session.set("currentRoomId", "home");
|
|
|
|
See [`Meteor.deps`](#meteordeps) for another example.
|
|
|
|
{{> api_box get}}
|
|
|
|
Example:
|
|
|
|
Session.set("enemy", "Eastasia");
|
|
var frag = Meteor.ui.render(function () {
|
|
return $("<p>We've always been at war with " +
|
|
Session.get("enemy") + "</p>");
|
|
});
|
|
|
|
// Page will say "We've always been at war with Eastasia"
|
|
document.body.append(frag);
|
|
|
|
// Page will change to say "We've always been at war with Eurasia"
|
|
Session.set("enemy", "Eurasia");
|
|
|
|
{{> api_box equals}}
|
|
|
|
These two expressions do the same thing:
|
|
|
|
(1) Session.get("key") === value
|
|
(2) Session.equals("key", value)
|
|
|
|
... but the second one is always better. It triggers fewer invalidations
|
|
(template redraws), making your program more efficient.
|
|
|
|
Example:
|
|
|
|
// Show a dynamically updating list of items. Let the user click on an
|
|
// item to select it. The selected item is given a CSS class so it
|
|
// can be rendered differently.
|
|
var frag = Meteor.ui.renderList(Posts, {
|
|
render: function (post) {
|
|
var cls = Session.equals("selected_post", post._id) ?
|
|
"selected" : "";
|
|
return $("<div class='" + cls + "'>" + post.title + "</div>");
|
|
}, events: {
|
|
'click': function () {
|
|
Session.set("selected_post", this._id);
|
|
}
|
|
}
|
|
});
|
|
|
|
document.body.appendChild(frag);
|
|
|
|
// In this code, when the user clicks on an item to select it, the
|
|
// render function is called twice: once to rerender the item
|
|
// that lost the selection, and once to rerender the item that gained
|
|
// the selection.
|
|
//
|
|
// If Session.get had been used instead of Session.equals, then when
|
|
// the user clicked on an item, the render function would have to
|
|
// called once for every Post shown in the list.
|
|
|
|
<h2 id="meteorui"><span>Meteor.ui</span></h2>
|
|
|
|
These are some utility functions for building interfaces. They make it
|
|
easy to create DOM elements that update automatically as data changes in
|
|
[`Session`](#pkg_session) variables or in a
|
|
[`Meteor.Collection`](#pkg_minimongo).
|
|
|
|
These helpers are implemented entirely on top of the public
|
|
interfaces at [`Meteor.deps`](#pkg_deps) and
|
|
[`Meteor.Collection`](#pkg_minimongo).
|
|
|
|
{{> api_box render}}
|
|
|
|
You provide a function that returns some DOM
|
|
elements. `Meteor.ui.render` gives you back some DOM elements that
|
|
automatically updates themselves in place. They are sensitive to changes
|
|
to session variables ([`Session.get`](#pkg_session_get) and to
|
|
database writes
|
|
([<code><i>collection</i>.update</code>](#pkg_minimongo_update), etc.)
|
|
|
|
The auto-updating elements are returned as a `DocumentFragment`. Simply
|
|
insert this `DocumentFragment` anywhere in the DOM you like. Its
|
|
elements will update themselves automatically until they are taken
|
|
offscreen — specifically, until [`Meteor.flush`](#flush) is called
|
|
when the elements are not children of `document`.
|
|
|
|
`events` lets you quickly hook up some event handlers to
|
|
the DOM nodes. If you provide `event_data`, it will be
|
|
passed to event handlers in `this`.
|
|
|
|
`Meteor.ui.render` may be called recursively (that
|
|
is, `render_func` can call `Meteor.ui.render`.) If
|
|
that happens, each invocation of `render` works
|
|
independently — an change to a dependency of the
|
|
inner `render_func` will (correctly) not cause the
|
|
outer `render_func` to be recomputed.
|
|
|
|
Example:
|
|
|
|
// Show the number of users online.
|
|
var frag = Meteor.ui.render(function () {
|
|
return $("<p>There are " + Users.find({online: true}).length +
|
|
" users online.</p>");
|
|
});
|
|
document.body.appendChild(frag);
|
|
|
|
// Find all users that have been idle for a while, and mark them as
|
|
// offline. The count on the screen will automatically update.
|
|
Users.update({idleTime: {$gt: 30}}, {online: false});
|
|
|
|
// Show a counter, and let the user click to increase or decrease it.
|
|
Session.set("counter", 0);
|
|
var frag = Meteor.ui.render(function () {
|
|
return $('<div>Counter: ' + Session.get("counter") + ' ' +
|
|
'<span class="inc">Increase</span>' +
|
|
'<span class="dec">Decrease</span></div>');
|
|
}, {
|
|
'click .inc': function (event) {
|
|
Session.set("counter", Session.get("counter") + 1);
|
|
},
|
|
'click .dec': function (event) {
|
|
Session.set("counter", Session.get("counter") - 1);
|
|
}
|
|
});
|
|
document.body.appendChild(frag);
|
|
|
|
{{> api_box renderList}}
|
|
|
|
This is [`Meteor.ui.render`](#render) crossed
|
|
with [<code><i>collection</i>.find().observe()</code>](#observe).
|
|
It calls a render function repeatedly for each document in a
|
|
collection that matches a query.
|
|
|
|
If you attach event handlers, then when they are triggered, they
|
|
will receive the document in `this`.
|
|
|
|
Returns a `DocumentFragment` with the same semantics as in
|
|
[`Meteor.ui.render`](#render) — insert it
|
|
anywhere, it will automatically update itself, make sure it's on the
|
|
page before the next time you
|
|
call [`Meteor.flush`](#flush).
|
|
|
|
Example:
|
|
|
|
// List the titles of all of the posts that have the tag
|
|
// "frontpage". Keep the list updated as new posts are made, as tags
|
|
// change, etc. Let the user click a post to select it.
|
|
Session.set("selected", null);
|
|
var frag = Meteor.ui.renderList(Posts, {
|
|
render: function (post) {
|
|
var style = Session.equals("selected", post._id) ? "selected" : "";
|
|
// A real app would need to quote/sanitize post._name
|
|
return $('<div class="' + style + '">' + post._name + '</div>');
|
|
},
|
|
selector: {tags: "frontpage"},
|
|
events: {
|
|
'click': function (event) {
|
|
Session.set("selected", this._id);
|
|
}
|
|
}
|
|
});
|
|
document.body.appendChild(frag);
|
|
|
|
{{#api_box_inline eventmaps}}
|
|
|
|
Several functions take event maps. An event map is an object where
|
|
the properties specify a set of events to handle, and the values are
|
|
the handlers for those events. The property can be in one of several
|
|
forms:
|
|
|
|
<dl>
|
|
{{#dtdd "<em>eventname</em>"}}
|
|
Matches a particular type of event, such as 'click'
|
|
{{/dtdd}}
|
|
|
|
{{#dtdd "<em>eventname selector</em>"}}
|
|
Matches a particular type of event, but only when it appears on
|
|
an element that matches a certain CSS selector
|
|
{{/dtdd}}
|
|
|
|
{{#dtdd "<em>event1, event2</em>"}}
|
|
To handle more than one type of event with the same function, use a comma-separated list.{{/dtdd}}
|
|
|
|
</dl>
|
|
|
|
The handler function gets one argument, an object with information
|
|
about the event. It will receive some additional context data
|
|
in `this`, depending on the context (eg: with
|
|
[`Meteor.ui.renderList`](#renderList), you get the
|
|
document; with a Handlebars template, you get the data being used to
|
|
fill the template.)
|
|
|
|
{{#warning}}
|
|
Right now, the event handlers are wired up with jQuery, and the event
|
|
object you receive is a jQuery event object. We would like to change
|
|
this so that you are not required to use jQuery.
|
|
{{/warning}}
|
|
|
|
{{#warning}}
|
|
There are currently two bugs that will cause events to not get
|
|
delivered in certain circumstances. Both have easy workarounds:
|
|
|
|
* If an event is filtered by a selector, then it will never be
|
|
delivered to a top-level node in a template. The problem is that
|
|
selectors will never match the top-level node in a template. This
|
|
matches other libraries but can be counterintuitive. The workaround is
|
|
to remove the selector if possible, or else add a wrapper element at the
|
|
top of the template.
|
|
|
|
* If a template A inserts another template B at the top-level of the
|
|
template, then if B updates itself, it will no longer receive
|
|
events. The workaround is to add a wrapper element around B.
|
|
|
|
{{/warning}}
|
|
|
|
Example:
|
|
|
|
{
|
|
// Fires when any element is clicked
|
|
'click': function (event) { ... },
|
|
|
|
// Fires when any element with the 'accept' class is clicked
|
|
'click .accept': function (event) { ... },
|
|
|
|
// Fires when 'accept' is clicked, or a key is pressed
|
|
'keydown, click .accept': function (event) { ... }
|
|
}
|
|
|
|
{{/api_box_inline}}
|
|
|
|
<h2 id="meteordeps"><span>Meteor.deps</span></h2>
|
|
|
|
Meteor has a simple dependency tracking system, so that it it can
|
|
automatically rerender templates and such when [`Session`](#session)
|
|
variables are modified, or database queries change.
|
|
|
|
Unlike most other systems, you don't have to manually declare these
|
|
dependencies — it "just works". The mechanism is simple and
|
|
efficient. When you call a function that supports reactive updates
|
|
(say, a database query), it automatically saves the current
|
|
"invalidation context" object if any (say, the current template being
|
|
rendered.) Later, when the data changes, it can "invalidates" this
|
|
context (tell the template to rerender itself.) The whole
|
|
implementation is about 50 lines of code.
|
|
|
|
Developers, particularly package authors, can use *invalidation
|
|
contexts* to implement additional reactive data sources or to write
|
|
functions that automatically register dependencies on reactive data
|
|
sources.
|
|
|
|
{{> api_box Context }}
|
|
|
|
Create an invalidation context by calling this constructor, then run
|
|
some code inside the context with [`run`](#run). Finally, register a
|
|
callback with [`on_invalidate`](#on_invalidate) that will get called
|
|
when the code you run wants to signal that it should be rerun.
|
|
|
|
Code can see if it's running inside an invalidation context by reading
|
|
the [`Meteor.deps.Context.current`](#current) global variable, which
|
|
will be the currently active context, or `null` if it's not being run
|
|
from inside a context. If it wants to participate in the reactivity
|
|
system, it should save this context away, and later call the
|
|
[`invalidate`](#invalidate) method on the context when it wants to
|
|
signal that something has changed. If it does this, it should also use
|
|
[`on_invalidate`](#on_invalidate) to set up a cleanup function so that
|
|
it can know when to stop listening for changes.
|
|
|
|
Invalidation contexts have an attribute `id` which is a unique positive
|
|
integer. You're free to add any other attributes you like to the
|
|
invalidation context for your own convenience, as long as they don't
|
|
start with an underscore.
|
|
|
|
{{> api_box current }}
|
|
|
|
This is a global variable that is set by [`run`](#run).
|
|
|
|
If you have a background in Lisp or programming language theory, you
|
|
might think of it as a dynamically scoped ("special") variable. (That
|
|
just means that [`run`](#run) sets it, runs some user-supplied code, and
|
|
then restores its previous value.)
|
|
|
|
{{> api_box run }}
|
|
|
|
This function simply sets [`Meteor.deps.Context.current`](#current) to
|
|
this invalidation context, runs `func`, and then restores it to its
|
|
previous value. It returns the result of calling `func`.
|
|
|
|
It's fine for `run` to be called recursively. `current` will return the
|
|
innermost context.
|
|
|
|
{{> api_box on_invalidate }}
|
|
|
|
If this context hasn't been invalidated yet, adds `callback` to the list
|
|
of callbacks that will be called when [`invalidate`](#invalidate) is
|
|
called. If the context has already been invalidated, call `callback`
|
|
immediately.
|
|
|
|
Typically this function will have two kinds of callers:
|
|
|
|
* The function that creates the invalidation context will use the
|
|
`on_invalidate` callback as a signal to rerun the code in the context,
|
|
to see what new value it returns. In order to rerun the code, it'll
|
|
create a fresh invalidation context and reregister its `on_invalidate`
|
|
callback on that new context. When that context is invalidated the
|
|
cycle will repeat.
|
|
|
|
* Functions that are sources of reactive data will save
|
|
[`Meteor.deps.Context.current`](#current) into some kind of list of
|
|
listeners. They'll use the `on_invalidate` callback to remove the
|
|
context from their listener list.
|
|
|
|
Example:
|
|
|
|
// Print the current username to the console. Will re-run every time
|
|
// the username changes.
|
|
var log_current_username = function () {
|
|
var update = function () {
|
|
var ctx = new Meteor.deps.Context(); // invalidation context
|
|
ctx.on_invalidate(update); // rerun update() on invalidation
|
|
ctx.run(function () {
|
|
var username = Session.get("username");
|
|
console.log("The current username is now", username);
|
|
});
|
|
};
|
|
update();
|
|
};
|
|
|
|
// Example use. Since Session is reactive (meaning that it knows how
|
|
// to use Meteor.deps to record its dependencies), log_current_username
|
|
// will be re-run whenever Session.set is called for "username".
|
|
Session.set("username", "matt");
|
|
log_current_username(); // prints matt
|
|
Session.set("username", "geoff"); // immediately prints geoff
|
|
Session.set("username", "geoff"); // won't print: Session won't trigger
|
|
// invalidation if the value is the same.
|
|
|
|
{{> api_box invalidate }}
|
|
|
|
If this function has already been called on this context, it does
|
|
nothing (a mathematician would say that it is "idempotent.") Otherwise
|
|
it calls each [`on_invalidate`](#on_invalidate) function registered on
|
|
the context.
|
|
|
|
The functions aren't called immediately — instead, they will be
|
|
called the next time you call [`Meteor.flush`](#flush). This function
|
|
just adds the context to the flush list and is guaranteed to do nothing
|
|
else just yet.
|
|
|
|
If you don't call [`Meteor.flush`](#flush) explicitly, it will be called
|
|
for you automatically when your code is done running (by setting a
|
|
`setTimeout` timer with a delay of zero.)
|
|
|
|
Example:
|
|
|
|
// Create a simple class called Weather that tracks the current
|
|
// temperature. The temperature can be read reactively.
|
|
var Weather = function () {
|
|
this.temperature = 60;
|
|
this.listeners = {};
|
|
};
|
|
|
|
// Function to get the temperature (and, if called in a reactive
|
|
// context, start listening for changes to the temperature.)
|
|
Weather.prototype.get_temp = function () {
|
|
var context = Meteor.deps.Context.current;
|
|
|
|
// If we're inside a context, and it's not yet listening to
|
|
// temperature changes..
|
|
if (context && !this.listeners[context.id])
|
|
// .. add it to our list of contexts that care about the temperature ..
|
|
this.listeners[context.id] = context;
|
|
|
|
// .. and remember to take it off our list when it goes away.
|
|
var self = this;
|
|
context.on_invalidate(function () {
|
|
delete self.listeners[context.id];
|
|
});
|
|
}
|
|
|
|
// return the current temperature, whether or not in a reactive context.
|
|
return this.temperature;
|
|
};
|
|
|
|
// Function to set the temperature, and notify anyone that might be
|
|
// listening for temperature updates.
|
|
Weather.prototype.set_temp = function (new_temp) {
|
|
if (this.temperature === new_temp)
|
|
return; // don't want to trigger invalidation if there's no change.
|
|
|
|
// Set the temperature
|
|
this.temperature = new_temp;
|
|
|
|
// Notify any contexts that care about temperature changes
|
|
for (var context_id in this.listeners)
|
|
// This will trigger the on_invalidate function above, but not
|
|
// immediately -- only when Meteor.flush() is called, or at the end
|
|
// of the event loop. So we know that this.listeners will be
|
|
// emptied, but it won't change while we're trying to loop over it.
|
|
this.listeners[context_id].invalidate();
|
|
};
|
|
|
|
<h2 id="meteor_environmentvariable"><span>Meteor.EnvironmentVariable</span></h2>
|
|
|
|
{{> api_box EnvironmentVariable}}
|
|
{{> api_box environmentVariable_get}}
|
|
{{> api_box environmentVariable_withValue}}
|
|
{{> api_box bindEnvironment}}
|
|
|
|
{{/better_markdown}}
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<template name="api_box">
|
|
<div class="api {{bare}}">
|
|
<h3 id="{{id}}">
|
|
<a class="name selflink" href="#{{id}}">{{{name}}}</a>
|
|
{{#if locus}}
|
|
<span class="locus">{{locus}}</span>
|
|
{{/if}}
|
|
</h3>
|
|
|
|
<div class="desc">
|
|
{{#each descr}}{{#better_markdown}}{{{this}}}{{/better_markdown}}{{/each}}
|
|
</div>
|
|
|
|
{{#if args}}
|
|
<h4>Arguments</h4>
|
|
{{> api_box_args args}}
|
|
{{/if}}
|
|
|
|
{{#if options}}
|
|
<h4>Options</h4>
|
|
{{> api_box_args options}}
|
|
{{/if}}
|
|
|
|
{{#if body}}
|
|
{{#better_markdown}}{{{body}}}{{/better_markdown}}
|
|
{{/if}}
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<template name="api_box_args">
|
|
<dl class="args">
|
|
{{#each this}}
|
|
<dt><span class="name">{{{name}}}</span>
|
|
<span class="type">
|
|
{{#if type_link}}
|
|
<a href="#{{type_link}}">{{type}}</a>
|
|
{{else}}
|
|
{{type}}
|
|
{{/if}}
|
|
</span></dt>
|
|
<dd>{{#better_markdown}}{{{descr}}}{{/better_markdown}}</dd>
|
|
{{/each}}
|
|
</dl>
|
|
</template>
|
|
|
|
|
|
<template name="api_section_helper">
|
|
<h2 id="{{id}}"><a href="#{{id}}" class="selflink"><span>{{name}}</span></a></h2>
|
|
</template>
|
|
|
|
|