Posted on by & filed under Content - Highlights and Reviews, Programming & Development.

A guest post by Matthew Beale, a web developer based in Brooklyn, New York. He is a JavaScript consultant and has contributed to projects such as: Ember.js, Ember-Data, SeedFu, and Facebooker.

Ember.js is a JavaScript application framework built around several founding principles. Among these principles is “convention over configuration.” The Ember.js router executes on several conventions when handling a URL, the most important one being a naming convention for templates, routes, controllers, and views:

In this post we cover how to use outlets to build route-less UI interactions (like modals), and how to create clean integration points between multiple controllers.

When /dashboard is handled, Ember must find the classes for each part of it’s MVC. The classes must be resolved. The route will be resolved by classifying and prefixing the route name:

If that class is not present, the class be be generated on the fly. Then, before calling the setupController hook on an instance of that route, the controller will be resolved:

After this and during the default renderTemplate behavior, a matching view and template are resolved:

Templates created via script tags or via your build pipeline both end up in Ember.TEMPLATES. As with the route controllers, views, and templates are each generated if they do not exist.

When templates (excluding the application template) are rendered by the router, they are rendered into outlets. Outlets are similar to the <%= yield %> statement in Rails layouts. They specify a point in a parent template where the router can attach a view.

Ember provides a default application template with no markup, just an outlet.

This conventional rendering behavior is just that: A convention. Ember provides an API for explicitly rendering and disconnecting outlets. The default behavior can be replaced or augmented by using that API.

Rendering Into Outlets

Ember routes will implicitly render a template with the same name as the route, and with a context of the resolved and instantiated controller. The renderTemplate function provides an opportunity to change or add to the default behavior:

In this case, the “store” template will still be rendered with the ShopController as its context, and be in the default (or “main”) outlet of it’s parent.

Often you don’t want to override the default behavior, but instead augment the rendering for that route. This ShopRoute renders a cart into an outlet named “cart” in the “shop” template:

The outlet parameter specifies the outlet name. This outlet is named “cart” by the template, but if no name had been provided, it would default to “main”. The into parameter specifies which template contains this outlet, and the controller specifies which controller to bind as context when rendering.

In the code above, the domains of cart and shop only intersect on the route. The controllers themselves can be said to have a “single responsibility”. They must only present content, and have not leaked anything about themselves to other controllers.

Single responsibility controllers, and the flexibility of the render function, make it easy to add new user-facing features without changing lots of code. Adding a product to a cart can be implemented without altering any controllers.

The addToCart action is sent when a user clicks the product name. The action could be handled by the ShopController, but is instead allowed to bubble to the ShopRoute. The route already knows something of the shop and cart domains, so it is a natural extension of it’s responsibilities to add products to the cart.

Named outlets allow you to contain between-controller logic solely in routes, which encourages action handling at the same level. Controllers themselves are then left to focus on UI state and model presentation.

Implementing Modals With Outlets

Outlets can be rendered into, or be disconnected, at any time. The route always has control over their lifecycle. This makes them ideal for parts of a UI that feel stateful, but are not attached to a route. A modal can be a good example of this.

For a good, rich example, we can use outlets to power bootstrap modals (JS Bin – for the full source code):

The actions of IndexRoute provide the outlet controls:

The ModalViewMixin takes care of Bootstrap and DOM concerns. When the “close” button it clicked, there is a delay for animation before the outlet is disconnected.

Conclusion

Outlets are a powerful part of Ember’s rendering API. Use them to build route-less UI interactions (like modals), or create clean integration points between multiple controllers. In many of the larger and more successful Ember code bases that I know of, this pattern of outlet use and actions handled on the route is key to managing code complexity.

Be sure to look at the Ember.js resources that you can find in Safari Books Online.

Safari Books Online has the content you need

Developing an Ember.js Edge will take the reader from a casual interest in Ember.js through to building a complete application. Along the way we’ll cover the current state of client-side web development, the history and evolution of Ember, and the projects and challenges that have informed its design. Then we’ll dig deep into each of Ember’s constituent component libraries, demonstrating how each operates on its own and how they work together harmoniously as a framework.
Instant Ember.js Application Development How-to is a practical guide that provides you with clear step-by-step examples. The in-depth examples take into account the key concepts and give you a solid foundation to expand your knowledge and your skills. That will help you use the power of Ember.JS in your applications.

About the author

matt.beale Matthew Beale is a web developer based in Brooklyn, New York. He is a JavaScript consultant and has contributed to projects such as: Ember.js, Ember-Data, SeedFu, and Facebooker, who can be reached at @mixonic.

Tags: Ember, Ember.js, Javascript, Modals, Outlets, Templates,

4 Responses to “Using Ember.js Outlets”

  1. Guilherme

    Your JS Bin breaks if I click outside the modal to close it. Other than that, good read. Thanks.

  2. Jeremy Bargar

    Hey, this is good stuff! Nice, concise modal example using outlets.

    One nitpick in your otherwise excellent jsbin is that instance-specific logic has crept into the general mixin code:


    controller.send('closeSummary');

    This means that any use of the otherwise generic mixin will have to have an event called ‘closeSummary’, which is really specific to the particulars of the book+summary domain. More problematic still is that a use case with multiple modals would not work – the mixin only knows about one event name.

    I’ve updated the example to use a more generic event name by default, and to show how the event name can be customized, as in your example:

    http://jsbin.com/ucanam/1039/edit?html,js,output