Files
meteor/docs/client/concepts.html
matt debergalis a59f501b37 edits
2012-03-20 16:55:34 -07:00

475 lines
18 KiB
HTML

<template name="concepts">
{{#better_markdown}}
<h1 id="concepts">Concepts</h1>
We've written our fair share of single-page JS applications by hand.
Writing an entire application in one language (JavaScript) with one
data format (JSON) is a real joy. Meteor is everything we wanted
when writing those apps.
{{> structure }}
{{> data }}
{{> reactivity }}
{{> templates }}
{{> packages_concept }}
{{> deploying }}
{{/better_markdown}}
</template>
<template name="structure">
{{#better_markdown}}
<h2 id="structuringyourapplication">Structuring your application</h2>
A Meteor application is a mix of JavaScript that runs inside a
client web browser, JavaScript that runs on the Meteor server inside
a Node.js container, and all the supporting HTML fragments, CSS rules,
and static assets. Meteor automates the packaging and transmission
of these different components. And, it is quite flexible about how
you choose to structure those components in your file tree.
The only server asset is JavaScript. Meteor gathers all your JS
files, excluding anything under the `client`
and `public` subdirectories, and loads them into a Node.js
server instance inside a fiber. In Meteor, your server code runs in
a single thread per request, not in the asynchronous callback style
typical of Node. We find the linear execution model a better fit for
the typical server code in a Meteor application.
There are more assets to consider on the client side. Meteor
gathers all JavaScript files in your tree with the exception of
the `server` and `public` subdirectories for the
client. It minifies this bundle and serves it to each new client.
You're free to use a single JS file for your entire application, or
create a nested tree of separate files, or anything in between.
Files outside the `client` and `server`
subdirectories are loaded on both the client and the server! That's
the place for model definitions and other functions. Also, instead of
putting client and server functions in different directories, you can
use the [`is_client` and `is_server`](#is_client) variables
to isolate JS to one or the other side.
CSS files work just the same: the client will get a bundle with all
the CSS in your tree (excluding the `server`
and `public` subdirectories).
In development mode, JS and CSS files are sent individually to make
debugging easier.
HTML files in a Meteor application are treated quite a bit differently
from a server-side framework. Meteor scans all the HTML files in your
directory for three top-level elements: `<head>`, `<body>`, and
`<template>`. The head and body sections are seperately concatenated
into a single head and body, which are transmitted to the client on
initial page load.
Template sections, on the other hand, are converted into JavaScript
functions, available under the `Template` namespace. It's
a really convenient way to ship HTML templates to the client.
See the <a href="#templates">templates</a> section for more.
Lastly, the Meteor server will serve any files under
the `public` directory, just like in a Rails or Django
project. This is the place for images, favicon, robots.txt, and
anything else.
{{/better_markdown}}
</template>
<template name="data">
{{#better_markdown}}
<h2 id="data">Data</h2>
Meteor makes writing distributed client code as simple as talking to a
local database. It's a clean and simple approach, much easier than
building individual RPC endpoints, slow roundtrips to the server, and
orchestrating invalidation messages.
A Meteor application's main data store is MongoDB, which
holds <b>collections</b> of individual <b>documents</b>. Your server
code has direct access to that database. Your client code
has <i>simulated</i> direct access to that database.
Every Meteor client includes an in-memory database cache. Each
client's cache holds valid copies of some set of documents. When a
matching document in the server's master database changes, Meteor
automatically synchronizes that change to every subscribed client.
To manage the client caches, your server code <b>publishes</b> sets of
documents, and your client code <b>subscribes</b> to those sets. For
example, if you are building a chat system, the server might publish two
sets: the set of all rooms, and the set of all messages in a given room.
Each client would subscribe to the master set of available rooms and the
set of messages in the currently-selected room. Once subscribed, the
client uses its cache as a fast local database, dramatically simplifying
your client model code.
// server: publish all room documents, and per-room messages
Meteor.publish("chatrooms");
Meteor.publish("messages", function (room_id) {
return Messages.find({room: room_id});
});
// client: subscribe to all rooms, and messages in the first room
Meteor.subscribe("chatrooms");
Meteor.subscribe("messages", Chatrooms.find()[0]._id);
Document modifications also propagate automatically. To insert, update,
or remove a document, client code uses the familiar <a target="_blank"
href="http://www.mongodb.org/display/DOCS/Manual">MongoDB API</a>. That
change instruction is executed immediately on the client's cached
data. <i>At the same time</i>, the client sends that instruction up to
the server, which executes the same change against the master database.
Usually the client and server agree, but should they differ (permissions
checking or overlapping with another client, for example), the server's
result will publish back down to the client. And of course, all other
clients with a matching subscription automatically receive an updated
document.
// create new message, executes on both client and server.
Messages.insert({room: 2413, text: "hello!"});
Putting it all together, these techniques accomplish <i>latency
compensation</i>. Clients hold a fresh copy of the data they need, and
never need to wait for a roundtrip to the server. And when clients
modify data, those modifications can run locally without waiting for the
confirmation from the server, while still giving the server final say
over the requested change.
{{/better_markdown}}
</template>
<template name="reactivity">
{{#better_markdown}}
<h2 id="reactivity">Reactivity</h2>
Meteor embraces the concept of
<a target="_blank" href="http://en.wikipedia.org/wiki/Reactive_programming">
reactive programming</a>. This means that you can write your code in a
simple imperative style, and the result will be automatically
recalculated whenever data changes that your code depends on.
Meteor.autosubscribe(function () {
Meteor.subscribe("messages", Session.get("currentRoomId"));
});
This example (taken from a chat room client) sets up a data
subscription based on the session variable `currentRoomId`.
If the value of `Session.get("currentRoomId")` changes for any reason, the
function will be automatically re-run, setting up a new subscription that
replaces the old one.
This automatic recomputation is achieved by a cooperation
between `Session` and `Meteor.autosubscribe`.
Methods like `Meteor.autosubscribe` establish a "reactive
context" inside of which data dependencies are tracked, and they are
prepared to re-run their function argument as necessary. Data
providers like `Session`, on the other hand, make note of
the context they are called from and what data was requested, and they
are prepared to send an invalidation signal when the data changes.
This simple pattern has wide applicability. Above, the programmer is
saved from writing unsubscribe/resubscribe calls and making sure they
are called at the right time. In general, Meteor can eliminate whole
classes of data propagation code which would otherwise clog up your
application with error-prone logic.
These Meteor functions run your code in a reactive context:
* <a href="#render">Meteor.ui.render</a> and <a href="#renderList">Meteor.ui.renderList</a>
* <a href="#autosubscribe">Meteor.autosubscribe</a>
* <a href="#templates">Templates</a>
And the reactive data sources that can trigger changes are:
* <a href="#session">Session</a> variables
* Database queries on <a href="#find">Collections</a>
* <a href="#status">Meteor.status</a>
Meteor's <a href="https://github.com/meteor/meteor/blob/master/packages/deps/deps.js" target="_blank">implementation</a>
of reactivity is short and sweet, about 50 lines of code. You can
hook into it yourself to add new reactive contexts or data sources,
using the <a href="#meteordeps">Meteor.deps</a> module.
{{/better_markdown}}
</template>
<template name="templates">
{{#better_markdown}}
<h2 id="templates">Templates</h2>
Meteor applications can create reactive DOM elements directly
with <a href="#render"><code>Meteor.ui.render</code></a>
and <a href="#renderlist"><code>Meteor.ui.renderList</code></a>. Templates
provide a way to simplify the process even further, allowing you to
write your HTML structure directly in HTML files.
To create a template, include a <code>&lt;template&gt;</code> tag with
a name attribute in any HTML file, whose content is the template text.
The template will be compiled and made available as a function on
the <code>Template</code> object. The function takes some JSON data
as input, and returns a <code>DocumentFragment</code>. Insert the
<code>DocumentFragment</code> anywhere you like &mdash; it will
automatically update itself as the data it depends on changes.
<div class="warning">
By default, you're using the popular Handlebars templating language,
with some extensions. But there is nothing Handlebar-specific in
Meteor and other templating systems will be available as
packages. For now, read up on the
<a href="http://www.handlebarsjs.com/">Handlebars documentation.</a>
</div>
To render the template, you simply call the function:
<pre>
&lt;!-- in myapp.html --&gt;
&lt;template name="hello"&gt;
&lt;div class="greeting"&gt;Hello there, {&#123;first}} {&#123;last}}!&lt;/div&gt;
&lt;/template&gt;
// in the JavaScript console
&gt; Template.hello({first: "Alyssa", last: "Hacker"});
=&gt; &lt;div class="greeting"&gt;Hello there, Alyssa Hacker!&lt;/div&gt;</pre>
In addition to passing JSON data directly to the template function,
you can also provide data to templates by setting additional
properties on the template function.
You can pass in functions this way, and use them just like you would
use data that was passed in as JSON.
<pre>
Template.players.top_10_scorers = function () {
return Users.find({}, {sort: {score: -1}, limit: 10});
};
&lt;!-- Use it like this --&gt;
&lt;template name="players"&gt;
{&#123;#each top_10_scorers}}
&lt;div&gt;{&#123;name}}&lt;/div&gt;
{&#123;/each}}
&lt;/template&gt;</pre>
Functions can take arguments, and they receive the current template
data in <code>this</code>.
<pre>
Template.players.league_is = function (league) {
return this.league === league;
};
&lt;!-- Use it like this --&gt;
&lt;template name="players"&gt;
{&#123;#each top_10_scorers}}
{&#123#if league_is "junior"}}
&lt;div&gt;Junior: {&#123;name}}&lt;/div&gt;
{&#123/if}}
{&#123#if league_is "senior"}}
&lt;div&gt;Senior: {&#123;name}}&lt;/div&gt;
{&#123/if}}
{&#123;/each}}
&lt;/template&gt;</pre>
<div class="note">
Handlebars note: <code>{&#123;#if league_is "junior"}}</code> is
allowed because of a Meteor extension that allows nested helper
calls. (Both <code>if</code> and <code>league_is</code> are
technically helpers, and stock Handlebars only allows one helper
invocation per expression.)
</div>
You can also pass in constant data.
<pre>
// Works fine with {&#123;#each sections}}
Template.report.sections = ["Situation", "Complication", "Resolution"];</pre>
Finally, you can set the <code>events</code> property of a template
function to a table of event handlers. The format is documented at
<a href="#eventmaps">event map</a>. The <code>this</code> argument to
the event handler will be the template data.
<div class="warning">
For now, the event handler gets the template data from the <em>top
level</em> of the current template, not the template data from the
template context of the element that triggered the event. This will
be changing.
</div>
<pre>
&lt;!-- myapp.html --&gt;
&lt;template name="scores"&gt;
{&#123;#each player}}
{&#123;&gt; player_score}}
{&#123;/each}}
&lt;/template&gt;
&lt;template name="player_score"&gt;
&lt;div&gt;{&#123;name}}: {&#123;score}}
&lt;span class="give_points"&gt;Give points&lt;/span&gt;
&lt;/div&gt;
&lt;/template&gt;
&lt;!-- myapp.js --&gt;
Template.player_score.events = {
'click .give_points': function () {
Users.update({_id: this._id}, {$inc: {score: 2}});
}
};</pre>
Templates are reactive &mdash; they are automatically wrapped
in <a href="#render"><code>Meteor.ui.render</code></a>. So, the DOM
elements they return automatically update themselves.
<pre class="prettyprint">
&lt;!-- in myapp.html --&gt;
&lt;template name="forecast"&gt;
&lt;div&gt;It'll be {&#123;prediction}} tonight&lt;/div&gt;
&lt;/template&gt;
&lt;!-- in myapp.js --&gt;
// JavaScript: reactive helper function
Template.forecast.prediction = function () {
return Session.get("weather");
};
&lt;!-- in the console --&gt;
&gt; Session.set("weather", "cloudy");
&gt; var x = Template.forecast();
=&gt; &lt;div&gt;It'll be cloudy tonight&lt;/div&gt;
&gt; document.body.appendChild(x);
&gt; Session.set("weather", "cool and dry");
&gt; x
=&gt; &lt;div&gt;It'll be cool and dry tonight&lt;/div&gt;</pre>
The auto-updating continues as long as the elements are on the
screen. The specific rule is: if the elements are not children
of <code>document</code> when
<a href="#flush"><code>Meteor.flush</code></a> runs, then Meteor may
stop updating them so that the browser's garbage collector can clean
them up.
{{/better_markdown}}
</template>
<template name="packages_concept">
{{#better_markdown}}
<h2 id="smartpackages">Smart Packages</h2>
Meteor has an unusually powerful package system. All of the
functionality you've read about so far is implemented as standard
Meteor packages.
Meteor packages are intelligent: the packages are themselves
JavaScript programs. They can inject code into the client or the
server, or hook new functions into the bundler, so they can extend the
Meteor environment in arbitrary ways. Some examples of packages are:
* The <a href="#coffeescript">coffeescript</a> package extends the
bundler, automatically compiling any <code>.coffee</code> files in
your tree. Once added, you can write your application in CoffeeScript
instead of JavaScript.
* The <a href="#jquery">jQuery</a>
and <a href="#backbone">Backbone</a> packages are examples of using
Meteor to prepackage client JavaScript libraries. You could get
the same result by copying the JavaScript files into your tree, but
it's faster to add a package.
* The <a href="#underscore">underscore</a> package extends both the
client and server environments. Many of the core Meteor features,
including Minimongo, the Session object, and reactive Handlebars
templates, are implemented as internal packages automatically
included with every Meteor application.
You can see a list of available packages
with <a href="#meteorlist">meteor list</a>,
add packages to your project
with <a href="#meteoradd">meteor add</a>, and remove them
with <a href="#meteorremove">meteor remove</a>.
See the <a href="#packagelist">Package List</a> section for a description
of the existing packages.
{{#warning}}
The package API is rapidly changing and isn't documented, so you can't
make your own packages just yet. Coming soon.
{{/warning}}
{{/better_markdown}}
</template>
<template name="deploying">
{{#better_markdown}}
<h2 id="deploying">Deploying</h2>
Meteor is a full application server. We include everything you need
to deploy your application on the internet: you just provide the JS,
HTML, and CSS.
<h3 class="nosection">Running on Meteor's infrastructure</h3>
The easiest way to deploy your application is to use <b>meteor
deploy</b>. We provide it because it's what, personally, we've always
wanted: an easy way to take an app idea, flesh it out over a weekend,
and put it out there for the world to use, with nothing getting in the
way of creativity.
$ meteor deploy myapp.meteor.com
Your application is now available at myapp.meteor.com. If
this is the first time deploying to this hostname, Meteor creates a
fresh empty database for your application. If you want to deploy an
update, Meteor will preserve the existing data and just refresh the
code.
You can also deploy to your own domain. Just set up the hostname you
want to use as a CNAME to <code>origin.meteor.com</code>,
then deploy to that name.
$ meteor deploy www.myapp.com
We provide this as a free service so you can try Meteor. It is also
helpful for quickly putting up internal betas, demos, and so on.
<h3 class="nosection">Running on your own infrastructure</h3>
You can run also your application on your own infrastructure, or any
other hosting provider like Heroku.
To get started, run
$ meteor bundle myapp.tgz
This command will generate a fully-contained Node.js application in
the form of a tarball. To run this application, you need to provide
Node.js 0.6 and a MongoDB server. You can then run the application by
invoking node, specifying the HTTP port for the application to listen
on, and the MongoDB endpoint. If you don't already have a MongoDB
server, we can recommend our friends at [MongoHQ](http://mongohq.com).
$ PORT=3000 MONGO_URL=mongodb://localhost:27017/myapp node bundle/main.js
{{#warning}}
For now, bundles will only run on the platform that the bundle was
created on. To run on a different platform, you'll need to rebuild
the native packages included in the bundle. To do that, make sure you
have <code>npm</code> available, and run the following:
$ cd bundle/server
$ rm -r fibers
$ npm install fibers@0.6.3
{{/warning}}
{{/better_markdown}}
</template>