Skip to content

Commit

Permalink
💀 orphaned outlets
Browse files Browse the repository at this point in the history
  • Loading branch information
lorcan committed Dec 2, 2017
1 parent 8724cf1 commit 767d469
Show file tree
Hide file tree
Showing 4 changed files with 4 additions and 288 deletions.
8 changes: 0 additions & 8 deletions packages/ember-glimmer/lib/component-managers/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ class SingletonRenderManager extends AbstractRenderManager {
this._pushToDebugStack(`controller:${name} (with the render helper)`, env);
}

if (dynamicScope.rootOutletState) {
dynamicScope.outletState = dynamicScope.rootOutletState.getOrphan(name);
}

return { controller } as RenderState;
}

Expand All @@ -76,10 +72,6 @@ class NonSingletonRenderManager extends AbstractRenderManager {
this._pushToDebugStack(`controller:${name} (with the render helper)`, environment);
}

if (dynamicScope.rootOutletState) {
dynamicScope.outletState = dynamicScope.rootOutletState.getOrphan(name);
}

return { controller, model: modelRef };
}

Expand Down
39 changes: 0 additions & 39 deletions packages/ember-glimmer/lib/views/outlet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,50 +22,11 @@ export class RootOutletStateReference implements VersionedPathReference<Option<O
return this.outletView.outletState;
}

getOrphan(name: string): VersionedPathReference<Option<OutletState>> {
return new OrphanedOutletStateReference(this, name);
}

update(state: OutletState) {
this.outletView.setOutletState(state);
}
}

// So this is a relic of the past that SHOULD go away
// in 3.0. Preferably it is deprecated in the release that
// follows the Glimmer release.
class OrphanedOutletStateReference extends RootOutletStateReference {
public root: any;
public name: string;

constructor(root: RootOutletStateReference, name: string) {
super(root.outletView);
this.root = root;
this.name = name;
}

value(): Option<OutletState> {
let rootState = this.root.value();

let orphans = rootState.outlets.main.outlets.__ember_orphans__;

if (!orphans) {
return null;
}

let matched = orphans.outlets[this.name];

if (!matched) {
return null;
}

let state = Object.create(null);
state[matched.render.outlet] = matched;
matched.wasUsed = true;
return { outlets: state, render: undefined };
}
}

class ChildOutletStateReference implements VersionedPathReference<any> {
public parent: VersionedPathReference<any>;
public key: string;
Expand Down
40 changes: 2 additions & 38 deletions packages/ember-routing/lib/system/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -1491,51 +1491,15 @@ function appendLiveRoute(liveRoutes, defaultParentState, renderOptions) {
if (target) {
set(target.outlets, renderOptions.outlet, myState);
} else {
if (renderOptions.into) {
deprecate(
`Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated.`,
false,
{
id: 'ember-routing.top-level-render-helper',
until: '3.0.0',
url: 'https://emberjs.com/deprecations/v2.x/#toc_rendering-into-a-render-helper-that-resolves-to-an-outlet'
}
);

// Megahax time. Post-3.0-breaking-changes, we will just assert
// right here that the user tried to target a nonexistent
// thing. But for now we still need to support the `render`
// helper, and people are allowed to target templates rendered
// by the render helper. So instead we defer doing anyting with
// these orphan renders until afterRender.
appendOrphan(liveRoutes, renderOptions.into, myState);
} else {
liveRoutes = myState;
}
assert(`Cannot render into a {{render}} helper '${renderOptions.into}' that resolves to an {{outlet}}`, !renderOptions.into);
liveRoutes = myState;
}
return {
liveRoutes,
ownState: myState
};
}

function appendOrphan(liveRoutes, into, myState) {
if (!liveRoutes.outlets.__ember_orphans__) {
liveRoutes.outlets.__ember_orphans__ = {
render: {
name: '__ember_orphans__'
},
outlets: Object.create(null)
};
}
liveRoutes.outlets.__ember_orphans__.outlets[into] = myState;
run.schedule('afterRender', () => {
// `wasUsed` gets set by the render helper.
assert(`You attempted to render into '${into}' but it was not found`,
liveRoutes.outlets.__ember_orphans__.outlets[into].wasUsed);
});
}

function representEmptyRoute(liveRoutes, defaultParentState, route) {
// the route didn't render anything
let alreadyAppended = findLiveRoute(liveRoutes, route.routeName);
Expand Down
205 changes: 2 additions & 203 deletions packages/ember/tests/routing/basic_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1931,10 +1931,7 @@ QUnit.test('Route should tear down multiple outlets', function() {
equal(jQuery('div.posts-footer:contains(postsFooter)', '#qunit-fixture').length, 0, 'The posts/footer template was removed');
});


QUnit.test('Route will assert if you try to explicitly render {into: ...} a missing template', function () {
expectDeprecation(/Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated./);

QUnit.test('Route will assert if you try to explicitly render {into: ...} a {{render}} helper that resolves to an {{outlet}}', function () {
Router.map(function() {
this.route('home', { path: '/' });
});
Expand All @@ -1945,7 +1942,7 @@ QUnit.test('Route will assert if you try to explicitly render {into: ...} a miss
}
});

expectAssertion(() => bootApplication(), 'You attempted to render into \'nonexistent\' but it was not found');
expectAssertion(() => bootApplication(), 'Cannot render into a {{render}} helper \'nonexistent\' that resolves to an {{outlet}}');
});

QUnit.test('Route supports clearing outlet explicitly', function() {
Expand Down Expand Up @@ -3083,204 +3080,6 @@ QUnit.test('Allows any route to disconnectOutlet another route\'s templates', fu
equal(trim(jQuery('#qunit-fixture').text()), 'hi');
});

QUnit.test('Can this.render({into:...}) the render helper', function() {
expectDeprecation(/Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated./);

expectDeprecation(() => {
setTemplate('application', compile('{{render "sidebar"}}'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

setTemplate('sidebar', compile('<div class="sidebar">{{outlet}}</div>'));
setTemplate('index', compile('other'));
setTemplate('bar', compile('bar'));

App.IndexRoute = Route.extend({
renderTemplate() {
this.render({ into: 'sidebar' });
},
actions: {
changeToBar() {
this.disconnectOutlet({
parentView: 'sidebar',
outlet: 'main'
});
this.render('bar', { into: 'sidebar' });
}
}
});

bootApplication();
equal(jQuery('#qunit-fixture .sidebar').text(), 'other');
run(router, 'send', 'changeToBar');
equal(jQuery('#qunit-fixture .sidebar').text(), 'bar');
});

QUnit.test('Can disconnect from the render helper', function() {
expectDeprecation(/Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated./);

expectDeprecation(() => {
setTemplate('application', compile('{{render "sidebar"}}'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

setTemplate('sidebar', compile('<div class="sidebar">{{outlet}}</div>'));
setTemplate('index', compile('other'));

App.IndexRoute = Route.extend({
renderTemplate() {
this.render({ into: 'sidebar' });
},
actions: {
disconnect: function() {
this.disconnectOutlet({
parentView: 'sidebar',
outlet: 'main'
});
}
}
});

bootApplication();
equal(jQuery('#qunit-fixture .sidebar').text(), 'other');
run(router, 'send', 'disconnect');
equal(jQuery('#qunit-fixture .sidebar').text(), '');
});

QUnit.test('Can this.render({into:...}) the render helper\'s children', function() {
expectDeprecation(/Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated./);

expectDeprecation(() => {
setTemplate('application', compile('{{render "sidebar"}}'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

setTemplate('sidebar', compile('<div class="sidebar">{{outlet}}</div>'));
setTemplate('index', compile('<div class="index">{{outlet}}</div>'));
setTemplate('other', compile('other'));
setTemplate('bar', compile('bar'));

App.IndexRoute = Route.extend({
renderTemplate() {
this.render({ into: 'sidebar' });
this.render('other', { into: 'index' });
},
actions: {
changeToBar() {
this.disconnectOutlet({
parentView: 'index',
outlet: 'main'
});
this.render('bar', { into: 'index' });
}
}
});

bootApplication();
equal(jQuery('#qunit-fixture .sidebar .index').text(), 'other');
run(router, 'send', 'changeToBar');
equal(jQuery('#qunit-fixture .sidebar .index').text(), 'bar');
});

QUnit.test('Can disconnect from the render helper\'s children', function() {
expectDeprecation(/Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated./);

expectDeprecation(() => {
setTemplate('application', compile('{{render "sidebar"}}'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

setTemplate('sidebar', compile('<div class="sidebar">{{outlet}}</div>'));
setTemplate('index', compile('<div class="index">{{outlet}}</div>'));
setTemplate('other', compile('other'));

App.IndexRoute = Route.extend({
renderTemplate() {
this.render({ into: 'sidebar' });
this.render('other', { into: 'index' });
},
actions: {
disconnect() {
this.disconnectOutlet({
parentView: 'index',
outlet: 'main'
});
}
}
});

bootApplication();
equal(jQuery('#qunit-fixture .sidebar .index').text(), 'other');
run(router, 'send', 'disconnect');
equal(jQuery('#qunit-fixture .sidebar .index').text(), '');
});

QUnit.test('Can this.render({into:...}) nested render helpers', function() {
expectDeprecation(/Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated./);

expectDeprecation(() => {
setTemplate('application', compile('{{render "sidebar"}}'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

expectDeprecation(() => {
setTemplate('sidebar', compile('<div class="sidebar">{{render "cart"}}</div>'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

setTemplate('cart', compile('<div class="cart">{{outlet}}</div>'));
setTemplate('index', compile('other'));
setTemplate('baz', compile('baz'));

App.IndexRoute = Route.extend({
renderTemplate() {
this.render({ into: 'cart' });
},
actions: {
changeToBaz() {
this.disconnectOutlet({
parentView: 'cart',
outlet: 'main'
});
this.render('baz', { into: 'cart' });
}
}
});

bootApplication();
equal(jQuery('#qunit-fixture .cart').text(), 'other');
run(router, 'send', 'changeToBaz');
equal(jQuery('#qunit-fixture .cart').text(), 'baz');
});

QUnit.test('Can disconnect from nested render helpers', function() {
expectDeprecation(/Rendering into a {{render}} helper that resolves to an {{outlet}} is deprecated./);

expectDeprecation(() => {
setTemplate('application', compile('{{render "sidebar"}}'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

expectDeprecation(() => {
setTemplate('sidebar', compile('<div class="sidebar">{{render "cart"}}</div>'));
}, /Please refactor [\w\{\}"` ]+ to a component/);

setTemplate('cart', compile('<div class="cart">{{outlet}}</div>'));
setTemplate('index', compile('other'));

App.IndexRoute = Route.extend({
renderTemplate() {
this.render({ into: 'cart' });
},
actions: {
disconnect() {
this.disconnectOutlet({
parentView: 'cart',
outlet: 'main'
});
}
}
});

bootApplication();
equal(jQuery('#qunit-fixture .cart').text(), 'other');
run(router, 'send', 'disconnect');
equal(jQuery('#qunit-fixture .cart').text(), '');
});

QUnit.test('Components inside an outlet have their didInsertElement hook invoked when the route is displayed', function(assert) {
setTemplate('index', compile('{{#if showFirst}}{{my-component}}{{else}}{{other-component}}{{/if}}'));

Expand Down

0 comments on commit 767d469

Please sign in to comment.