Ember.js in-element feature
Tags: ember, feature
Published: 2020-08-10 06:55:29


> In this article, I am going to talk about a new feature of Ember.js on portal rendering which comes in V 3.20. Also, I am going to build a simple sample application using this feature. For more, let’s stay tuned.

Single-page applications use to load and render their code under one root DOM element, which brings a limitation for the developers. Say you need to render an element into your application. Easy right? That element is mounted to the nearest DOM element and rendered inside of it as a result. But! What if we want to render that element outside of the div somewhere else? That could be tricky because it breaks the convention that a component needs to render as a new element and follow a parent-child hierarchy. The parent wants to go where its child goes.

In a library like React.js developers can use `ReactDOM.createPortal(child, container)` to achieve this goal. In the Ember.js framework, developers could use community addons like [ember-wormhole](https://github.com/yapplabs/ember-wormhole) or [ember-elsewhere](https://github.com/ef4/ember-elsewhere).

Ember.js recently released a new version 3.20 which introduces a new helper for this approach. It’s `{{in-element}}`.

```hbs
{{#in-element this.myDestinationElement}}
  <div>Some content</div>
{{/in-element}}
```

To use the helper, pass in a DOM element to target (`this.myDestinationElement` in the example below) and a block to render:

This new public API behaves a little differently from the private API:
* For the public API `{{in-element}}`, by default, the rendered content will replace all the content of the destination, effectively becoming its innerHTML. If you want it to be appended instead of replacing the content, you can pass in `insertBefore=null`.
* In the private API `{{-in-element}}`, the rendered content was appended to any existing content in the destination.
Developers should use the public API, `{{in-element}}`, and discontinue using `{{-in-element}}`.

![ember.js in-element new feature](https://dev-to-uploads.s3.amazonaws.com/i/00sq1ozehq4329bpo248.png)


For using this helper, first, you need to update your ember to version 3.20. Try below command then

```shell
npm install -g ember-cli@3.20.0
```

And then you can create a new ember project and use this feature.


**Note:** By default, Ember renders the main application after all body elements, and appends the directory to `<body>`. So, if you suppose to render the outer element below your vendor resources file, it doesn’t work. As a result, if you suppose to render your outer elements, below your main application element, you should specify the `rootElement` of your Ember app located in `PATH-TO-PROJECT/app/app.js` and then you can add your outer element below that.

## Sample Use-case
To create a simple use-case of this new feature let create a really simple sample.

![ember.js in-element feature](https://dev-to-uploads.s3.amazonaws.com/i/u7zn4gsqaxqq1uqawsz8.png)

So first of all update to new ember version

```shell
npm install -g ember-cli@3.20.0
```

then create a new ember project

```shell
ember new emberjs-in-element-demo
```
So, then let create our outer element. for that you should edit `PROJECT/app/index.html` file as below

```diff
...
  <body>
    {{content-for "body"}}
+    <div id="modal_root"></div>

    <script src="{{rootURL}}assets/vendor.js"></script>
    <script src="{{rootURL}}assets/emberjs-in-element-demo.js"></script>

    {{content-for "body-footer"}}
  </body>
...
```
Then let create our desire component, and name it as `render-out`

```shell
ember g component render-out
ember g component-class render-out
```

Then we should specify our destination element in the component.

```javascript
// app/components/render-out.js
import Component from '@glimmer/component';

export default class RenderOutComponent extends Component {
  myDestinationElement = document.getElementById('modal_root')
}
```
and use the new helper 

```hbs
{{!-- app/components/render-out.hbs --}}
{{#if @renderOut }} 
  {{#in-element this.myDestinationElement}}
    {{yield}}
  {{/in-element}}
{{else}}
  {{yield}}
{{/if}}
```
To explain the above code, if a user passed the `@renderOut` property, then the passed children would render in the `<div id="modal_root"></div>` element, otherwise, it renders as before. 
So you can use it as

```hbs
{{!-- app/templates/application.hbs --}}
<RenderOut @renderOut=true>
  This content render inside of <div id="modal_root"></div>
</RenderOut>
```
You can check my sample code in [this repository](https://github.com/shayanypn/emberjs-in-element-demo).


### Resources
 * https://blog.emberjs.com/2020/07/29/ember-3-20-released.html
 * https://github.com/shayanypn/emberjs-in-element-demo
 * https://reactjs.org/docs/portals.html


### Join the discussion
I would love to get some feedback here.