8 Essential Ember.js Interview Questions *

Toptal sourced essential questions that the best Ember.js developers and engineers can answer. Driven from our community, we encourage experts to submit questions and offer feedback.

Hire a Top Ember.js Developer Now
Toptal logois an exclusive network of the top freelance software developers, designers, finance experts, product managers, and project managers in the world. Top companies hire Toptal freelancers for their most important projects.

Interview Questions

1.

Sketch out the implementation of a master-detail view. A list of bands can be seen on the URL /bands and when one of them is selected, some detailed information is shown about the selected band, while the list of bands remains visible. The URL changes to band/24, where 24 is the ID of the selected band.

Make sure to show the relevant route definitions, route objects and templates.

You can assume you have access to a store object in the routes as this.store and that the store has the findAll(type) and findRecord(type, id) methods to retrieve all objects of a certain type and a specific object of a certain type.

View answer
// app/router.js

this.route('bands', function() {
  this.route('band', { path: ':id' });
});
// app/routes/bands.js

export Ember.Route.extend({
  model: function() {
    return this.store.findAll('band');
  }
});
// app/routes/bands/band.js

export Ember.Route.extend({
  model: function(params) {
    return this.store.find('band', params.id);
  }
});
<!-- app/templates/bands.hbs -->
<ul>
{{#each model as |band|}}
  <li>
    {{link-to band.name 'bands.band'}}
  </li>
{{/each}}
</ul>

{{outlet}}
<!-- app/templates/bands/band.hbs -->
<div>{{model.description}}</div>
2.

How does the above change if the URL structure is to be retained, but the list of bands must no longer be displayed when an individual band is selected? As before, sketch out the route definitions, route objects, and templates.

View answer

The routes are no longer nested, but instead are defined on the same level:

// app/router.js

this.route('bands');
this.route('band', { path: '/bands/:id' });
// app/routes/bands.js

export Ember.Route.extend({
  model: function() {
    return this.store.findAll('band');
  }
});
// app/routes/band.js

export Ember.Route.extend({
  model: function(params) {
    return this.store.find('band', params.id);
  }
});
<!-- app/templates/bands.hbs -->
<ul>
{{#each model as |band|}}
  <li>
    {{link-to band.name 'bands.band'}}
  </li>
{{/each}}
</ul>

{{outlet}}
<!-- app/templates/bands/band.hbs -->
<div>{{model.description}}</div>
3.

Use Ember computed macros to clear up the following class definition.

// app/models/monster.js

export default Ember.Object.extend({
  headCount: null,
  eyeCount: null,
  legCount: null,

  canBreathFire: false,
  canFreeze: false,
  canPetrify: false,

  victories: [],

  isDragon: Ember.computed('canBreathFire', function() {
    return this.get('canBreathFire') && this.get('headCount') > 1;
  }),

  isFake: Ember.computed('canBreathFire', 'canFreeze', 'canPetrify', function() {
    const hasDeadlyPower = this.get('canBreathFire') || this.get('canFreeze') || this.get('canPetrify');
    return !hasDeadlyPower;
  }),

  xp: Ember.computed('victories.@each.xp', function() {
    return this.get('victories').reduce(function(totalXP, victory) {
      return totalXP + victory.get('xp');
    }, 0);
  }),
});
View answer

The Ember.computed namespace contains several useful methods that make property definitions more explicit and robust.

In this example, a monster is a dragon if it is multi-headed and breathes fire, so you can define properties for each term and then use Ember.computed.and to determine “dragon-ness”.

Ember.computed.or is used to create a property that resolves to true if any properties in it are truthy. That’s what we use for hasDeadlyPower, which is true if the monster breathes fire, if it freezes its enemies, or if it petrifies enemies.

If a monster does not have a deadly power, it’s not really a monster. We can test for this by simply using Ember.computed.not on the previously computed hasDeadlyPower property.

In our example, the total number of XP is the sum of XPs the monster has earned from each victory. To find this total, we first create an array of these victory XPs, and then sum them together. Ember.computed has us covered for both of these, using Ember.computed.mapBy, and Ember.computed.sum.

// app/models/monster.js

export default Ember.Object.extend({
  headCount: null,
  eyeCount: null,
  legCount: null,

  canBreathFire: false,
  canFreeze: false,
  canPetrify: false,

  victories: [],

  multiHeaded: Ember.computed.gt('headCount', 1),
  isDragon: Ember.computed.and('canBreathFire', 'multiHeaded'),
  hasDeadlyPower: Ember.computed.or('canBreathFire', 'canFreeze', 'canPetrify'),
  isFake: Ember.computed.not('hasDeadlyPower'),
  victoryXPs: Ember.computed.mapBy('victories', 'xp'),
  xp: Ember.computed.sum('victoryXPs'),
});

Apply to Join Toptal's Development Network

and enjoy reliable, steady, remote Freelance Ember.js Developer Jobs

Apply as a Freelancer
4.

Consider the following definition of a Monster class:

// app/models/monster.js

export default Ember.Object.extend({
  ...
  victories: [],
  ...
})

What’s wrong with initializing the victories property the way it’s done here? How would you fix it?

View answer

The problem is that properties defined at the class level (such as victories in this example) are shared across instances of the class. Since victories is a mutable array, any object pushed into it from any instance will be pushed to the property of the class and thus become “visible” in other instances, too:

import Monster from 'models/monster';

let dragon = Monster.create({ name: "Smaug" });
let troll = Monster.create({ name: "Trololo" });

>> dragon.get('victories').pushObject('dwarves')
>> dragon.get('victories') // => "dwarves"
>> troll.get('victories') // => "dwarves", WAT?

Possible solutions involve creating the victories array at the instance level.

For example, when the instance is created:

// app/models/monster.js

export default Ember.Object.extend({
  ...
  init: function() {
    this._super();
    if (!this.get('victories')) {
      this.set('victories', []);
    }
  },
})

Or, possibly, as a computed property:

// app/models/monster.js

export default Ember.Object.extend({
  ...
  victories: Ember.computed(function() {
    return [];
  })
})
5.

Assume you have a star rating component that displays stars for ratings of different items. When any star is clicked, the set action is fired on the component, with the new rating as the first parameter.

Here is the current component code and its invocation:

// app/components/star-rating.js

export default Ember.Component.extend({
  item: null,
  rating: 0,
  ...
  actions: {
    set: function(newRating) {
      var item = this.get('item');
      item.set('rating', newRating);
      return item.save();
    }
  }
});
{{#each songs as |song|}}
  {{star-rating item=song rating=song.rating}}
{{/each}}

You want your component to be reusable, and to be able to display the rating of anything from a song to the dribbling skill of a soccer player. In light of this, how could the following component definition be improved? How would you use the improved component to display the dribbling skill of a soccer player?

View answer

The component should receive a new action name that must be sent “upwards” from the set action:

// app/components/star-rating.js

export default Ember.Component.extend({
  item: null,
  rating: 0,
  setAction: '',
  ...
  actions: {
    set: function(newRating) {
      var item = this.get('item');
      this.sendAction('setAction', {
        item: this.get('item'),
        rating: newRating
      });
    }
  }
});

The setAction name needs to be passed into the component when invoking it from the template. In the first case, let that be updateRating:

{{#each songs as |song|}}
  {{star-rating item=song rating=song.rating setAction="updateRating"}}
{{/each}}

The corresponding updateRating action can be defined on the route (or controller) as follows:

// app/routes/band/songs.js

export default Ember.Route.extend({
  actions: {
    updateRating: function(params) {
      var song = params.item,
          rating = params.rating;

      song.set('rating', rating);
      return song.save();
    }
  }
});

To use the same component to set the dribbling skill of a soccer player, you invoke the component like this:

{{star-rating item=dribblingSkill rating=dribblingSkill.score setAction="updateSkill"}}

The action fired on the route would then be called updateSkill but nothing needs to change in the component’s code. Thus, the component is reusable.

6.

List the route hooks and actions you know, and give a common use case for each.

View answer

Hooks (called after entering):

  • beforeModel
  • model
  • afterModel
  • redirect
  • setupController
  • renderTemplate

Actions:

  • willTransition
  • didTransition
  • loading
  • error

Bonus points if the candidate mentions these hooks:

  • activate (after redirect)
  • deactivate (first hook when exiting a route)
  • resetController (called after deactivate when exiting a route)

Use Cases for Hooks


beforeModel

This is the place to put logic before the route loads any data.

Examples might include:

  • Transitioning to a login route if the user is not logged in
  • Transitioning to a particular route from the application route (e.g. redirect to /bands from /)

model

Fetches the main data backing up the route’s template.


afterModel

This is the place to put logic after the route has loaded its data.

Examples might include:

  • Banning a user from seeing a resource they have no access to
  • Redirecting to the detailed post view if there is only one post

redirect

This can be used for the same purpose as afterModel, the only difference being that transitioning in redirect does not invalidate the current transition, and thus does not rerun the previous hooks.


setupController

Sets up data other than the model on the controller.


activate

Called when the route has been fully entered.

One use case might be setting a CSS class on the body tag so that a modal becomes visible.


deactivate

Called when the route has been exited

A possible use case is resetting the CSS class that was set in the activate hook. In general, this is used to reset what was done in activate.


resetController

This hook is called when either the route is exited or its model changes (switching from one post to the next). These two cases are distinguished by the second argument of the method, which is true when the route is exited.

Since route-driven controllers are singletons in Ember, this is the best place to reset properties on the controller that we don’t want to persist when the route is exited. A valid use case might be resetting a counter to zero, or flipping back a switch that designates the status of a UX flow.

Use Cases for Actions


willTransition

This action is fired before the router starts the transition, which gives the developer the possibility to conditionally abort it.

Consequently, it might be used to warn the user about losing unsaved changes, for example.


didTransition

This action is fired on the destination route when the transition has fully completed. Setting the document’s title is a possible use case for this route action.


loading

If a model hook returns a promise, that promise must be resolved before the corresponding template can be rendered. When the model hook returns a promise, a loading action is fired on the parent route (so for bands.band, the loading action is fired on bands), which, by default, renders a loading template under the parent (in the given example, bands.loading). This default behavior can be overridden.


error

If one of the model hooks (beforeModel, model or afterModel) throws an error, an error action is fired on the parent route (so for bands.band, the error action is fired on bands), which, by default, renders an error template under the parent (in the given example, bands.error). This default behavior can be overridden.

7.

Explain what the “Data Down, Actions Up” (DDAU) concept is and what problem it solves.

View answer

DDAU is a best practice for data communication between components and the context in which they are used. The data is passed into components from their context (“data down”) by the component invocation in the template. When a user action makes it necessary to change that passed-in data, instead of mutating the passed-in data, the component should send an action (“actions up”) to its context. That action will trigger a change in the data passed into the component, and thus, the component’s state will change (potentially resulting in redrawing some parts of the component’s DOM representation).

DDAU was conceived to replace context-component communication via two-way bindings. Two-way bindings are used heavily in Ember, but they have a few issues, and it is now considered poor practice to rely on them. Mutating data at any nested level in the component hierarchy leads to scenarios that are hard to debug, because there are lots of places a piece of data given to a component can be changed. DDAU makes the flow of data very explicit and easier to comprehend.

8.

Explain what the each template helper does and show a few ways it can be extended/customized.

View answer

The #each helper iterates through an array and calls its block with each of its items. It is typically used to render a list of things.

{{#each bands as |band|}}
  {{band.name}}
{{/each}}

An else branch can be provided that will be rendered when the array is empty.

{{#each bands as |band|}}
  {{band.name}}
{{else}}
  No bands here, move along.
{{/each}}

Bonus points

An index may also be provided as the second parameter of the block:

{{#each bands as |band index|}}
  {{index}}. {{band.name}}
{{else}}
  No bands here, move along.
{{/each}}

Furthermore, a key may be given that tells Ember whether a DOM representation of an item can be reused or needs to be rerendered (i.e., whether the item has changed). This can improve rendering speed.

{{#each bands key="name" as |band index|}}
  {{index}}. {{band.name}}
{{else}}
  No bands here, move along.
{{/each}}

There is more to interviewing than tricky technical questions, so these are intended merely as a guide. Not every “A” candidate worth hiring will be able to answer them all, nor does answering them all guarantee an “A” candidate. At the end of the day, hiring remains an art, a science — and a lot of work.

Why Toptal

Tired of interviewing candidates? Not sure what to ask to get you a top hire?

Let Toptal find the best people for you.

Hire a Top Ember.js Developer Now

Our Exclusive Network of Ember.js Developers

Looking to land a job as an Ember.js Developer?

Let Toptal find the right job for you.

Apply as an Ember.js Developer

Job Opportunities From Our Network

Submit an interview question

Submitted questions and answers are subject to review and editing, and may or may not be selected for posting, at the sole discretion of Toptal, LLC.

* All fields are required

Looking for Ember.js Developers?

Looking for Ember.js Developers? Check out Toptal’s Ember.js developers.

Johnathan Hebert

Freelance Ember.js Developer
United StatesToptal Member Since March 19, 2017

Johnathan has 15 years of experience writing web apps that span consumer productivity software to mission-critical financial trading platforms. He has extensive knowledge of front-end JavaScript and browser APIs as well as significant experience with popular frameworks and libraries like React and Redux. Johnathan's deep full-stack experience includes Node.js and Express, MongoDB as well as more traditional technologies like PHP, ASP.NET, and MySQL.

Show More

Goran Trlin

Freelance Ember.js Developer
Bosnia and HerzegovinaToptal Member Since November 8, 2015

Goran is a talented computer programmer with 15 years of experience. He has extensive experience in dozens of programming languages and platforms and is always striving to learn more. He is interested in a wide range of software engineering topics and always on the lookout for new and exciting projects.

Show More

Marcelo Biffara

Freelance Ember.js Developer
ArgentinaToptal Member Since January 20, 2015

Marcelo is passionate about software development and has known exactly what he wanted to do since childhood. He is a full-stack developer who enjoys writing in Ruby and JavaScript and has experience working with SQL and NoSQL databases and 3rd-party API integration. Marcelo has worked on successful projects, many of which involved high-concurrency platforms and websites.

Show More

Toptal Connects the Top 3% of Freelance Talent All Over The World.

Join the Toptal community.

Learn more