diff --git a/text/0418-deprecate-route-render-methods.md b/text/0418-deprecate-route-render-methods.md new file mode 100644 index 0000000000..f357ad4f3d --- /dev/null +++ b/text/0418-deprecate-route-render-methods.md @@ -0,0 +1,273 @@ +- Start Date: 2018-19-12 +- RFC PR: https://github.com/emberjs/rfcs/pull/418 +- Ember Issue: (leave this empty) + +# Deprecate Route render APIs + +## Summary + +This RFC proposses the deprecation of [`Route#render`](https://emberjs.com/api/ember/3.6/classes/Route/methods/render?anchor=render), [`Route#renderTemplate`](https://emberjs.com/api/ember/3.6/classes/Route/methods/render?anchor=renderTemplate) and named `{{outlet}}` APIs. The following deprecation message will be emitted upon usage of `render` or `renderTemplate`: + +``` +The usage of `renderTemplate` is deprecated. Please see the following deprecation guide to migrate. +``` +and + +``` +The usage of `render` is deprecated. Please see the following deprecation guide to migrate. +``` + +The following will be compile time deprecation for named outlets: + +``` +Please refactor `{{outlet }}` to a component . +``` + +## Motivation + +These APIs are largely holdovers from a time where components where not as prominent in your typical Ember application. While they are still documented, these APIs created an interesting coupling between the `Route` and the template. These APIs are also prone to breaking conventional naming conventions, which can lead to confusion for developers. Another issue is that it is unclear how something like this works with route based tree shaking, as there are no strong conventions or direct imports as to what is actually being used. + +## Transition Path + +The migration plan here is going to be somewhat situational based on the UI that was being constructed. For cases where named outlets were being used it is likely that they should just be moved to components. For cases where you were escaping the existing DOM hierarchy to render a template somewhere else in the DOM, one should use the built-in [`{{in-element}}`](https://github.com/emberjs/rfcs/blob/master/text/0287-promote-in-element-to-public-api.md) helper or an addon like [ember-elsewhere](https://github.com/ef4/ember-elsewhere). Below are some example of how a migration would look. + +__Migrating Named Outlets__ + +Given: + +```js +Ember.Route.extend({ + // ... + + renderTemplate() { + this.render('cart', { + into: 'checkout', + outlet: 'cart', + controller: 'cart' + }) + } +}) +``` + +```hbs +{{! checkout.hbs}} +
+ {{outlet}} +
+ +``` + +This would tell Ember to render `cart.hbs` into `checkout.hbs` at the `{{outlet "cart"}}` and use the `cart` controller to back the `cart.hbs` template. This is pretty confusing pattern and creates this implicit coupling that is spread between the `Route` and template. + +Luckily, we can express this entire concept with components. + +```hbs +{{! checkout.hbs}} +
+ {{outlet}} +
+ +``` + +In the event you were using `model` to derive what to render, you can us the `{{component}}` helper to dynamically render a component. + +__Migrating Hiearchy Escaping__ + +Given: + +```hbs +{{! app/templates/authenticated.hbs}} + + + +
+ {{outlet}} +
+``` + +```hbs +{{! app/templates/account.hbs }} +{{#link-to 'account'}} + {{this.name}} />
+{{/link-to}}
+```
+
+```js
+// app/routes/authenticated.js
+import Route from '@ember/route';
+import { inject as service } from '@ember/service';
+
+export default Route.extend({
+  // ...
+  user: service('user'),
+  renderTemplate() {
+    if (this.user.isLoggedIn) {
+      this.render('account', {
+        into: 'applcation',
+        outlet: 'account',
+        controller: 'account'
+      });
+    } else {
+      this.transitionTo('login')
+    }
+  }
+});
+```
+
+One way this could be migrated is like the following:
+
+```hbs
+{{! app/templates/authenticated.hbs}}
+
+<nav>
+  <h1>ACME Corp.</h1>
+  <section id= + + +
+ {{outlet}} +
+``` + +```hbs +{{! app/templates/authenticated/campaigns.hbs }} + +{{outlet}} + +{{#in-element this.accountPlaceholder}} + {{#link-to 'account'}} + {{this.account.name}} />
+  {{/link-to}}
+{{/in-element}}
+```
+
+```js
+// app/routes/authenticated.js
+import Route from '@ember/route';
+import { inject as service } from '@ember/service';
+
+export default Route.extend({
+  //...
+  user: service('user'),
+  beforeModel() {
+    if (!this.user.isLoggedIn) {
+      this.transitionTo('login')
+    }
+  }
+});
+```
+
+```js
+// app/controller/authenticated/campaigns.js
+import Route from '@ember/route';
+import Controller from '@ember/controller';
+import { inject as service } from '@ember/service';
+import { inject as controller } from '@ember/controller';
+
+export default Controller.extend({
+  //...
+  account: controller('account')
+  init() {
+    this._super(...arguments);
+    this.accountPlaceholder = document.getElementById('account-placeholder');
+  }
+});
+```
+
+If you want to do this with components you could do the same thing as the following:
+
+```hbs
+{{! app/templates/authenticated.hbs}}
+
+<nav>
+  <h1>ACME Corp.</h1>
+  <section id= + + +
+ {{outlet}} +
+``` + +```hbs +{{! app/templates/authenticated/campaigns.hbs }} +{{outlet}} + + +``` + +```hbs +{{! app/templates/components/user-account.hbs }} +{{#in-element this.accountPlaceholder}} + {{#link-to 'account'}} +