From d3c7b92d806234c8657708718b1745c693c6e4f4 Mon Sep 17 00:00:00 2001 From: David Greenspan Date: Thu, 5 Jun 2014 23:29:11 -0700 Subject: [PATCH 1/2] Comments in renderpoint.js --- packages/blaze/renderpoint.js | 122 +++++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 2 deletions(-) diff --git a/packages/blaze/renderpoint.js b/packages/blaze/renderpoint.js index 0b53fa70be..8681576ce1 100644 --- a/packages/blaze/renderpoint.js +++ b/packages/blaze/renderpoint.js @@ -1,5 +1,117 @@ -// RenderPoints must support being evaluated and/or createDOMRanged multiple -// times. They must not contain per-instance state. + +/// RenderPoints are objects that can be included in an HTMLjs tree +/// processed by Blaze alongside the built-in HTMLjs types like Tags, +/// strings, and arrays. Among the types of RenderPoints are +/// Components, Controllers (including "#with"), and reactivity +/// primitives like Isolate and List. When Blaze encounters a +/// RenderPoint object while performing one of its four internal +/// operations on HTMLjs trees -- toDOM, toHTML, toText, and evaluate +/// -- it calls upon the RenderPoint to render itself using one of the +/// four public RenderPoint methods corresponding to these operations +/// (createDOMRange, toHTML, toText, and evaluate). +/// +/// Blaze.Isolate is an example of a RenderPoint. Calling Blaze.Isolate +/// with a function argument returns a Blaze.Isolate object, which can be +/// included in an HTMLjs tree to establish a region of DOM that is +/// reactively recalculated: +/// +/// ``` +/// Blaze.render(function () { +/// return HTML.DIV(Blaze.Isolate(function () { +/// return Session.get('foo'); +/// })); +/// }); +/// ``` +/// +/// The RenderPoint base class comes with basic implementations of +/// the four public methods (evaluate, toText, toHTML, and createDOMRange) +/// in terms of a private method called "render," returning the HTMLjs +/// content to render, which is expected to be overridden by subclasses. +/// +/// For example, here is a simple RenderPoint subclass which renders +/// some content inside a DIV: +/// +/// ``` +/// DivWrapper = Blaze.RenderPoint.extend({ +/// constructor: function (contentFunc) { +/// DivWrapper.__super__.constructor.call(this); +/// this.contentFunc = contentFunc; +/// }, +/// render: function () { +/// var f = this.contentFunc; +/// return HTML.DIV({id:"wrapper"}, f()); +/// } +/// }); +/// ``` +/// +/// To use DivWrapper: +/// +/// ``` +/// Blaze.render(function () { +/// return new DivWrapper(function () { +/// return HTML.SPAN("Hello"); +/// }); +/// }); +/// ``` +/// +/// RenderPoint subclasses can influence rendering in a variety of ways, +/// such as by running code before and after rendering (e.g. to set a +/// dynamic variable) or by saving a pointer to the DOMRange returned by +/// createDOMRange and updating it reactively. +/// +/// RenderPoint instances are meant to be constructed inside Blaze.render +/// and used immediately. If the Blaze.render re-runs, a new RenderPoint +/// instance will be created. However, RenderPoints must also support +/// the case where one instance is rendered multiple times; the relationship +/// between RenderPoints and DOMRanges may be one to many. +/// +/// When a RenderPoint is used in an HTML attribute value, the same +/// RenderPoint instance may be evaluated multiple times. For +/// example, take this code which renders a DIV with a reactively +/// updating "id": +/// +/// ``` +/// Blaze.render(function () { +/// return HTML.DIV({id: Blaze.Isolate(function () { +/// return Session.get('foo'); +/// })}); +/// }); +/// ``` +/// +/// When a tag like the DIV is rendered, the attributes are evaluated +/// in a Deps computation. However, Blaze.Isolate is only ever called +/// once here, and the resulting Isolate object (RenderPoint) is +/// evaluated multiple times. +/// +/// The class hierarchy rooted at RenderPoint looks like this: +/// +/// * RenderPoint +/// * Isolate +/// * List +/// * Controller +/// * With +/// * Component +/// +/// Controllers are special because they have a "parent" pointer to the +/// enclosing Controller, and the current Controller is tracked by a +/// dynamic variable. Controllers may be used in HTML attribute values +/// (where they are rendered as text), but if they are rendered to DOM, +/// bidirectional pointers are set up between the resulting DOMRange +/// (which is assumed to be the only one) and the Controller instance. +/// +/// Components have a lifecycle (they get stopped and finalized) and +/// their rendered contents are isolated by default. They are not +/// meant to be used in attributes. They may only be rendered once; +/// code that gets re-run (such as the function passed to Blaze.render +/// or Blaze.Isolate) must create a new component instance each time. +/// In other words, you can't create a component instance outside of +/// Blaze.render and then use it from the function passed to +/// Blaze.render. +/// +/// Some RenderPoints can be instantiated with or without `new` +/// (notably Isolate, List, and With), but by default, RenderPoints, +/// Controllers, and Components must be created with `new`. + Blaze.RenderPoint = JSClass.create({ render: function () { return null; @@ -35,6 +147,8 @@ Blaze.Isolate = Blaze.RenderPoint.extend({ return func(); }, createDOMRange: function () { + // Blaze.render does the actual work of setting up a computation + // and reactively updating the DOMRange. return Blaze.render(this.func); } }); @@ -56,6 +170,9 @@ Blaze.List = Blaze.RenderPoint.extend({ self.funcSeq = funcSequence; }, render: function () { + // Get and call all the functions in funcSeq, taking a dependency + // on funcSeq. This is the path taken for toText, toHTML, and + // evaluate (but not createDOMRange, which is handled specially). var funcSeq = this.funcSeq; this.funcSeq.depend(); @@ -68,6 +185,7 @@ Blaze.List = Blaze.RenderPoint.extend({ return result; }, createDOMRange: function () { + // Blaze.renderList does the actual work. return Blaze.renderList(this.funcSeq); } }); From e5118295d39f3183f55956026f1e5f457a0520bd Mon Sep 17 00:00:00 2001 From: Avital Oliver Date: Fri, 6 Jun 2014 11:15:46 -0700 Subject: [PATCH 2/2] Fix Blaze.getElementController We had a bug in which event handlers inside {{#if}} blocks didn't see the data context. I'll commit a test that would have caught this after this commit. --- packages/blaze/component.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/blaze/component.js b/packages/blaze/component.js index 753f15eafb..d0c49cbc53 100644 --- a/packages/blaze/component.js +++ b/packages/blaze/component.js @@ -176,8 +176,12 @@ Blaze.getElementController = function (elem) { var controller = null; while (range && ! controller) { controller = (range.controller || null); - if (! controller) - range = range.parentRange; + if (! controller) { + if (range.parentRange) + range = range.parentRange; + else + range = Blaze.DOMRange.forElement(range.parentElement); + } } return controller; };