Files
meteor/docs/client/api.html
2012-04-18 20:46:47 -07:00

1500 lines
56 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="core"><span>Meteor Core</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().count() === 0) {
Rooms.insert({name: "Initial room"});
}
});
}
<h2 id="publishandsubscribe"><span>Publish and subscribe</span></h2>
These functions control how Meteor servers publish sets of records and
how clients can subscribe to those sets.
{{> api_box publish}}
To publish records to clients, call `Meteor.publish` on the server with
two parameters: the name of the record set, and a *publish function*
that Meteor will call each time a client subscribes to the name.
Publish functions can return a
[`Collection.Cursor`](#meteor_collection_cursor), in which case Meteor
will publish that cursor's documents.
// server: publish the rooms collection, minus secret info.
Meteor.publish("rooms", function () {
return Rooms.find({}, {fields: {secret_info: false}});
});
Otherwise, the publish function can <i>set</i> and <i>unset</i>
individual record attributes on a client, use these methods provided by
`this` in your publish function.
<!-- discuss complete -->
In particular, 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.
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();
});
});
{{#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}}
{{> api_box subscription_set}}
{{> api_box subscription_unset}}
{{> api_box subscription_complete}}
{{> api_box subscription_flush}}
{{> api_box subscription_onStop}}
If you call [`observe`](#observe) in your publish handler, this
is the place to stop the observes.
{{> api_box subscription_stop}}
{{> api_box subscribe}}
When you subscribe to a record set, it tells the server to send records
to the client. The client stores these records 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 records until ...
...
Players = new Meteor.Collection("players");
If more than one subscription sends conflicting values for an attribute
(same collection name, document ID, and attribute name), then the value
on the client will be that from the *first* subscription the client
activated. (Even if it is not the first to send the duplicated
attribute.)
If all of the attributes in a 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}}
`func` will be run immediately, and while it runs,
records will be kept of the subscriptions it makes
(via [`Meteor.subscribe`](#meteor_subscribe)) and the data it uses
(including calls to [`Session.get`](#session_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.
`Meteor.autosubscribe` will automatically stop the old subscription.
It's not necessary to call `stop` on subscriptions made from inside
`Meteor.autosubscribe`.
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");});
});
<h2 id="methods_header"><span>Methods</span></h2>
Methods are remote functions that Meteor clients can invoke.
{{> 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";
},
bar: function () {
// .. do other stuff ..
return "baz";
}
});
Calling `methods` on the server defines functions that can be called
remotely by clients. They should return a value or throw an exception.
Inside your method invocation, `this` 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.
Calling `methods` on the client defines *stub* functions associated with
server methods of the same name. You don't have to define a stub for
your method if you don't want to. In that case, method calls are just
like remote procedure calls in other systems, and you'll have to wait
for the results from the server.
If you do define a stub, when a client invokes a server method it will
also run its stub in parallel. On the client, the return value of a
stub is ignored. 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. If a stub throws an
exception it will be logged to the console.
{{> 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 error}}
If you want to return an error from a method, throw an exception.
Methods can throw any kind of exception. But `Meteor.Error` is the only
kind of error that a server will send to the client. If a method
function throws a different exception, then it will be mapped to
`Meteor.Error(500, "Internal server error")` on the wire.
{{> api_box meteor_call}}
This is how to invoke a method. It will run the method on the server.
If a stub is available, it will also run the stub on the client.
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 exception. When the method is complete (which may or
may not happen before `Meteor.call` returns), the callback will be
called with two arguments: `error` and `result`. If an error was thrown,
then `error` will be the exception object. Otherwise, `error` will be
undefined and the return value (possibly undefined) will be in `result`.
// async call
Meteor.call('foo', 1, 2, function (error, 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 call
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 top level), 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 meteor_apply}}
`Meteor.apply` is just like `Meteor.call`, but it allows the
arguments to be passed as an array.
<h2 id="connections"><span>Server connections</span></h2>
These functions manage and inspect the network connection between the
Meteor client and server.
{{> 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 and
method invocations will be queued up until the connection is
reestablished.</dd>
<dt><span class="name">status</span>
<span class="type">String</span></dt>
<dd>Describes the current reconnection status. The possible
values are <code>connected</code> (the connection is up and
running), <code>connecting</code> (disconnected and trying to open a
new connection), and <code>waiting</code> (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
<code>retry_time - (new Date()).getTime()</code>. This key will
be set only when <code>status</code> is <code>waiting</code>.
</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="#meteor_deps">invalidation
context</a> to get realtime updates.
{{> api_box reconnect}}
{{> api_box connect}}
To call methods on another Meteor application or subscribe to its data
sets, call `Meteor.connect` with the URL of the application.
`Meteor.connect` returns an object which provides:
* `subscribe`
* `methods` (to define stubs)
* `call`
* `apply`
* `status`
* `reconnect`
When you call `Meteor.subscribe`, `Meteor.status`, `Meteor.call`, and
`Meteor.apply`, you are using a connection back to that default
server.
{{#warning}}
In this release, `Meteor.connect` can only be called on the client.
Servers can not yet connect to other servers.
{{/warning}}
<h2 id="collections"><span>Collections</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 &mdash; 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 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` 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. 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);
{{#warning}}
In this release, 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 &mdash; 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.
{{/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`](#meteor_ui_render),
[`Meteor.autosubscribe`](#meteor_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}}
Equivalent to `find(selector, options).fetch()[0]`.
{{> 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, `insert` returns
immediately. Once the insert completes (or fails), the callback is
called with error and result arguments, same as for
[`methods`](#methods_header).
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 to
the console. If you provide a callback, Meteor will call that function
with the error or result of the server's insert.
Example:
var groceries_id = Lists.insert({name: "Groceries"});
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 `modifier` (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`.
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, `update` returns
immediately. Once the update completes, the callback is called with a
single error argument in the case of failure, or no arguments if the
update was successful.
On the client, `update` never blocks. If you do not provide a callback
and the update fails on the server, then Meteor will log a warning to
the console. If you provide a callback, Meteor will call that function
with an error argument if there was an error, or no arguments if the
update was successful.
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});
{{#warning}}
The Mongo `upsert` feature is not implemented.
{{/warning}}
{{> 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, `remove` returns
immediately. Once the remove completes, the callback is called with a
single error argument in the case of failure, or no arguments if the
update was successful.
On the client, `remove` never blocks. If you do not provide a callback
and the remove fails on the server, then Meteor will log a warning to
the console. If you provide a callback, Meteor will call that function
with an error argument if there was an error, or no arguments if the
update was successful.
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);
<h2 id="meteor_collection_cursor"><span>Cursors</span></h2>
To create a cursor, use [`find`](#find). To access the documents in a
cursor, use [`forEach`](#foreach), [`map`](#map), or [`fetch`](#fetch).
{{> api_box cursor_foreach}}
When called in a reactive context, `forEach` registers dependencies on
the matching documents.
{{> api_box cursor_map}}
When called in a reactive context, `map` registers dependencies on
the matching documents.
{{> api_box cursor_fetch}}
When called in a reactive context, `fetch` registers dependencies on
the matching documents.
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.count() + " posts with " +
"scores greater than 10</p>";
});
document.body.appendChild(frag);
Unlike the other functions, `count` registers a dependency only 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}}
The `forEach`, `map`, or `fetch` methods can only be called once on a
cursor. To access the data in a cursor more than once, use `rewind` to
reset the cursor.
{{> 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)"}}
A new document entered 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.**
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_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 "popular"
{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 modifier 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`](#session_get)`("current_list")`
from inside a template, the template will automatically be rerendered
whenever [`Session.set`](#session_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`](#meteor_deps) 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:
<template name="posts_view">
{{dstache}}! 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. }}
{{dstache}}#each posts}}
{{dstache}}> post_item }}
{{dstache}}/each}}
</{{! }}template>
<template name="post_item">
<div class="{{dstache}}post_class}}">{{dstache}}title}}</div>
</{{! }}template>
///// in JS file
Template.posts_view.posts = function() {
return Posts.find();
};
Template.post_item.post_class = function() {
return Session.equals("selected_post", this._id) ?
"selected" : "";
};
Template.post_item.events = {
'click': function() {
Session.set("selected_post", this._id);
}
};
// Using Session.equals here means that when the user clicks
// on an item and changes the selection, only the newly selected
// and the newly unselected items are re-rendered.
//
// If Session.get had been used instead of Session.equals, then
// when the selection changed, all the items would be re-rendered.
<h2 id="meteor_ui"><span>Meteor.ui</span></h2>
`Meteor.ui` provides building blocks for creating reactive UIs out of strings of
HTML, making it easy to create DOM elements that update
automatically as data changes in
[`Session`](#session) variables or in a
[`Meteor.Collection`](#meteor_collection). Meteor's built-in templates already use these functions, but if you prefer a different way of generating HTML,
are integrating a new template language with Meteor, or need to compose a reactive
snippet of HTML on the fly, then this package has what you need.
This package is implemented on top of [`Meteor.deps`](#meteor_deps), which provides the
data dependency tracking and invalidation system, while `Meteor.ui` contributes
the ability to turn HTML into DOM elements, keep track of regions of the DOM that
need updating, and patch old DOM content with new, recalculated content.
{{> api_box render}}
`Meteor.ui.render` creates a `DocumentFragment` (a sequence of DOM
nodes) that automatically updates in realtime. You pass in
`html_func`, a function that returns an HTML
string. `Meteor.ui.render` calls your function and turns the output
into DOM nodes. Meanwhile, it tracks the data that was used when
`html_func` ran, and automatically wires up callbacks so that whenever
any of the data changes, `html_func` is re-run and the DOM nodes
are updated in place.
Insert the returned `DocumentFragment` directly into the DOM wherever
you would like it to appear. The inserted nodes will continue to
update until they are taken off the screen. Then they will be
automatically cleaned up. For the details, see
[`Meteor.flush`](#meteor_flush).
You can also hook up events to the rendered DOM nodes using the
`events` option. If you provide `event_data`, it will be passed to
event handlers in `this`. (See [Event Maps](#eventmaps).)
When render replaces DOM elements because data changed, it can leave
input elements undisturbed so that focus is preserved, text entered
into fields isn't lost, and so forth. To activate this feature, give
each such element a unique `id`, or give it a unique `name` attribute
inside the nearest enclosing element with an `id`.
If you want a region of your HTML to be able to update independently
of the other HTML around it, wrap it in [`Meteor.ui.chunk`](#meteor_ui_chunk).
`Meteor.ui.render` tracks the data dependencies of `html_func` by
running it in a reactive context, so it can respond to changes in any
reactive data sources used by that function. For more information, or
to learn how to make your own reactive data sources, see
[Reactivity](#reactivity).
<!--
Meteor.ui.render runs html_func in a reactive context, then returns a
DocumentFragment that can be inserted anywhere in a Document and that
will automatically update itself in place whenever the context is
invalidated. The updating will stop if the nodes in the fragment are
ever not on the screen (that is, children of `window.document`) when
Meteor.flush runs.
During an update, if a node has a unique id, or if it has a name that
is unique among the descendants of the nearest enclosing parent that
has an id, then it will be "patched" (updated in place, rather than
replaced), meaning that focus will be preserved, the text in <input>
elements will be not be lost, etc.
By default, Meteor.ui.render puts the entire output of `html_func` in
a single invalidation context. For finer control of rerendering, you
can use Meteor.ui.chunk to create a nested tree of invalidation
contexts.
[events]
[more?]
-->
Example:
// Show the number of users online.
var frag = Meteor.ui.render(function () {
return "<p>There are " + Users.find({online: true}).count() +
" 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>';
}, { events:
{
'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 chunk}}
When generating HTML from a function passed to [`Meteor.ui.render`](#meteor_ui_render), you can use `Meteor.ui.chunk` to mark a substring of the HTML as separately reactive. If the data used to generate that substring changes, only the elements corresponding to that substring will be updated, not the elements before and after it.
Like `render`, `Meteor.ui.chunk` takes a function `html_func` that returns a HTML string. It calls that function, records the data that the function used (using a [reactive context](#meteor_ui_deps)), and arranges to rerun the function as necessary whenever the data changes. What's different from `render` is that it returns another HTML string, not a DocumentFragment. So, unlike `render`, `chunk` may be nested as deeply as you like, for example to render nested views or subtemplates.
`chunk` can also be used to attach events to part of an HTML string, in much the same way that they could be attached to DOM elements. When the string is parsed into DOM elements by `render`, the event handlers will automatically be hooked up.
`chunk` works by creating a unique ID for the chunk, wrapping the HTML string in a comment that calls out that ID, and adding an entry to the chunk table for the current invocation of `render`. As `render` turns the HTML string into DOM nodes, it pulls out the comments and wires up the appropriate callbacks and pointers. On the other hand, if there is no current invocation of `render`, `chunk` just passes the string through unchanged. (In this case, if an event map is provided, it is ignored.)
The contents of a chunk must be balanced HTML tags; the string returned by
`html_func` cannot start or end inside a tag or tag attribute.
Example:
Meteor.startup(function() {
Session.set("greeting", "Hello");
Session.set("target", "World");
// Render two chunks that will be tracked and updated independently.
document.body.appendChild(
Meteor.ui.render(function() {
return "<div>" +
Meteor.ui.chunk(function() { return Session.get("greeting"); }) +
" " +
Meteor.ui.chunk(function() { return Session.get("target"); }) +
"</div>";
}));
// Updates "Hello" to "Goodbye" without touching "World"
Session.set("greeting", "Goodbye");
// Updates "World" to "Ralph" without touching "Goodbye"
Session.set("target", "Ralph");
});
// Every two seconds, alternates between Goodbye Ralph and Goodbye World.
// If you select the word Goodbye or part of it, the selection stays on
// update, because the text node is not being replaced.
Meteor.setInterval(function() {
if (Session.get("target") === "Ralph") {
Session.set("target", "World");
} else {
Session.set("target", "Ralph");
}
}, 2000);
{{> api_box listChunk}}
`listChunk` is like `chunk`, but instead of creating one chunk, it creates several, one for each record in the results of a database query.
It keeps the chunks updated as the results of the database query change. For example, if a new record is created in the database that matches the query, a new chunk is inserted. If the query is sorted, and a database record changes, and the change causes it to move in the sort order, then the chunk is moved appropriately.
If you provide `else_func`, then whenever the query returns no results, it will be called to render alternative content. You might use this to show a message like "No records match your query."
You can provide an `events` option to attach a set of event handlers to each chunk that `listChunk` creates. When events fire, `this` in the event handler will contain the database record that triggered the event.
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.render(function() {
return Meteor.ui.listChunk(Posts.find({tags: "frontpage"}),
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>';
},
{ events:
{
'click': function (event) {
Session.set("selected", this._id);
}
}
});
});
document.body.appendChild(frag);
{{> 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 &mdash; 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 by calling
[`Meteor.ui.render`](#meteor_ui_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`](#meteor_ui_render) that works through invalidations.
{{#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.listChunk`](#meteor_ui_listchunk), 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="timers"><span>Timers</span></h2>
Meteor uses global environment variables
to keep track of things like the current request's user. To make sure
these variables have the right values, you need to use
`Meteor.setTimeout` instead of `setTimeout` and `Meteor.setInterval`
instead of `setInterval`.
These functions work just like their native JavaScript equivalents.
You'll get an error if you call the native function.
{{> api_box setTimeout}}
{{> api_box setInterval}}
{{> api_box clearTimeout}}
{{> api_box clearInterval}}
<h2 id="meteor_deps"><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 &mdash; 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 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 &mdash; instead, they will be
called the next time you call [`Meteor.flush`](#meteor_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`](#meteor_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();
};
{{> 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.)
<h2 id="meteor_http"><span>Meteor.http</span></h2>
{{> api_box httpcall}}
This function initiates an HTTP request to a remote server. It returns
a result object with the contents of the HTTP response. The result
object is detailed below.
On the server, this function can be run synchronously by omitting
the callback argument. In this case, the results are returned
to the caller once the request completes. This is useful when making
server-to-server HTTP API calls from with Meteor methods. You can easily
delay return method success or failure to the client until after the HTTP
call has finished. In this case, consider running
[`this.unblock()`](#method_unblock) to allow other methods to start.
The `url` argument can begin with `"http://"` or `"https://"`. On the
client, you can also use relative URLs, on the server only absolute URLs
are allowed. `url` can include a query string, but it will be
overwritten if the `query` option is passed. The `params` option will be
encoded and either added to the URL (GET requests and POST requests that
already have `content` specified) or to the body of the request (POST
requests without `content` specified)
The callback receives two arguments, `error` and `result`. The `error`
argument will contain an Error if the request fails in any way,
including a bad HTTP status code. The result object is always
defined. When run in synchronous mode, the `result` is returned from the
function, and the `error` value is a stored as a property in `result`.
Contents of the result object:
<dl class="objdesc">
<dt><span class="name">statusCode</span>
<span class="type">Number or null</span></dt>
<dd>Numeric HTTP result status code, or null on error.</dd>
<dt><span class="name">headers()</span>
<span class="type">Object</span></dt>
<dd>Return a dictionary of HTTP headers from the response.</dd>
<dt><span class="name">content()</span>
<span class="type">String</span></dt>
<dd>Return the body of the HTTP response as a string.</dd>
<dt><span class="name">data()</span>
<span class="type">Object</span></dt>
<dd>Return the body of the document parsed as a JSON object</dd>
<dt><span class="name">error</span>
<span class="type">Error</span></dt>
<dd>Error object if the request failed. Matches the `error` callback parameter.</dd>
</dl>
Example server method:
Meteor.methods({check_twitter: function (user_id) {
this.unblock();
var result = Meteor.http.call("GET", "http://api.twitter.com/xxx",
{params: {user: user_id}});
if (result.statusCode === 200)
return true
return false;
}});
Example asynchronous HTTP call:
Meteor.http.call("POST", "http://api.twitter.com/xxx",
{data: {some: "json", stuff: 1}},
function (error, result) {
if (result.statusCode === 200) {
Session.set("twizzled", true);
}
});
{{/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>