Template inheritance


#1

Hi, I’m a Laravel (and blade) developer, and I’m trying to replicate the behavior of template inheritance.

From Laravel docs:

<!-- app/views/layouts/base.blade.php -->
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    @yield('body')
</body>
</html>

<!-- app/views/home.blade.php -->
@extends('layouts.base')
@section('body')
    <h1>Hurray!</h1>
    <p>We have a template!</p>
@stop

This will render:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>Hurray!</h1>
    <p>We have a template!</p>
</body>
</html>

I couldn’t find something like this in the documentation. Is this behavior supported in ember? Will be it supported?

Thank you


#2

In short, inheritance in this particular pattern doesn’t need to be supported. Ember is a frontend SPA framework, because of which it gets to assemble it’s webpages in parts. Because Laravel must serve the entire page at once, it does not get this benefit.

What Laravel is doing above is probably closest to the way “outlets” and resources work in Ember. For instance, let’s say that “blade” is a resource. In Ember, you’d likely have 3 templates in use here (not including the parts that belong in index.html).

<!-- app/templates/application.hbs -->
<h1>My Application</h1>
{{outlet}}

With the exception of the addition of the <h1> which I’ve added for a demo later, the above is the full ember equivalent to app/views/layouts/base.blade.php. Because Ember is an SPA, the html and body tags never need replaced and the content of head is never changed. (If you want specific pages to alter the meta there are ways of doing so, but out of the box Ember doesn’t adjust the title tag or meta attributes based on route… yet)

We said blade is a resource. Let’s say we’ve defined it on our router like below.

import Ember from "ember";

var Router = Ember.Router.extend({});

Router.map(function() {
    this.resource('blade', function () {
        this.route('home');
        this.route('dashboard');
    });
});

export default Router;

This router gives us three new routes.

  • /blade/home
  • /blade/dashboard
  • /blade (which is really blade.index)

These routes can make use of a resource template

<!-- app/templates/blade.hbs -->
<h2>Blade</h2>
{{outlet}}

By default this template doesn’t need to be created and will just be an outlet. The three new routes all render into the resource’s outlet. So navigating to /blade/home will render the template blade/home.hbs into the outlet in the template blade.hbs which itself is rendered into the outlet in application.hbs.

Given this template for home

<!-- app/templates/blade/home.hbs -->
<h3>Home</h3>
<p>We have a template!</p>

The final page output would be whatever is in your index.html file (let’s say it’s just a simple html, body, head, and title tag) plus the compilation of these three templates. Each ember view get’s wrapped in it’s own div (which has a class of ember-view)/

Final output

<html>
  <head>
    <title>My Application</title>
  </head>
  <body>
    <div class="ember-view">
      <h1>My Application</h1>
      <div class="ember-view">
        <h2>Blade</h2>
        <div class="ember-view">
          <h3>Home</h3>
          <p>We have a template!</p>
        </div>
      </div>
    </div>
  </body>
</html>

#3

You can specify the layout in your view file like so

// app/views/with-custom-layout.js
import Ember from 'ember';

export default Ember.View.extend({
    layoutName: 'layouts/base'
});

and you can use render to render some view in a named outlet for example

// app/routes/with-partial.js
import Ember from 'ember';

export default Ember.Route.extend({
    afterModel: function () {
        this.render('views/name', {
            into: 'application',
            outlet: 'nameOfTheOutlet'
        });
    }
});

#4

Hello,

I feel that this is missing one key use of inheritance (in my case coming from django template language/jinja) where you can name blocks of a template and then easily override them in child templates. Using outlets makes sense and works if you are only trying to manipulate what’s inside the outlet. But what if I also want to manipulate another block of code in the parent template?

It would be nice if it was as easy as:

{{block 'header'}}
    <h1>New Title</h1>
{{/block}}

{{block 'outlet'}}
    <p>Goes into the parent outlet</p>
{{/block}}

How can you achieve something similar and is that a pattern that could ever be supported? It would be nice to only have to deal with it in the template vs having to add custom controller logic…

Thanks! -Eric