Layout property for declaring HTML in component?

I just read the EmberConf 2017 blog post, and found out about ember-redux for the first time (which I have other questions about how many people use this instead of Ember Data), but what really intrigued me is this syntax, as I like the idea of having the component’s template and logic in one file:

export default Ember.Component.extend({
    number: Ember.computed(function() {
        return store.getState();
    }),
    actions: {
        add: function() {
            store.dispatch({type: 'ADD'});
        }
    },
    layout: hbs`  //<=== WHAT IS THIS????
      {{number}}
      <button onclick={{action "add"}}>add</button>
    `
});

I had no idea about this property, despite working on an Ember app for the past 2 years. My question is, is this meant to be a substitute for a template.hbs file? Or is it only for very simple components, or for something else?

2 Likes

You’re asking a good question — I definitely had the O_o face the first time I saw it too. There’s a few pieces to this — I’ll try to answer all of them the best I can.

The Syntax The place you’ll typically see this ```hbs`Template```` stuff is in Component Integration tests. (Take a look at the guides to see what I’m talking about!)

The short about this syntax is: This is a way of writing templates inline with your javascript. This syntax will compile

The Layout Property When you have a component with a template, there is some glue under-the-hood that connects them together.

Let’s say you have a component at the path app/components/my-component.js. If you did not specify a layout property on that component, when it renders for the first time, it’s going to look for a template at the path app/templates/components/my-component.hbs

(Though, since the template is compiled to Javascript, it’ll technically be looking for app/templates/components/my-component.js, but you’ll never make that file yourself — ember-cli handles that).

You can, if you want, specify your own layout in a component. Just import the template yourself!

// components/my-component.js
import Ember from 'ember';
import layout from 'my-app/templates/components/some-other-template';

export default Ember.Component.extend({
  layout
});

In fact, addons have to do this because different Ember apps might resolve the location of the template differently so an add-on can’t know in advance how the template will be resolved (some apps might use pods, some might use a custom solution, who knows!).

Here’s an example of that from Ember Power Select: https://github.com/cibernox/ember-power-select/blob/master/addon/components/power-select.js#L3

The Babel Plugin The other weird thing going on is this thing called Tagged Template Literals. This is one of the lesser-known ES6 features that allows you to extend the functionality of javascript string templates.

In Ember land, this is done through an add-on (you’ll see it in your package.json!) called ember-cli-htmlbars-inline-precompile. This more or less amounts to a Babel plugin which (I think) adds a little more functionality to what Tagged Templates would do alone.


Hope this helps!

7 Likes

Having investigated React more in-depth recently, I first saw the tagged template literal syntax, and it clicked with me.

I guess what I’m really wondering is whether it’s a viable alternative to put all the component’s markup (and given that I think the direction is to go all components, no controllers) under the layout:hbs property, or would that cause any problems?

The only other thing we need is styled-components in Ember, and I’d be able to have single-file components (minus routes and models of course), although I guess with the layout property you could use atomic css principles (like Tachyons) to do it.

You can certainly do as ember-redux shows and put the template inside of the component definition. But, this does not make it any more react-like — in that the lexical scope of the template does not change by introducing it that way. (Whereas, React’s “templates” / JSX are really, truly javascript so they do have the same scope).

In a practical sense — this means that in order to do something along the lines of styled components, it won’t be as easy as it would be to do in React.

I’ve used ember-css-modules in the past which might give you some ideas around how this type of problem might be solved in the ember world. I’ve done some of this experimentation (GitHub - Ticketfly-UI/ember-css-composer: CSS Class Name Macros) as I’m interested in the answers to the same questions you’re asking about. FYI — That initial experiment didn’t really bear the fruit I was hoping for, so there might some spare parts to take from it but I don’t think it’s actually useable.

I would love to see something more like styled components — but for now, we’re probably sticking with something like ember-css-modules and see how that project develops.

Thanks for the extra info. I learned about ember-css-modules just yesterday, and liked that as well, so still not sure whether I’ll use that or Tachyons, but at least there’s some good alternatives now to what I did on my last project, which was global Bootstrap + customizations.

1 Like

I’ve just been looking into ember-redux as well, and that’s where I came across the inline layouting like you mentioned. One thought I had from a redux standpoint – For the “container components” (talked about in the ember-redux docs), it made it a lot easier with it all in one file like that. Since the slice of the state, and the actions that dispatch, are probably just being passed down to your child component as properties, it makes sense to have it all in one file instead of having to constantly click back-and-forth between .js and .hbs files to see what’s going on.

But then again, I’ve always wondered why Ember components were split into two files (js and hbs) when I have to switch back and forth to conceptualize the whole component… but I’m assuming there’s a good reason for that that I don’t know about.

This is a genuine BRUH moment. Thank you @Spencer_Price , very helpful!