Views only have an immediate onViewRendered cb

You don’t get the “wait for attached” behavior from Blaze.View anymore, you just get a plain onViewRendered callback.  No onMaterialized.  The template “rendered” callback implements the wait-for-attached and wait-for-afterFlush parts.

Also, if a template somehow re-renders, it won’t get extra event handlers now.  And if it re-renders before being attached, something sensible will happen (like all the calls of the rendered callback will be saved for onAttached).

view.isCreatedForExpansion and view.isInRender become private.
This commit is contained in:
David Greenspan
2014-08-13 17:22:07 -07:00
parent b5507c33a7
commit 4d9f4ebd35
5 changed files with 99 additions and 85 deletions

View File

@@ -49,7 +49,7 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) {
var eachView = Blaze.View('each', function () {
var subviews = this.initialSubviews;
this.initialSubviews = null;
if (this.isCreatedForExpansion) {
if (this._isCreatedForExpansion) {
this.expandedValueDep = new Deps.Dependency;
this.expandedValueDep.depend();
}

View File

@@ -42,7 +42,10 @@ Template.prototype.constructView = function (contentFunc, elseFunc) {
elseFunc ? new Template('(elseBlock)', elseFunc) : null);
if (self.__eventMaps || typeof self.events === 'object') {
view.onMaterialized(function () {
view.onViewRendered(function () {
if (view.renderCount !== 1)
return;
if (! self.__eventMaps.length && typeof self.events === "object") {
// Provide limited back-compat support for `.events = {...}`
// syntax. Pass `template.events` to the original `.events(...)`
@@ -86,8 +89,22 @@ Template.prototype.constructView = function (contentFunc, elseFunc) {
}
if (self.rendered) {
view.onRendered(function () {
self.rendered.call(view.templateInstance());
var callRendered = function () {
Deps.afterFlush(function () {
if (! view.isDestroyed) {
Blaze._withCurrentView(view, function () {
self.rendered.call(view.templateInstance());
});
}
});
};
view.onViewRendered(function onViewRendered() {
if (view.isDestroyed)
return;
if (! view.domrange.isAttached)
view.domrange.onAttached(callRendered);
else
callRendered();
});
}

View File

@@ -47,7 +47,6 @@ Blaze.View = function (name, render) {
this._callbacks = {
created: null,
materialized: null,
rendered: null,
destroyed: null
};
@@ -56,9 +55,9 @@ Blaze.View = function (name, render) {
// and also may help Chrome optimize the code by keeping
// the View object from changing shape too much.
this.isCreated = false;
this.isCreatedForExpansion = false;
this._isCreatedForExpansion = false;
this.isDestroyed = false;
this.isInRender = false;
this._isInRender = false;
this.parentView = null;
this.domrange = null;
@@ -71,11 +70,7 @@ Blaze.View.prototype.onViewCreated = function (cb) {
this._callbacks.created = this._callbacks.created || [];
this._callbacks.created.push(cb);
};
Blaze.View.prototype.onMaterialized = function (cb) {
this._callbacks.materialized = this._callbacks.materialized || [];
this._callbacks.materialized.push(cb);
};
Blaze.View.prototype.onRendered = function (cb) {
Blaze.View.prototype.onViewRendered = function (cb) {
this._callbacks.rendered = this._callbacks.rendered || [];
this._callbacks.rendered.push(cb);
};
@@ -102,7 +97,7 @@ Blaze.View.prototype.onViewDestroyed = function (cb) {
/// of the View (as in Blaze.With) should be started from an onViewCreated
/// callback. Autoruns that update the DOM should be started
/// from either onViewCreated (guarded against the absence of
/// view.domrange), onMaterialized, or onRendered.
/// view.domrange), or onViewRendered.
Blaze.View.prototype.autorun = function (f, _inViewScope) {
var self = this;
@@ -130,7 +125,7 @@ Blaze.View.prototype.autorun = function (f, _inViewScope) {
if (! self.isCreated) {
throw new Error("View#autorun must be called from the created callback at the earliest");
}
if (this.isInRender) {
if (this._isInRender) {
throw new Error("Can't call View#autorun from inside render(); try calling it from the created or rendered callback");
}
if (Deps.active) {
@@ -164,7 +159,7 @@ Blaze._createView = function (view, parentView, forExpansion) {
view.parentView = (parentView || null);
view.isCreated = true;
if (forExpansion)
view.isCreatedForExpansion = true;
view._isCreatedForExpansion = true;
Blaze._fireCallbacks(view, 'created');
};
@@ -173,32 +168,18 @@ Blaze._materializeView = function (view, parentView) {
Blaze._createView(view, parentView);
var domrange;
var needsRenderedCallback = false;
var scheduleRenderedCallback = function () {
if (needsRenderedCallback && ! view.isDestroyed &&
view._callbacks.rendered && view._callbacks.rendered.length) {
Deps.afterFlush(function callRendered() {
if (needsRenderedCallback && ! view.isDestroyed) {
needsRenderedCallback = false;
Blaze._fireCallbacks(view, 'rendered');
}
});
}
};
var lastHtmljs;
// We don't expect to be called in a Computation, but just in case,
// wrap in Deps.nonreactive.
Deps.nonreactive(function () {
view.autorun(function doRender(c) {
// `view.autorun` sets the current view.
view.renderCount++;
view._isInRender = true;
// Any dependencies that should invalidate this Computation come
// from this line:
view.renderCount++;
view.isInRender = true;
var htmljs = view._render();
view.isInRender = false;
view._isInRender = false;
Deps.nonreactive(function doMaterialize() {
var materializer = new Blaze._DOMMaterializer({parentView: view});
@@ -211,10 +192,7 @@ Blaze._materializeView = function (view, parentView) {
} else {
domrange.setMembers(rangesAndNodes);
}
Blaze._fireCallbacks(view, 'materialized');
needsRenderedCallback = true;
if (! c.firstRun)
scheduleRenderedCallback();
Blaze._fireCallbacks(view, 'rendered');
}
});
lastHtmljs = htmljs;
@@ -235,8 +213,6 @@ Blaze._materializeView = function (view, parentView) {
element, function teardown() {
Blaze._destroyView(view, true /* _skipNodes */);
});
scheduleRenderedCallback();
});
// tear down the teardown hook
@@ -261,11 +237,11 @@ Blaze._materializeView = function (view, parentView) {
Blaze._expandView = function (view, parentView) {
Blaze._createView(view, parentView, true /*forExpansion*/);
view.isInRender = true;
view._isInRender = true;
var htmljs = Blaze._withCurrentView(view, function () {
return view._render();
});
view.isInRender = false;
view._isInRender = false;
var result = Blaze._expand(htmljs, view);
@@ -315,7 +291,7 @@ Blaze._HTMLJSExpander.def({
// (i.e. we are in its render() method).
var currentViewIfRendering = function () {
var view = Blaze.currentView;
return (view && view.isInRender) ? view : null;
return (view && view._isInRender) ? view : null;
};
Blaze._expand = function (htmljs, parentView) {

View File

@@ -10,21 +10,29 @@ if (Meteor.isClient) {
});
v.onViewCreated(function () {
buf += 'c';
buf += 'c' + v.renderCount;
});
v.onViewRendered(function () {
buf += 'r' + v.renderCount;
});
v.onViewDestroyed(function () {
buf += 'd';
buf += 'd' + v.renderCount;
});
test.equal(buf, '');
var div = document.createElement("DIV");
Blaze.insert(Blaze.render(v), div);
test.equal(buf, 'c');
test.equal(buf, 'c0r1');
test.equal(canonicalizeHtml(div.innerHTML), "foo");
R.set("bar");
Deps.flush();
test.equal(buf, 'c0r1r2');
test.equal(canonicalizeHtml(div.innerHTML), "bar");
Blaze.remove(v);
test.equal(buf, 'cd');
test.equal(buf, 'c0r1r2d2');
test.equal(canonicalizeHtml(div.innerHTML), "");
});

View File

@@ -457,58 +457,71 @@ Tinytest.add("ui - render - reactive attributes", function (test) {
})();
});
Tinytest.add("ui - render - views", function (test) {
Tinytest.add("ui - render - templates and views", function (test) {
(function () {
var counter = 1;
var buf = [];
var makeView = function () {
var view = Blaze.View('myView', function () {
var myTemplate = Blaze.Template(
'myTemplate',
function () {
return [String(this.number),
(this.number < 3 ? makeView() : HR())];
});
var number = counter++;
myTemplate.constructView = function (number) {
var view = Template.prototype.constructView.call(this);
view.number = number;
return view;
};
view.onViewCreated(function () {
var parent = Blaze.getParentView(view, 'myView');
if (parent) {
buf.push('parent of ' + view.number + ' is ' +
parent.number);
}
myTemplate.created = function () {
test.isFalse(Deps.active);
var view = this.view;
var parent = Blaze.getParentView(view, 'myTemplate');
if (parent) {
buf.push('parent of ' + view.number + ' is ' +
parent.number);
}
buf.push('created ' + Blaze.getCurrentData());
buf.push('created ' + Blaze.getCurrentData());
};
myTemplate.rendered = function () {
test.isFalse(Deps.active);
var nodeDescr = function (node) {
if (node.nodeType === 8) // comment
return '';
if (node.nodeType === 3) // text
return node.nodeValue;
return node.nodeName;
};
var view = this.view;
var start = view.domrange.firstNode();
var end = view.domrange.lastNode();
// skip marker nodes
while (start !== end && ! nodeDescr(start))
start = start.nextSibling;
while (end !== start && ! nodeDescr(end))
end = end.previousSibling;
buf.push('dom-' + Blaze.getCurrentData() +
' is ' + nodeDescr(start) +'..' +
nodeDescr(end));
};
myTemplate.destroyed = function () {
test.isFalse(Deps.active);
buf.push('destroyed ' + Blaze.getCurrentData());
};
var makeView = function () {
var number = counter++;
return Blaze.With(number, function () {
return myTemplate.constructView(number);
});
view.onRendered(function () {
var nodeDescr = function (node) {
if (node.nodeType === 8) // comment
return '';
if (node.nodeType === 3) // text
return node.nodeValue;
return node.nodeName;
};
var start = this.domrange.firstNode();
var end = this.domrange.lastNode();
// skip marker nodes
while (start !== end && ! nodeDescr(start))
start = start.nextSibling;
while (end !== start && ! nodeDescr(end))
end = end.previousSibling;
buf.push('dom-' + Blaze.getCurrentData() +
' is ' + nodeDescr(start) +'..' +
nodeDescr(end));
});
view.onViewDestroyed(function () {
buf.push('destroyed ' + Blaze.getCurrentData());
});
return Blaze.With(number, function () { return view; });
};
var div = document.createElement("DIV");