Dynamic layouts in Ember.js

There are situations where the application’s layout does not strictly follow routes structure (e.g. routes from the same nesting level would like to use a completely different layout). In such scenario, a layout can be implemented with glimmer components where the application’s controller switches between them.

Let’s imagine we would like to use two different layouts: public and application. We can implement those as following components (remember to replace with in the template):

  • app/components/layout/public.hbs
  • app/components/layout/application.hbs

Then we need to add layout property to the ApplicationController class so we can switch between layouts from routes (@tracked decorator needed for reactivity):

// app/controllers/application.js
import Controller from '@ember/controller';
import { tracked } from '@glimmer/tracking';

export default class ApplicationController extends Controller {
  @tracked layout = 'application';
}

We also need to add an initializer that will reopen Route class to select a current layout in the setupController hook based on route’s layout property (it might fall-back to application if not defined):

// app/initializers/layout.js
import Route from '@ember/routing/route';

export function initialize() {
  Route.reopen({
    setupController() {
      this._super(...arguments);

      const layout = this.layout || 'application';
      this.controllerFor('application').layout = layout;
    }
  });
}

export default {
  initialize
};

Now to change the layout, just provide layout in the route class, e.g.:

import Route from '@ember/routing/route'

export default class HomeRoute extends Route {
  layout = 'public';
}