6 Comments

Creating Reusable Page Layouts in Ember

Creating components is a great way to remove redundancy in Ember.js apps. For example, you might have a custom button that is used over and over in many different views but is defined only once. This is great, but what if you want to reuse an entire nested page layout instead? It’s easy to do with yields and some Ember magic.

The first step is to create a new component for the page.

Your .js file should look something like the code below, and you should have a property for each dynamic section.


import Ember from 'ember';

export default Ember.Component.extend({
  title: {isTitle: true},
  formGroups: {isFormGroups: true},
  error: {isError: true},
  footer: {isFooter: true},
  pageFooter: {isPageFooter: true}
});

Your .hbs file should look something like this:


{{title-bar}}

<div class="page-wrapper">
  <div class="content">
    <div class="custom-header horizontal-box">
      <div class="large-title">
        {{yield title}}
      </div>
    </div>
    <form>
      <div class="vertical-box horizontal-box">
        <div class="custom-body">
          <div class="custom-form">
            {{yield formGroups}}
            {{yield error}}
          </div>
        </div>
      </div>
      <div class="form-footer">
        {{yield footer}}
      </div>
    </form>
  </div>
  {{yield pageFooter}}
</div>

Using this page layout is easy. Inside your view’s .hbs template file, just add the following code:


{{#page-layout as |section|}}
  {{#if section.isTitle}}
    "Add New Comment"
  {{else if section.isFormGroups}}
    <div class="form-group">
      <label for="comment-name">Name</label>
      {{input value=model.name id="comment-name" class="form-control" autofocus="autofocus"}}
    </div>
    <div class="form-group">
      <label for="comment-body">Comment</label>
      {{textarea value=model.body id="comment-name" class="form-control" autofocus="autofocus"}}
    </div>
  {{else if section.isError}}
    {{#if hasSwearWords}}
      {{validation-error error="Comments must not contain swear words."}}
    {{/if}}
  {{else if section.isFooter}}
    <button {{action "submit"}}>Submit</button>
  {{/if}}
{{/paged-dialog}}

You can use this page layout in many different view templates. The benefit here is that the page layout guarantees the exact same HTML structure, and any tweaks will be applied to all views.

The page layout is intentionally pretty dumb–it only cares about the nested structure of the elements. It leaves the view templates in charge of all business logic and data management.

Hope this approach is helpful for you.