My edits to the basic docs.

This commit is contained in:
Ben Newman
2014-10-24 18:16:06 -04:00
parent 51160f4e0b
commit 2e90ec853a
5 changed files with 211 additions and 159 deletions

View File

@@ -3,35 +3,37 @@
<h2 id="collections"><span>Collections</span></h2>
Meteor stores data in *collections*. JavaScript objects stored in collections
are called `documents`. To get started, declare a collection with `new
Mongo.Collection`.
are called `documents`. To get started, declare a collection with
`new Mongo.Collection`.
{{> autoApiBox "Mongo.Collection"}}
Calling this constructor creates a collection object which acts just like a
MongoDB collection. 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.
Calling the `Mongo.Collection` constructor creates a collection object
which acts just like a MongoDB collection. 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.
Since client code and server code can both access the same collection using the
same API, it's usually best to declare persistent collections as global
variables in a JavaScript file that's present on the client and the server.
To allow both client code and server code to access the same collection
using the same API, it's usually best to declare collections as global
variables in a JavaScript file that's present on both client and server.
Here's an example of declaring a collection:
Here's an example of declaring two named, persistent collections as global
variables:
```
// In a JS file that's loaded on the client and server
// In a JS file that's loaded on the client and the server
Posts = new Mongo.Collection("posts");
Comments = new Mongo.Collection("comments");
```
If you pass null as the name, then you're creating a local collection. It's not
synchronized anywhere; it's just a local collection of JavaScript objects that
supports Mongo-style find, insert, update, and remove operations.
If you pass `null` as the name, then you're creating a local
collection. Local collections are not synchronized between the client and
the server; they are just temporary collections of JavaScript objects that
support Mongo-style `find`, `insert`, `update`, and `remove` operations.
By default, Meteor automatically publishes every document in your collection to
each connected client. To turn this behavior off, remove the autopublish
package:
By default, Meteor automatically publishes every document in your
collection to each connected client. To disable this behavior, you must
remove the `autopublish` package:
```
$ meteor remove autopublish
@@ -45,24 +47,28 @@ Use `findOne` or `find` to retrieve documents from a collection.
{{> autoApiBox "Mongo.Collection#findOne"}}
This method lets you retrieve a specific document from your collection. It is
simplest to call it with a document id:
This method lets you retrieve a specific document from your
collection. The `findOne` method is most commonly called with a specific
document `_id`:
```
var post = Posts.findOne(postId);
```
You can also call `findOne` with a Mongo selector, which is an object that
specifies a set of rules for the document you're looking for:
However, you can also call `findOne` with a Mongo selector, which is an
object that specifies a required set of attributes of the desired
document. For example, this selector
```
// this selector
var post = Posts.findOne({
createdBy: "12345",
title: {$regex: /first/}
});
```
// will match this document
will match this document
```
{
createdBy: "12345",
title: "My first post!",
@@ -72,17 +78,20 @@ var post = Posts.findOne({
You can read about MongoDB query operators such as `$regex`, `$lt` (less than),
`$text` (text search), and more in the [MongoDB
documentation](http://docs.mongodb.org/manual/reference/operator/query/). One
interesting thing to note is that Mongo selectors also match items in arrays.
For example:
documentation](http://docs.mongodb.org/manual/reference/operator/query/).
One useful behavior that might not be obvious is that Mongo selectors also
match items in arrays. For example, this selector
```
// this selector
Post.findOne({
tags: "meteor"
});
```
// will match this document
will match this document
```
{
title: "I love Meteor",
createdBy: "242135223",
@@ -91,18 +100,22 @@ Post.findOne({
```
The `findOne` method is reactive just like [`Session.get`](#session_get),
meaning if you use it inside a [template helper](#template_helpers) or a
[`Tracker.autorun`](#tracker_autorun), it will automatically rerender
the view or rerun the computation if the returned document changes. This also
means that sometimes `findOne` will return `null` if the document is removed
from the collection, so you should be prepared to accept that value.
meaning that, if you use it inside a [template helper](#template_helpers)
or a [`Tracker.autorun`](#tracker_autorun) callback, it will automatically
rerender the view or rerun the computation if the returned document
changes.
Note that `findOne` will return `null` if it fails to find a matching
document, which often happens after the document has been removed from the
collection, so you should be prepared to handle `null` values.
{{> autoApiBox "Mongo.Collection#find"}}
The `find` method is similar to `findOne`, but instead of returning a single
document it returns a MongoDB cursor. A cursor is a special object that refers
to the documents that might be returned from a query. You can pass a cursor into
a template helper anywhere you could pass an array:
The `find` method is similar to `findOne`, but instead of returning a
single document it returns a MongoDB *cursor*. A cursor is a special
object that represents a list of documents that might be returned from a
query. You can pass a cursor into a template helper anywhere you could
pass an array:
```
Template.blog.helpers({
@@ -124,8 +137,8 @@ Template.blog.helpers({
</template>
```
If you want to actually retrieve all of the documents from a cursor, call
`.fetch()` like this:
When you want to retrieve the current list of documents from a cursor,
call the cursor's `.fetch()` method:
```
// get an array of posts
@@ -154,8 +167,8 @@ Posts.insert({
Every document in every `Mongo.Collection` has an `_id` field. It must be
unique, and is automatically generated if you don't provide one. The `_id`
field should be used to refer to a specific document, and can be used to
retrieve that document using [`collection.findOne`](#findOne).
field can be used to retrieve a specific document using
[`collection.findOne`](#findOne).
{{> autoApiBox "Mongo.Collection#update"}}
@@ -165,13 +178,14 @@ changes should be made to the matched documents. Watch out - unless you use
an operator like `$set`, `update` will simply replace the entire matched
document with the modifier.
Here's an example of setting the `content` field on all posts whose titles
contain the word "first":
```
// Sets the content field on all posts
// that have 'first' in the title
Posts.update({
title: {$regex: /first/}
}, {
$set: { content: "Tomorrow will be a great day." }
$set: {content: "Tomorrow will be a great day."}
});
```
@@ -179,40 +193,40 @@ You can read about all of the different operators that are supported in the
[MongoDB documentation](http://docs.mongodb.org/manual/reference/operator/update/).
There's one catch: when you call `update` on the client, you can only find
documents by theid `_id` field. To use all of the possible selectors, you must
call `update` in server code or from a [method](#meteor_methods).
documents by their `_id` field. To use all of the possible selectors, you
must call `update` in server code or from a [method](#meteor_methods).
{{> autoApiBox "Mongo.Collection#remove"}}
This method uses the same selectors as `find` and `update`, and removes any
documents that match the selector from the database. Use `remove` carefully -
there's no way to get that data back.
Just like with `update`, you can only remove documents by using their `_id`
field unless `remove` is called in server code or from a
[method](#meteor_methods).
This method uses the same selectors as `find` and `update`, and removes
any documents that match the selector from the database. Use `remove`
carefully &mdash; there's no way to get that data back.
As with `update`, client code can only remove documents by `_id`, whereas
server code and [methods](#meteor_methods) can remove documents using any
selector.
{{> autoApiBox "Mongo.Collection#allow"}}
In newly created apps, Meteor allows almost any calls to `insert`, `update`, and
`remove` from any client or server code. This is because apps started with
`meteor create` include the `insecure` package by default to speed up
`meteor create` include the `insecure` package by default to simplify
development. Obviously, if any user could change the database whenever they
wanted it would be bad for security, so we should remove `insecure` and specify
some permissions rules.
wanted it would be bad for security, so it is important to remove the
`insecure` package and specify some permissions rules:
```
$ meteor remove insecure
```
Once we have removed this package, we can use the `allow` and `deny` methods to
control who can perform which operations on the database. By default, all
operations on the client are denied, so we need to add some `allow` rules.
Keep in mind that server code and code inside [methods](#meteor_methods) is
not affected by `allow` and `deny` - these rules only apply when `insert`,
`update`, and `remove` are called from untrusted client code.
Once you have removed the `insecure` package, use the `allow` and `deny`
methods to control who can perform which operations on the database. By
default, all operations on the client are denied, so we need to add some
`allow` rules. Keep in mind that server code and code inside
[methods](#meteor_methods) are not affected by `allow` and `deny` &mdash;
these rules only apply when `insert`, `update`, and `remove` are called
from untrusted client code.
For example, we might say that users can only create new posts if the
`createdBy` field matches the ID of the current user, so that users can't
@@ -234,8 +248,9 @@ Posts.allow({
});
```
There are three possible callbacks, that get different arguments. The first
argument is always the `_id` of the logged in user.
The `allow` method accepts three possible callbacks: `insert`, `remove`,
and `update`. The first argument to all three callbacks is the `_id` of
the logged in user, and the remaining arguments are as follows:
1. `insert(userId, document)`
@@ -259,9 +274,10 @@ argument is always the `_id` of the logged in user.
{{> autoApiBox "Mongo.Collection#deny"}}
Deny lets you override parts of your `allow` rules. While only one of your
`allow` callbacks has to return true to allow a modification, _every one_ of
your `deny` callbacks has to return false for the database change to happen.
The `deny` method lets you selectively override your `allow` rules. While
only one of your `allow` callbacks has to return true to allow a
modification, _every one_ of your `deny` callbacks has to return false for
the database change to happen.
For example, if we wanted to override part of our `allow` rule above to exclude
certain post titles:
@@ -276,4 +292,4 @@ Posts.deny({
});
```
{{/template}}
{{/template}}

View File

@@ -10,9 +10,9 @@ Methods can return values and throw errors.
{{> autoApiBox "Meteor.methods"}}
Calling `methods` on the server defines functions that can be called remotely by
clients. Here's an example of a method that checks its arguments and throws an
error:
Calling `Meteor.methods` on the server defines functions that can be
called remotely by clients. Here's an example of a method that checks its
arguments and throws an error:
```
// On the server
@@ -38,18 +38,12 @@ Meteor.methods({
});
```
The [`check`](#check) function is a convenient way to enforce the expected
[types and structure](#matchpatterns) of method arguments.
Inside your method definition, `this` is bound to a method invocation object,
which has a few useful properties:
* `userId`: the id of the current user.
* `unblock`: when called, allows the next method from this client to
begin running. Useful if this method is doing something that takes a long time,
like making an API call.
* `isSimulation`: true if this code is inside a method stub.
Since methods usually expect particular types as arguments,
use [`check`](#check) to ensure your method arguments have
the correct [types and structure](#matchpatterns).
which has several useful properties, including `this.userId`, which
identifies the currently logged-in user.
### Latency Compensation

View File

@@ -6,13 +6,14 @@
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
What's special about `Session` is that it's _reactive_. If you call
`Session.get("myKey")` in a [template helper](#template_helpers) or inside
[`Tracker.autorun`](#tracker_autorun), the relevant part of the template will
automatically be re-rendered whenever `Session.set("myKey", newValue)` is
be re-rendered automatically whenever `Session.set("myKey", newValue)` is
called.
{{> autoApiBox "Session.set"}}
<!-- XXX The Session.set API box is a little wonky -->
{{> autoApiBox "Session.get"}}
@@ -40,9 +41,9 @@ Session.set("enemy", "Eurasia");
// Page will change to say "We've always been at war with Eurasia"
```
Using `Session` gives us our first taste of "reactivity", the idea that the view
should update automatically when necessary, without us having to call a "render"
Using `Session` gives us our first taste of _reactivity_, the idea that the view
should update automatically when necessary, without us having to call a `render`
function manually. In the next section, we will learn how to use Tracker, the
lightweight library that makes this possible in Meteor.
{{/template}}
{{/template}}

View File

@@ -8,15 +8,15 @@ which parts of the page are displayed.
<h3 class="api-title" id="defining-templates">Defining Templates in HTML</h3>
Templates are defined in '.html' files that can be located anywhere in your
Templates are defined in `.html` files that can be located anywhere in your
Meteor project folder except the `server`, `public`, and `private` directories.
Each HTML file can have any number of three types of top-level elements:
Each `.html` file can contain any number of the following top-level elements:
`<head>`, `<body>`, or `<template>`. Code in the `<head>` and `<body>` tags is
appended to that section of the HTML page, and code inside `<template>` tags can
be included using `{{dstache}}> templateName}}`, as shown in the example below.
Templates can be included more than once - one of the main purposes of templates
is to reduce view code duplication.
Templates can be included more than once &mdash; one of the main purposes of
templates is to avoid writing the same HTML multiple times by hand.
```
<!-- add code to the <head> of the page -->
@@ -39,15 +39,22 @@ is to reduce view code duplication.
The `{{dstache}} ... }}` syntax is part of a language called "Spacebars" that
Meteor uses to add functionality to HTML. As shown above, it lets you include
templates in other parts of your page. Using Spacebars, you can also display
data passed in via _helpers_. Helpers can be static values or functions.
data obtained from _helpers_. Helpers are written in JavaSript, and can be
either simple values or functions.
{{> autoApiBox "Template#helpers"}}
Here's how you might define a helper called `name` for a template called
`nametag` (in JavaScript):
```
// In JavaScript, define a helper called "nametag" on our template
Template.nametag.helpers({
name: "Ben Bitdiddle"
});
```
And here is the `nametag` template itself (in HTML):
```
<!-- In an HTML file, display the value of the helper -->
<template name="nametag">
@@ -55,8 +62,8 @@ Template.nametag.helpers({
</template>
```
Spacebars also has a few other handy functions that can be used to make your
views more dynamic.
Spacebars also has a few other handy control structures that can be used
to make your views more dynamic:
- `{{dstache}}#each data}} ... {{dstache}}/each}}` - Iterate over the items in
`data` and display the HTML inside the block for each one.
@@ -65,35 +72,40 @@ is `true`, display the first block; if it is false, display the second one.
- `{{dstache}}#with data}} ... {{dstache}}/with}}` - Set the data context of
the HTML inside, and display it.
In addition to using helpers, Spacebars lets you display data by having a _data
context_ inside every block. This means that you can use `{{dstache}}property}}`
to reference a property on the object currently in the data context.
Each nested `#each` or `#with` block has its own _data context_, which is
an object whose properties can be used as helpers inside the block. For
`#with` blocks, the data context is simply the value that appears after
the `#with` and before the `}}` characters. For `#each` blocks, each
element of the given array becomes the data context while the block is
evaluated for that element.
For instance, if the `people` helper has the following value
```
// helper 'people' is:
// [{name: "Bob"}, {name: "Frank"}, {name: "Alice"}]
Template.welcomePage.helpers({
people: [{name: "Bob"}, {name: "Frank"}, {name: "Alice"}]
});
```
<!-- show every person's name -->
then you can display every person's name as a list of `<p>` tags:
```
{{dstache}}#each people}}
<p>{{dstache}}name}}</p>
{{dstache}}/each}}
```
<!-- or we can use the nametag template from above -->
or use the "nametag" template from above instead of `<p>` tags:
```
{{dstache}}#each people}}
<p>{{dstache}}> nametag}}</p>
{{dstache}}/each}}
```
You can find detailed documentation for Spacebars in the
[README on GitHub](https://github.com/meteor/meteor/blob/devel/packages/spacebars/README.md).
{{> autoApiBox "Template#helpers"}}
Each template has a local dictionary of helpers that it can use to inject data
into the HTML. Call `Template.myTemplate.helpers()` to add to this dictionary,
and use the data in your templates with `{{helperName}}`.
For example, to show the logged in user's username:
Remember that helpers can be functions as well as simple values. For
example, to show the logged in user's username, you might define a
function-valued helper called `username`:
```
// in your JS file
@@ -104,6 +116,9 @@ Template.profilePage.helpers({
});
```
Now, each time use the `username` helper, the helper function will be
called to determine the user's name:
```
// in your HTML
<template name="profilePage">
@@ -111,20 +126,26 @@ Template.profilePage.helpers({
</template>
```
The sections about `Session`, `Tracker`, `Collections`, and `Accounts` will talk
more about how to add dynamic data to your templates.
You can also register a helper to be available in all templates by using
The helpers above have all been associated with specific templates, but
you can also make a helper available in all templates by using
[`Template.registerHelper`](#template_registerhelper).
You can find detailed documentation for Spacebars in the
[README on GitHub](https://github.com/meteor/meteor/blob/devel/packages/spacebars/README.md).
Later in this documentation, the sections about `Session`, `Tracker`,
`Collections`, and `Accounts` will talk more about how to add dynamic data
to your templates.
{{> autoApiBox "Template#events"}}
The event map passed into `Template.myTemplate.events` has event descriptors
as its keys and functions as the values. Event handlers get two arguments:
the event object and the template instance.
The event map passed into `Template.myTemplate.events` has event
descriptors as its keys and event handler functions as the values. Event
handlers get two arguments: the event object and the template instance.
To attach event handlers to the following template
```
<!-- an example template -->
<template name="example">
<button class="my-button">My button</button>
<form>
@@ -134,8 +155,9 @@ the event object and the template instance.
</template>
```
you might call `Template.example.events` as follows:
```
// Adding events to a template
Template.example.events({
"click .my-button": function (event, template) {
alert("My button was clicked!");
@@ -147,34 +169,38 @@ Template.example.events({
});
```
The first part of the key is the name of the event being captured. Pretty much
any DOM event is supported. Some common ones are: `click`, `mousedown`,
`mouseup`, `mouseenter`, `mouseleave`, `keydown`, `keyup`, `keypress`, `focus`,
`blur`, and `change`.
The first part of the key (before the first space) is the name of the
event being captured. Pretty much any DOM event is supported. Some common
ones are: `click`, `mousedown`, `mouseup`, `mouseenter`, `mouseleave`,
`keydown`, `keyup`, `keypress`, `focus`, `blur`, and `change`.
The second part is a selector that indicates which elements to listen to. This
can be almost any selector
The second part of the key (after the first space) is a selector that
indicates which elements to listen to. This can be almost any selector
[supported by JQuery](http://api.jquery.com/category/selectors/).
Whenever the indicated event happens on the selected element, the function
given in the event map will be called with the relevant DOM event and
template instance. See the [Event Maps section](#eventmaps) for details.
Whenever the indicated event happens on the selected element, the
corresponding event handler function will be called with the relevant DOM
event object and template instance. See the [Event Maps section](#eventmaps)
for details.
{{> autoApiBox "Template#rendered"}}
The function assigned to this property is called once for every instance of
Template.*myTemplate* when it is inserted into the document for the first time.
*Template.myTemplate* when it is inserted into the document for the first time.
The _rendered_ callback can be used to integrate external libraries that aren't
This _rendered_ callback can be used to integrate external libraries that aren't
familiar with Meteor's automatic view rendering, and need to be initialized
every time HTML is inserted into the page. Use the
[`created`](http://docs.meteor.com/#template_created) and
[`destroyed`](http://docs.meteor.com/#template_destroyed) callbacks to perform
initialization or clean-up on any objects.
For example, to use the HighlightJS library to apply code highlighting to
all `<pre>` elements inside the `codeSample` template, you might assign
the following function to `Template.codeSample.rendered`:
<!-- XXX Why is this not a function like Meteor.startup? -->
```
// Apply code highlighting to <pre> elements inside when
// the template is rendered (need to include HighlightJS)
Template.codeSample.rendered = function () {
hljs.highlightBlock(this.findAll('pre'));
};
@@ -182,9 +208,10 @@ Template.codeSample.rendered = function () {
In the callback function, `this` is bound to a [template
instance](#template_inst) object that is unique to this inclusion of the
template and remains across re-renderings. You can use functions like
[`this.findAll`](#template_findAll) to get DOM nodes in this template's rendered
HTML.
template and remains across re-renderings. You can use methods like
[`this.find`](#template_find) and
[`this.findAll`](#template_findAll) to access DOM nodes in the template's
rendered HTML.
<h2 id="template_inst"><span>Template instances</span></h2>
@@ -195,12 +222,12 @@ that persist as the template is reactively updated.
Template instance objects can be found in several places:
1. The value of `this` in the `created`, `rendered`,
and `destroyed` template callbacks
and `destroyed` template callbacks
2. The second argument to event handlers
3. As [`Template.instance()`](#template_instance) inside helpers
You can assign additional properties of your choice to the template instance to
keep track any state relevant to the template. For example, when using the
keep track of any state relevant to the template. For example, when using the
Google Maps API you could attach the `map` object to the current template
instance to be able to refer to it in helpers and event handlers. Use the
[`created`](#template_created) and [`destroyed`](#template_destroyed) callbacks
@@ -210,12 +237,14 @@ to perform initialization or clean-up.
`template.findAll` returns an array of DOM elements matching `selector`. You can
also use `template.$`, which works exactly like JQuery but only returns elements
from this template.
within `template`.
{{> autoApiBox "Blaze.TemplateInstance#find"}}
<!-- XXX Why is this not findOne? -->
Get one DOM element matching `selector`, or `null` if there are no
such elements. Like `findAll`, `find` only returns elements from inside the
template.
{{/template}}
{{/template}}

View File

@@ -8,18 +8,18 @@ automatically rerun templates and other computations whenever
sources change.
Unlike most other systems, you don't have to manually declare these dependencies
&mdash; it "just works". The mechanism is simple and efficient. Once you've
&mdash; it "just works." The mechanism is simple and efficient. Once you've
initialized a computation with `Tracker.autorun`, whenever you call a function
that supports reactive updates, Tracker automatically records that this data was
accessed. Later, when the data changes, the computation is rerun automatically.
This is how a template knows how to re-render whenever the data in its
[helpers](#template_helpers) changes.
that supports reactive updates, the `Tracker` automatically records which data were
accessed. Later, when those data change, the computation is rerun automatically.
This is how a template knows how to re-render whenever its [helper
functions](#template_helpers) have new data to return.
{{> autoApiBox "Tracker.autorun" }}
`Tracker.autorun` allows you to run a function that depends on reactive data
sources, in such a way that if there are changes to the data later,
the function will be rerun.
`Tracker.autorun` allows you to run a function that depends on reactive
data sources. Whenever those data sources are updated with new data, the
function will be rerun.
For example, you can monitor one `Session` variable and set another:
@@ -31,30 +31,42 @@ Tracker.autorun(function () {
```
Or you can wait for a session variable to have a certain value, and do
something the first time it does, calling `stop` on the computation to
prevent further rerunning:
something the first time it does. If you want to prevent further rerunning
of the function, you can call `stop` on the computation object that is
passed as the first parameter to the callback function:
```
// This computation will wait for a session variable to become true,
// then run exactly once.
// For this example, assume shouldAlert starts out false
Session.set("shouldAlert", false);
Tracker.autorun(function (computation) {
if (Session.get("shouldAlert")) {
computation.stop();
alert("Oh no!");
}
});
// The autorun function runs but does not alert
Session.set("shouldAlert", false);
// The autorun function runs and alerts "Oh no!"
Session.set("shouldAlert", true);
// The autorun function no longer runs
Session.set("shouldAlert", "maybe?");
```
The function is invoked immediately, at which point it may alert and
stop right away if `shouldAlert` is already true. If not, the
function is run again when `shouldAlert` becomes true.
The first time `Tracker.autorun` is called, the callback function is
invoked immediately, at which point it would alert and stop right away if
`shouldAlert` had been true. If not, the function is run again when
`shouldAlert` becomes true.
If the initial run of an autorun throws an exception, the computation
is automatically stopped and won't be rerun.
To learn more about how Tracker works and to explore advanced ways to use it,
visit the <a href="http://manual.meteor.com/#tracker"> Tracker</a> chapter in the
visit the <a href="http://manual.meteor.com/#tracker">Tracker</a> chapter in the
<a href="http://manual.meteor.com/">Meteor Manual</a>, which describes it in
complete detail.
much more detail.
{{/template}}
{{/template}}