Skip to content

Commit

Permalink
Merge pull request #11153 from emberjs/support-parent-rerenders
Browse files Browse the repository at this point in the history
Support parent rerenders
  • Loading branch information
rwjblue committed May 13, 2015
2 parents a0ae04f + 467eb6d commit bfcc15e
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 9 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"express": "^4.5.0",
"github": "^0.2.3",
"glob": "~4.3.2",
"htmlbars": "0.13.18",
"htmlbars": "0.13.19",
"qunit-extras": "^1.3.0",
"qunitjs": "^1.16.0",
"route-recognizer": "0.1.5",
Expand Down
2 changes: 2 additions & 0 deletions packages/ember-htmlbars/lib/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import getValue from "ember-htmlbars/hooks/get-value";
import getCellOrValue from "ember-htmlbars/hooks/get-cell-or-value";
import cleanupRenderNode from "ember-htmlbars/hooks/cleanup-render-node";
import destroyRenderNode from "ember-htmlbars/hooks/destroy-render-node";
import didRenderNode from "ember-htmlbars/hooks/did-render-node";
import willCleanupTree from "ember-htmlbars/hooks/will-cleanup-tree";
import didCleanupTree from "ember-htmlbars/hooks/did-cleanup-tree";
import classify from "ember-htmlbars/hooks/classify";
Expand Down Expand Up @@ -53,6 +54,7 @@ merge(emberHooks, {
destroyRenderNode: destroyRenderNode,
willCleanupTree: willCleanupTree,
didCleanupTree: didCleanupTree,
didRenderNode: didRenderNode,
classify: classify,
component: component,
lookupHelper: lookupHelper,
Expand Down
3 changes: 3 additions & 0 deletions packages/ember-htmlbars/lib/hooks/did-render-node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function didRenderNode(morph, env) {
env.renderedNodes[morph.guid] = true;
}
4 changes: 2 additions & 2 deletions packages/ember-htmlbars/lib/hooks/link-render-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ export default function linkRenderNode(renderNode, env, scope, path, params, has

if (params && params.length) {
for (var i = 0; i < params.length; i++) {
subscribe(renderNode, scope, params[i]);
subscribe(renderNode, env, scope, params[i]);
}
}

if (hash) {
for (var key in hash) {
subscribe(renderNode, scope, hash[key]);
subscribe(renderNode, env, scope, hash[key]);
}
}

Expand Down
6 changes: 6 additions & 0 deletions packages/ember-htmlbars/lib/morphs/morph.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import DOMHelper from "dom-helper";
import o_create from 'ember-metal/platform/create';

var HTMLBarsMorph = DOMHelper.prototype.MorphClass;
let guid = 1;

function EmberMorph(DOMHelper, contextualElement) {
this.HTMLBarsMorph$constructor(DOMHelper, contextualElement);

this.emberView = null;
this.emberToDestroy = null;
this.streamUnsubscribers = null;
this.guid = guid++;

// A component can become dirty either because one of its
// attributes changed, or because it was re-rendered. If any part
Expand Down Expand Up @@ -49,4 +51,8 @@ proto.cleanup = function() {
}
};

proto.didRender = function(env, scope) {
env.renderedNodes[this.guid] = true;
};

export default EmberMorph;
1 change: 1 addition & 0 deletions packages/ember-htmlbars/lib/system/render-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export function renderHTMLBarsBlock(view, block, renderNode) {
var env = {
lifecycleHooks: [],
renderedViews: [],
renderedNodes: {},
view: view,
outletState: view.outletState,
container: view.container,
Expand Down
6 changes: 3 additions & 3 deletions packages/ember-htmlbars/lib/utils/subscribe.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { isStream } from "ember-metal/streams/utils";
import { isStream, labelFor } from "ember-metal/streams/utils";

export default function subscribe(node, scope, stream) {
export default function subscribe(node, env, scope, stream) {
if (!isStream(stream)) { return; }
var component = scope.component;
var unsubscribers = node.streamUnsubscribers = node.streamUnsubscribers || [];
Expand All @@ -21,6 +21,6 @@ export default function subscribe(node, scope, stream) {
node.shouldReceiveAttrs = true;
}

node.ownerNode.emberView.scheduleRevalidate();
node.ownerNode.emberView.scheduleRevalidate(node, labelFor(stream));
}));
}
104 changes: 104 additions & 0 deletions packages/ember-htmlbars/tests/integration/component_invocation_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -451,3 +451,107 @@ QUnit.test('implementing `render` allows pushing into a string buffer', function

equal(view.$('#zomg').text(), 'Whoop!');
});

QUnit.test("comopnent should rerender when a property is changed during children's rendering", function() {
expectDeprecation(/twice in a single render/);

var outer, middle;

registry.register('component:x-outer', Component.extend({
value: 1,
grabReference: Ember.on('init', function() {
outer = this;
})
}));

registry.register('component:x-middle', Component.extend({
grabReference: Ember.on('init', function() {
middle = this;
})
}));

registry.register('component:x-inner', Component.extend({
pushDataUp: Ember.observer('value', function() {
middle.set('value', this.get('value'));
})
}));

registry.register('template:components/x-outer', compile('{{#x-middle}}{{x-inner value=value}}{{/x-middle}}'));
registry.register('template:components/x-middle', compile('<div id="middle-value">{{value}}</div>{{yield}}'));
registry.register('template:components/x-inner', compile('<div id="inner-value">{{value}}</div>'));


view = EmberView.extend({
template: compile('{{x-outer}}'),
container: container
}).create();

runAppend(view);

equal(view.$('#inner-value').text(), '1', 'initial render of inner');
equal(view.$('#middle-value').text(), '1', 'initial render of middle');

run(() => outer.set('value', 2));

equal(view.$('#inner-value').text(), '2', 'second render of inner');
equal(view.$('#middle-value').text(), '2', 'second render of middle');

run(() => outer.set('value', 3));

equal(view.$('#inner-value').text(), '3', 'third render of inner');
equal(view.$('#middle-value').text(), '3', 'third render of middle');

});

QUnit.test("comopnent should rerender when a property (with a default) is changed during children's rendering", function() {
expectDeprecation(/modified value twice in a single render/);

var outer, middle;

registry.register('component:x-outer', Component.extend({
value: 1,
grabReference: Ember.on('init', function() {
outer = this;
})
}));

registry.register('component:x-middle', Component.extend({
value: null,
grabReference: Ember.on('init', function() {
middle = this;
})
}));

registry.register('component:x-inner', Component.extend({
value: null,
pushDataUp: Ember.observer('value', function() {
middle.set('value', this.get('value'));
})
}));

registry.register('template:components/x-outer', compile('{{#x-middle}}{{x-inner value=value}}{{/x-middle}}'));
registry.register('template:components/x-middle', compile('<div id="middle-value">{{value}}</div>{{yield}}'));
registry.register('template:components/x-inner', compile('<div id="inner-value">{{value}}</div>'));


view = EmberView.extend({
template: compile('{{x-outer}}'),
container: container
}).create();

runAppend(view);

equal(view.$('#inner-value').text(), '1', 'initial render of inner');
equal(view.$('#middle-value').text(), '', 'initial render of middle (observers do not run during init)');

run(() => outer.set('value', 2));

equal(view.$('#inner-value').text(), '2', 'second render of inner');
equal(view.$('#middle-value').text(), '2', 'second render of middle');

run(() => outer.set('value', 3));

equal(view.$('#inner-value').text(), '3', 'third render of inner');
equal(view.$('#middle-value').text(), '3', 'third render of middle');

});
1 change: 1 addition & 0 deletions packages/ember-metal-views/lib/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ Renderer.prototype.ensureViewNotRendering =

Renderer.prototype.clearRenderedViews =
function Renderer_clearRenderedViews(env) {
env.renderedNodes = {};
env.renderedViews.length = 0;
};

Expand Down
2 changes: 1 addition & 1 deletion packages/ember-routing-views/lib/views/outlet.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export var CoreOutletView = View.extend({
this.dirtyOutlets();
this._outlets = [];

this.scheduleRevalidate();
this.scheduleRevalidate(null, null);
}
},

Expand Down
3 changes: 2 additions & 1 deletion packages/ember-views/lib/views/states/has_element.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ merge(hasElement, {
}
node.isDirty = true;
});
renderNode.ownerNode.emberView.scheduleRevalidate();

renderNode.ownerNode.emberView.scheduleRevalidate(renderNode, view.toString(), "rerendering");
},

cleanup(view) {
Expand Down
12 changes: 11 additions & 1 deletion packages/ember-views/lib/views/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -1330,7 +1330,17 @@ var View = CoreView.extend(
this.scheduledRevalidation = false;
},

scheduleRevalidate() {
scheduleRevalidate(node, label, manualRerender) {
if (node && !this._dispatching && node.guid in this.env.renderedNodes) {
if (manualRerender) {
Ember.deprecate(`You manually rerendered ${label} (a parent component) from a child component during the rendering process. This rarely worked in Ember 1.x and will be removed in Ember 2.0`);
} else {
Ember.deprecate(`You modified ${label} twice in a single render. This was unreliable in Ember 1.x and will be removed in Ember 2.0`);
}
run.scheduleOnce('render', this, this.revalidate);
return;
}

Ember.deprecate(`A property of ${this} was modified inside the ${this._dispatching} hook. You should never change properties on components, services or models during ${this._dispatching} because it causes significant performance degradation.`, !this._dispatching);

if (!this.scheduledRevalidation || this._dispatching) {
Expand Down

0 comments on commit bfcc15e

Please sign in to comment.