/ ember-js

Good EmberJs DOM practices

1 - The idea

As a regular JavaScript framework, EmberJs provide us a good way to be DRY through component concept.
As a developer with poor notions of HTML and CSS but with a good background of other languages, the idea of a reusable component sound great and I was quickly comfortable with them.

Some times ago, I needed to promptly build a new front, and as a bad CSS user, I turned my self to a CSS framework (check out sementic-ui). If you are not familiar with the concept: it allows you with just a good DOM organization and some class names to have a neat rendering.

2 - The problem

Now let's have a template index.hbs

{{!-- ... --}}
<div class='segment'>
    <div class='content'>Hey, this is an awesome site builded so fast thanks to sementic ui</div>
    <h3> big thanks to :</h3>
    <div class='label list'>
        {{each authors as |author|}}
            {{author/label model=author}}
        {{/each}}
    </div>
</div>
{{!-- ... --}}

And author/label.hbs (which leads to our problem)

<div class='label'>
    {{model.name}}
</div>

So far so good. But the rendering was bad. Why ? Let's take a look at the rendered DOM:

<!-- ... -->
<div class="segment">
    <div class="content">Hey, this is an awesome site builded so fast thanks to sementic ui</div>
    <h3> big thanks to :</h3>
    <div class="labels list">
        <div id="ember2158" class="ember-view">
            <div class="label">
                Arth
            </div>
        </div>
        <div id="ember2165" class="ember-view">
            <div class="label">
                Shark
            </div>
        </div>
        <div id="ember2172" class="ember-view">
            <div class="label">
                Ernlyne
            </div>
        </div>
    </div>
</div>
<!-- ... -->

The CSS framework works with a precise DOM organization. And the wrapper div classes as labels list is "waiting" for directs child elements with "label" class.
But the EmberJs component engine systematically encapsulate it with a div with a random emberXXXX id and some class names. And it'sactuallyy breaking my DOM and moreover my (or the framework's) CSS logic.

And think more about it when we should have a 5 or 6 depth components calls...

3 - What we want

The perfect rendered html should looks like that :

<!-- ... -->
<div class="segment">
    <div class="content">Hey, this is an awesome site builded so fast thanks to sementic ui</div>
    <h3> big thanks to :</h3>
    <div class='labels list'>
        <div class='label'>
            Arth
        </div>
        <div class='label'>
            Shark
        </div>
        <div class='label'>
            Ernlyne
        </div>
    </div>
</div>
<!-- ... -->

This looks good, clean, lighter etc ... We don't want thoose extra encapsulating DOM elements.

4 - The solution(s)

It appears that Ember provides many ways to custom all those component's basic DOM elements.

For example, knowing that we can explicitly set the tagName attribute, we can set it to an empty string, with will simply result to what we want ('erasing' the encapsulating div). But it appears to be a bad practice given the fact that you amputate your component some possibilities such as to manipulate the component DOM through the this.$ function...

The second idea, and the good one, is simply to get rid of our component <div class='label'> element, and to set the component itself class to 'label' :

{{!-- author/label.hbs --}}
{{model.name}}
// author/label.js
import { Component } from 'ember';

export default Component.extend({
    classNames: ['label'],
});

By doing this, we finnaly reach our goal to have the following HTML rendering :

<!--...-->
     <div class='labels list'>
        <div id="ember510" class=' ember-view label'>
            Arth
        </div>
        <div id="ember393" class='ember-view label'>
            Shark
        </div>
        <div id="ember225" class='ember-view  label'>
            Ernlyne
        </div>
    </div>
<!--...-->

And our css framework can now do it's magic.

5 - Go futher.

This said, It would now appear to you that if your template.hbs component start with a <watevertag> and finish by this same </watevertag> closing tag of the same section, there is probably a better way to doing this ( => remove it and change the classNames and maybe tagName properties).

Finaly, here are all basic DOM customization that ember allowed you to do :

// author/label.js
import { Component, Computed } from 'ember';

export default Component.extend({
    // array of strings which will be displayed as it is as classes of the component wrapper element
    classNames: ['label', 'mobile-hidden'], 
    
    // array of strings which represents the computed values itself or the className you provide if or else it's truthy
    classNameBindings ['isTeacher', 'isGirl:is-women:is-boy', 'myProperty'],
    myProperty: Computed(() => 'my-awesome-class' if true)
    // if isTeacher and !isGirl : classNames+='my-awesome-class is-teacher is-boy'
    // if !isTeacher and isGirl : classNames+='my-awesome-class is-women'

    tagName: 'tr' // tag which will be used, if empty, render without wrapper
    
    id: 'charles' // you can set the id attr to, but don't use a static string, or it's no more an id ...
    
    // you can even dynamically set some DOM attributes ! (if related property is null, the attribute isn't rendered (even with empty value))
    attributeBindings: ['href'],
    href: 'http://emberjs.com'
});

Check out this EmberJs RFC for another aproach (only under canary version for now).

nathan gouy

nathan gouy

!! > Looking For a Job in Canada < !! Currently working at diduenjoy as a full stack Js/Ruby lead dev. Very curious and creative, I express it through development, cooking and DIY.

Read More
Good EmberJs DOM practices
Share this

Subscribe to Nathan gouy Blog