mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge branch 'support-autorun' into devel
This commit is contained in:
@@ -22,6 +22,9 @@
|
||||
|
||||
For more information on Meteor Accounts, see http://docs.meteor.com/#accounts
|
||||
|
||||
* The new function `Meteor.autorun` allows you run any code in a reactive
|
||||
context. See http://docs.meteor.com/#meteor_autorun
|
||||
|
||||
* Arrays and objects can now be stored in the `Session`; mutating the value you
|
||||
retrieve with `Session.get` does not affect the value in the session.
|
||||
|
||||
|
||||
@@ -2110,6 +2110,34 @@ might think of it as a dynamically scoped ("special") variable. (That
|
||||
just means that [`run`](#run) sets it, runs some user-supplied code, and
|
||||
then restores its previous value.)
|
||||
|
||||
{{> api_box autorun }}
|
||||
|
||||
`Meteor.autorun` allows you to set up your own reactive context, where you can
|
||||
perform arbitrary actions when dependencies change. For example, you can monitor
|
||||
a cursor (which is a reactive data source) and aggregate it into a session
|
||||
variable:
|
||||
|
||||
Meteor.autorun(function() {
|
||||
var oldest = _.max(Monkeys.find().fetch(), function (monkey) {
|
||||
return monkey.age;
|
||||
});
|
||||
if (oldest)
|
||||
Session.set("oldest", oldest.name);
|
||||
});
|
||||
|
||||
Or you can wait for a session variable to get a certain value, and do something
|
||||
the first time it does so, using the `stop` handle to prevent further runs:
|
||||
|
||||
Meteor.autorun(function(handle) {
|
||||
if (!Session.equals("shouldAlert", true)) return;
|
||||
handle.stop();
|
||||
alert("Oh no!");
|
||||
});
|
||||
|
||||
The function is invoked immediately and — like all reactive
|
||||
sources — the rerun occurs at the time of the next
|
||||
[`Meteor.flush`](#meteor_flush).
|
||||
|
||||
|
||||
{{> api_box flush }}
|
||||
|
||||
@@ -2143,8 +2171,6 @@ elements are cleaned up by logic that is triggered by context invalidations.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="meteor_http"><span>Meteor.http</span></h2>
|
||||
|
||||
`Meteor.http` provides an HTTP API on the client and server. To use
|
||||
|
||||
@@ -608,6 +608,18 @@ Template.api.current = {
|
||||
descr: ["The current [`invalidation context`](#context), or `null` if not being called from inside [`run`](#run)."]
|
||||
};
|
||||
|
||||
Template.api.autorun = {
|
||||
id: "meteor_autorun",
|
||||
name: "Meteor.autorun(func)",
|
||||
locus: "Client",
|
||||
descr: ["Run a function and rerun it whenever its dependencies change. Returns a handle that provides a `stop` method, which will prevent further reruns."],
|
||||
args: [
|
||||
{name: "func",
|
||||
type: "Function",
|
||||
descr: "The function to run. It receives one argument: the same handle that `Meteor.autorun` returns."}
|
||||
]
|
||||
};
|
||||
|
||||
Template.api.flush = {
|
||||
id: "meteor_flush",
|
||||
name: "Meteor.flush()",
|
||||
|
||||
@@ -199,9 +199,10 @@ application with error-prone logic.
|
||||
|
||||
These Meteor functions run your code in a reactive context:
|
||||
|
||||
* [Templates](#templates)
|
||||
* [`Meteor.render`](#meteor_render) and [`Meteor.renderList`](#meteor_renderlist)
|
||||
* [`Meteor.autosubscribe`](#meteor_autosubscribe)
|
||||
* [Templates](#templates)
|
||||
* [`Meteor.autorun`](#meteor_autorun)
|
||||
|
||||
And the reactive data sources that can trigger changes are:
|
||||
|
||||
|
||||
@@ -223,6 +223,7 @@ var toc = [
|
||||
{instance: "context", name: "invalidate"}
|
||||
],
|
||||
{name: "Meteor.deps.Context.current", id: "current"},
|
||||
"Meteor.autorun",
|
||||
"Meteor.flush"
|
||||
// ],
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ Template.circles.rendered = function () {
|
||||
|
||||
if (! self.handle) {
|
||||
d3.select(self.node).append("rect");
|
||||
self.handle = Meteor._autorun(function () {
|
||||
self.handle = Meteor.autorun(function () {
|
||||
var circle = d3.select(self.node).selectAll("circle")
|
||||
.data(Circles.find({group: data.group}).fetch(),
|
||||
function (d) { return d._id; });
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
var waitUntilLoggedIn = function (test, expect) {
|
||||
var unblockNextFunction = expect();
|
||||
var quiesceCallback = function () {
|
||||
Meteor._autorun(function (handle) {
|
||||
Meteor.autorun(function (handle) {
|
||||
if (!Meteor.userLoaded()) return;
|
||||
handle.stop();
|
||||
unblockNextFunction();
|
||||
|
||||
@@ -17,7 +17,7 @@ if (Meteor.isClient) (function () {
|
||||
test.equal(Meteor.user().username, someUsername);
|
||||
});
|
||||
return function () {
|
||||
Meteor._autorun(function(handle) {
|
||||
Meteor.autorun(function(handle) {
|
||||
if (!Meteor.userLoaded()) return;
|
||||
handle.stop();
|
||||
callWhenLoaded();
|
||||
@@ -70,7 +70,7 @@ if (Meteor.isClient) (function () {
|
||||
// Set up a reactive context that only refreshes when Meteor.user() is
|
||||
// invalidated.
|
||||
var user;
|
||||
var handle1 = Meteor._autorun(function () {
|
||||
var handle1 = Meteor.autorun(function () {
|
||||
user = Meteor.user();
|
||||
});
|
||||
// At the beginning, we're not logged in.
|
||||
@@ -86,7 +86,7 @@ if (Meteor.isClient) (function () {
|
||||
handle1.stop();
|
||||
});
|
||||
var waitForLoaded = expect(function () {
|
||||
Meteor._autorun(function(handle2) {
|
||||
Meteor.autorun(function(handle2) {
|
||||
if (!Meteor.userLoaded()) return;
|
||||
handle2.stop();
|
||||
callWhenLoaded();
|
||||
|
||||
@@ -55,14 +55,14 @@
|
||||
|
||||
Meteor.deps._ContextSet = _ContextSet;
|
||||
|
||||
////////// Meteor._autorun
|
||||
////////// Meteor.autorun
|
||||
|
||||
// Run f(). Record its dependencies. Rerun it whenever the
|
||||
// dependencies change.
|
||||
//
|
||||
// Returns an object with a stop() method. Call stop() to stop the
|
||||
// rerunning. Also passes this object as an argument to f.
|
||||
Meteor._autorun = function (f) {
|
||||
Meteor.autorun = function (f) {
|
||||
var ctx;
|
||||
var slain = false;
|
||||
var handle = {
|
||||
|
||||
46
packages/deps/deps_tests.js
Normal file
46
packages/deps/deps_tests.js
Normal file
@@ -0,0 +1,46 @@
|
||||
Tinytest.add('deps - autorun', function (test) {
|
||||
var listeners = new Meteor.deps._ContextSet;
|
||||
var x = 0;
|
||||
var handle = Meteor.autorun(function (handle) {
|
||||
listeners.addCurrentContext();
|
||||
++x;
|
||||
});
|
||||
test.equal(x, 1);
|
||||
Meteor.flush();
|
||||
test.equal(x, 1);
|
||||
listeners.invalidateAll();
|
||||
test.equal(x, 1);
|
||||
Meteor.flush();
|
||||
test.equal(x, 2);
|
||||
listeners.invalidateAll();
|
||||
test.equal(x, 2);
|
||||
Meteor.flush();
|
||||
test.equal(x, 3);
|
||||
listeners.invalidateAll();
|
||||
// Prevent the function from running further.
|
||||
handle.stop();
|
||||
Meteor.flush();
|
||||
test.equal(x, 3);
|
||||
listeners.invalidateAll();
|
||||
Meteor.flush();
|
||||
test.equal(x, 3);
|
||||
|
||||
Meteor.autorun(function (internalHandle) {
|
||||
listeners.addCurrentContext();
|
||||
++x;
|
||||
if (x == 6)
|
||||
internalHandle.stop();
|
||||
});
|
||||
test.equal(x, 4);
|
||||
listeners.invalidateAll();
|
||||
Meteor.flush();
|
||||
test.equal(x, 5);
|
||||
listeners.invalidateAll();
|
||||
// Increment to 6 and stop.
|
||||
Meteor.flush();
|
||||
test.equal(x, 6);
|
||||
listeners.invalidateAll();
|
||||
Meteor.flush();
|
||||
// Still 6!
|
||||
test.equal(x, 6);
|
||||
});
|
||||
@@ -11,3 +11,9 @@ Package.on_use(function (api, where) {
|
||||
api.use('underscore', where);
|
||||
api.add_files(['deps.js', 'deps-utils.js'], where);
|
||||
});
|
||||
|
||||
Package.on_test(function (api) {
|
||||
api.use('tinytest');
|
||||
api.use('deps');
|
||||
api.add_files('deps_tests.js', 'client');
|
||||
});
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
Tinytest.add('session - context invalidation for get', function (test) {
|
||||
var xGetExecutions = 0;
|
||||
Meteor._autorun(function () {
|
||||
Meteor.autorun(function () {
|
||||
++xGetExecutions;
|
||||
Session.get('x');
|
||||
});
|
||||
@@ -99,7 +99,7 @@
|
||||
|
||||
Tinytest.add('session - context invalidation for equals', function (test) {
|
||||
var xEqualsExecutions = 0;
|
||||
Meteor._autorun(function () {
|
||||
Meteor.autorun(function () {
|
||||
++xEqualsExecutions;
|
||||
Session.equals('x', 5);
|
||||
});
|
||||
@@ -132,7 +132,7 @@
|
||||
function (test) {
|
||||
// Make sure the special casing for equals undefined works.
|
||||
var yEqualsExecutions = 0;
|
||||
Meteor._autorun(function () {
|
||||
Meteor.autorun(function () {
|
||||
++yEqualsExecutions;
|
||||
Session.equals('y', undefined);
|
||||
});
|
||||
|
||||
@@ -795,7 +795,7 @@ Spark.isolate = function (htmlFunc) {
|
||||
var range;
|
||||
var firstRun = true;
|
||||
var retHtml;
|
||||
Meteor._autorun(function (handle) {
|
||||
Meteor.autorun(function (handle) {
|
||||
if (firstRun) {
|
||||
retHtml = renderer.annotate(
|
||||
htmlFunc(), Spark._ANNOTATION_ISOLATE,
|
||||
|
||||
Reference in New Issue
Block a user