-
Notifications
You must be signed in to change notification settings - Fork 5.2k
/
api.html
1499 lines (1147 loc) · 55.7 KB
/
api.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<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 — 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 — 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.