Deprecations Added in Ember 2.x

What follows is a list of deprecations introduced to Ember.js during the 2.x cycle.

For more information on deprecations in Ember, see the main deprecations page.

Deprecations Added in 2.1

Initializer Arity

until: 3.0.0
id: ember-application.app-initializer-initialize-arguments

In prior versions of Ember initializers have taken two arguments (generally labeled as container and application). Starting with Ember 2.1 providing two arguments to an initializer will trigger a deprecation.

The following initializer for Ember 2.0 will trigger a deprecation:

1
2
3
4
5
6
7
8
export function initialize(container, application) {
  application.inject('route', 'service:session');
}

export default {
  name: 'inject-session',
  initialize: initialize
}

To clear the deprecation, remove the first argument (container in the above example):

1
2
3
4
5
6
7
8
export function initialize(application) {
  application.inject('route', 'service:session');
}

export default {
  name: 'inject-session',
  initialize: initialize
}

In some cases an addon might need to support both versions of Ember with the same initializer, one way to do this without triggering a deprecation would be the following (using the same example as above):

1
2
3
4
5
6
7
8
9
export function initialize() {
  let application = arguments[1] || arguments[0];
  application.inject('route', 'service:session');
}

export default {
  name: 'inject-session',
  initialize: initialize
}

Ember.Application#registry / Ember.ApplicationInstance#registry

until: 3.0.0
id: ember-application.app-instance-registry

When the container and registry were split, the registry was added to Ember.Application instances (provided to initializers as the first argument in 2.1) and Ember.ApplicationInstance instances (provided to instance initializers as the first argument). Unfortunately, this was done without making it clear that the .registry property on Ember.Application instances was private. This lead quite a few addons and applications to directly use the registry.

During the 2.1 cycle a new feature (ember-registry-container-reform) was enabled to provide more public API's to access the registry functionality (without exposing all of the private internals).

The following list can be used to migrate from app.registry.* usage to the new public API's:

Ember.ApplicationInstance#container

until: 3.0.0
id: ember-application.app-instance-container

When instance initializers were added, using appInstance.container.lookup was suggested in lieu of using the first argument to initializers. Unfortunately, the container system has always been private and the previous initializer deprecation led users down the wrong path.

During the 2.1 cycle a new feature (ember-registry-container-reform) was enabled to provide more public API's for accessing the container for looking up instances without exposing all of the private internals.

Please refactor from using appInstance.container.lookup to appInstance.lookup.

Before:

app/initializers/preload-store.js
1
2
3
4
5
6
7
8
9
10
export function initialize(appInstance) {
  let store = appInstance.container.lookup('service:store');

  store.pushPayload(`<payload here>`);
}

export default {
  name: 'preload-store',
  initialize: initialize
}

After:

app/instance-initializers/preload-store.js
1
2
3
4
5
6
7
8
9
10
export function initialize(appInstance) {
  let store = appInstance.lookup('service:store');

  store.pushPayload(`<payload here>`);
}

export default {
  name: 'preload-store',
  initialize: initialize
}

Ember debug function options

until: 3.0.0
id: ember-debug.deprecate-options-missing, ember-debug.deprecate-id-missing, ember-debug.deprecate-until-missing, ember-debug.warn-options-missing, ember-debug.warn-id-missing

Starting in Ember 2.1 various debug functions now require a third argument (commonly called options).

id is required by all methods listed below, and the deprecation related methods also require an until property.

The id property is intended to allow runtime debug handlers to uniquely identify the source, and the until property is used to indicate the future version when the deprecated behavior will no longer exist.

The goal of these changes is to allow tools like ember-cli-deprecation-workflow to make managing these deprecations and warnings much easier (by matching on the id instead of the full deprecation/warn message).

Ember.Component#defaultLayout

until: 3.0.0
id: ember-views.component.defaultLayout

Specifying a defaultLayout to a component is deprecated in favor of specifying layout directly. defaultLayout was often used in order to allow inheriting components to fallback to their parents defaultLayout if a custom layout was not provided. Due to the way a components layout is looked up naturally, this is true when using layout properties in both locations. Changing the layout detection process allows initial render speed (with many components) to be improved pretty significantly.

Before:

1
2
3
4
5
6
// Ember < 2.1
import layout from '../templates/some-thing-lol';

export default Ember.Component.extend({
  defaultLayout: layout
});

After:

1
2
3
4
5
6
// Ember 2.1 and later
import layout from '../templates/some-thing-lol';

export default Ember.Component.extend({
  layout: layout
});

Ember.Component#currentState

until: 2.3.0
id: ember-view.current-state

The currentState property on Ember.Component instances is a private property that Ember uses internally to deal with the various states a component can be in (in DOM, pre-render, destroying, etc). Unfortunately, this removes a pretty common term (currentState might be used for many things in a user-land component).

In Ember 2.1 the internal .currentState property has been moved to _currentState to avoid conflicts.

Please keep in mind that .currentState / ._currentState is still private and should not be used/relied upon outside of Ember internals.

Deprecations Added in 2.2

Function as test in Ember.deprecate, Ember.warn, Ember.assert

until: 2.5.0
id: ember-debug.deprecate-test-as-function
Deprecated behavior

Calling Ember.deprecate, Ember.warn or Ember.assert with a function as test argument is deprecated.

You can no longer pass arguments of type function to these methods. Following calls will trigger deprecations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const message = 'Test message.';
const options = { id: 'test', until: '3.0.0' };

// passing function
Ember.deprecate(message, function() {
  return true;
}, options);

const myConstructor = {}.constructor;

// passing constructor (also a function)
Ember.warn(message, myConstructor, options);

// passing function with double arrow syntax
Ember.assert(message, () => true, options);

Demo.

Refactoring

You have 3 options to refactor second argument from function to boolean:

  1. Use IIFE.
  2. Use !!Constructor for constructors.
  3. Pass boolean directly instead of wrapping it in function.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ... message, options omitted for brevity

// passing IIFE (1)
Ember.deprecate(message, (function() {
    return true;
})(), options);

const myConstructor = {}.constructor;

// passing !!constructor (2)
Ember.warn(message, !!myConstructor, options);

// passing boolean directly (3)
Ember.assert(message, true, options);

Demo.

In a future version functions will be treated as truthy values instead of being executed.

Deprecations added in 2.3

Injected container access

until: 3.0.0
id: ember-application.injected-container

this.container has been private API since at least Ember 1.0.0. Unfortunately, there was not a public API available to use as an alternative. In the Ember 2.1 cycle a number of public API's were added to instance initializers that allowed access to the container and registry (see here and here for details of the public API's), but this new public API surface was not added to individual instances that were using this.container directly.

Ember 2.3 now provides a public API for usage from within any instances that were instantiated from the container.

This refactor is relatively straight forward for applications, but still leaves a few gaps for addons that want to function without deprecation on all versions while still using the newer paradigms. ember-getowner-polyfill was created for this exact reason.

Note that the getOwner API is natively available in Ember versions 2.3 and up, so you don't need to install a polyfill. Use it like you would use any other API, as in the After: example.

Before:

1
2
3
4
5
6
7
8
9
10
// Ember < 2.3
import Ember from 'ember';

export default Ember.Helper.extend({
  init() {
    this._super(...arguments);

    this.customThing = this.container.lookup('custom:thing');
  }
});

After:

do an ember install of the polyfill.

1
ember install ember-getowner-polyfill

After it installs, use as follows:-

1
2
3
4
5
6
7
8
9
10
// Ember < 2.3 needs the polyfill installed, Ember >= 2.3 available natively.
import Ember from 'ember';

export default Ember.Helper.extend({
  init() {
    this._super(...arguments);

    this.customThing = Ember.getOwner(this).lookup('custom:thing');
  }
});

Deprecations Added in 2.4

{{#render}} helper with block

until: 2.4.0
id: ember-template-compiler.deprecate-render-block

The {{render}} helper was never intended to support a block form, but unfortunatley (mostly due to various refactorings in 1.10 and 1.13) it started working in block form. Since this was not properly engineered, there are a number of caveats (i.e. the controller and target values of anything inside the block are incorrect) that prevent continued support.

Support the following forms will be removed after 2.4:

1
2
3
{{#render 'foo'}}
  <p>Stuff Here</p>
{{/render}}
1
2
3
{{#render 'foo' someModel}}
  <p>Stuff Here</p>
{{/render}}

Deprecations Added in 2.6

Ember.Component#didInitAttrs

until: 3.0.0
id: ember-views.did-init-attrs

Using didInitAttrs is deprecated in favour of using init. When init is called the attrs sent in with the component will be available after calling this._super(...arguments)

Given a htmlbars template like this:

1
{{my-component handle="@tomdale"}}

Before:

1
2
3
4
5
6
export default Ember.Component.extend({
  didInitAttrs() {
    this._super(...arguments);
    this.get('handle'); // @tomdale
  }
});

After:

1
2
3
4
5
6
export default Ember.Component.extend({
  init() {
    this._super(...arguments);
    this.get('handle'); // @tomdale
  }
});

Model param in {{render helper

until: 3.0.0
id: ember-template-compiler.deprecate-render-model

Using the model param in the {{render helper is deprecated in favor of using components. Please refactor to a component and invoke thusly:

For example, if you had:

1
{{render 'foo-bar' someModel}}
app/templates/foo-bar.hbs
1
<p>{{someProp}} template stuff here</p>
app/controllers/foo-bar.js
1
2
3
4
5
export default Controller.extend({
  someProp: Ember.computed('model.yolo', function() {
    return this.get('model.yolo');
  })
});

Would be refactored to:

1
{{foo-bar model=someModel}}
app/templates/components/foo-bar.hbs
1
<p>{{someProp}} template stuff here</p>
app/components/foo-bar.js
1
2
3
4
5
export default Component.extend({
  someProp: Ember.computed('model.yolo', function() {
    return this.get('model.yolo');
  })
});

Legacy support addons

Ember provides addons ember-legacy-views and ember-legacy-controllers that allow for projects to continue using some legacy concepts in 2.x. Beginning in 2.4, use of these addons is now deprecated.

See the deprecation guide sections on removing views, ArrayController, and ObjectController for information on migration.

Once view and controller deprecations are removed, you can remove the addons with the command: npm uninstall --save-dev ember-legacy-views && npm uninstall ember-legacy-controllers

Deprecations Added in 2.7

Ember.Backburner

until: 2.8.0
id: ember-metal.ember-backburner

Ember.Backburner was private throughout the Ember 2.x series and will be removed after 2.8.

Ember.Binding

until: 3.0.0
id: ember-metal.binding

Ember.Binding has not been needed for some time and is deprecated in favor of computed properties and services (depending on what you were binding to). It is recommended that you take the following actions:

  1. Refactor global bindings to services
  2. Refactor oneWay bindings to readOnly computed properties
  3. Refactor all other bindings to alias computed properties

The guide on services is a good place to start for creating and consuming services to replace your global bindings. In general though, you will replace your global with a service and consume it like this:

1
2
3
4
export default Ember.Component.extend({
  // will load the service in file /app/services/cool-service.js
  coolService: Ember.inject.service()
});

This would replace a binding that may have looked like this:

1
2
3
export default Ember.Component.extend({
  boringObjectBinding: 'MyApp.boringObject'
});

Refactoring local bindings to computed properties can be achieved with less work:

If you had this:

1
2
3
4
5
export default Ember.Component.extend({
  thingContainer: ,
  thingOneBinding: Ember.Binding.oneWay('thingContainer.thingOne'),
  thingTwoBinding: 'thingContainer.thingTwo'
});

You could change it to this:

1
2
3
4
5
export default Ember.Component.extend({
  thingContainer: ,
  thingOne: Ember.computed.readOnly('thingContainer.thingOne'),
  thingTwo: Ember.computed.alias('thingContainer.thingTwo')
});

See the guide on computed properties for further reading.

Deprecations Added in 2.8

Use Ember.String.htmlSafe over Ember.Handlebars.SafeString

until: 3.0.0
id: ember-htmlbars.ember-handlebars-safestring

Creating safe strings. Before:

1
2
3
4
5
6
7
8
import Ember from 'ember';
const { computed } = Ember;

export default Ember.Component.extend({
  myString: computed(function(){
    return new Ember.Handlebars.SafeString(someString);
  });
})

After:

1
2
3
4
5
6
7
8
import Ember from 'ember';
const { computed } = Ember;

export default Ember.Component.extend({
  myString: computed(function(){
    return Ember.String.htmlSafe(someString);
  });
)};

Detecting safe strings. Before:

1
2
3
4
5
6
7
8
9
10
11
12
import Ember from 'ember';

export default Ember.Component.extend({
  actions: {
    save() {
      let myString = this.get('myString');
      if (myString instanceof Ember.Handlebars.SafeString) {
        // ...
      }
    }
  }
});

After:

1
2
3
4
5
6
7
8
9
10
11
12
import Ember from 'ember';

export default Ember.Component.extend({
  actions: {
    save() {
      let myString = this.get('myString');
      if (Ember.String.isHTMLSafe(myString)) {
        // ...
      }
    }
  }
});

If you're an addon maintainer, there is a polyfill for safe string detection (ember-string-ishtmlsafe-polyfill) that will help maintain backwards compatibility. Additionally, it's worth noting that Ember.String.htmlSafe is supported back to pre-1.0, so there should be no concerns of backwards compatibility there.

Enumerable#contains

until: 3.0.0
id: ember-runtime.enumerable-contains

The Enumerable#contains and Array#contains methods were deprecated in favor of Enumerable#includes and Array#includes to stay in line with ES standards. See RFC for details.

contains and includes have similar behaviors. A notable exception is how NaN values are handled. contains uses Strict equality comparison algorithm for testing inclusion while includes uses SameValueZero algorithm.

Before:

1
2
3
4
5
6
var arr = ['a', 'b', 'c', NaN, undefined, null];
arr.contains('b');        // true
arr.contains('d');        // false
arr.contains(NaN);        // false
arr.contains(null);       // false
arr.contains(undefined);  // false

After:

1
2
3
4
5
6
var arr = ['a', 'b', 'c', NaN, undefined, null];
arr.includes('b');        // true
arr.includes('d');        // false
arr.includes(NaN);        // true
arr.includes(null);       // true
arr.includes(undefined);  // true

includes also allows a second optional parameter startAt to specify the index at which to begin searching:

1
2
3
var arr = ['a', 'b', 'c', NaN];
arr.includes('c', 2);   // true
arr.includes('c', -2);  // true

Note that the second startAt parameter is only available for Ember.Array because Ember.Enumerable does not rely on index-ordered access.

Enumerable#without and MutableEnumerable#addObject use now internally includes instead of contains. This leads to some minor breaking changes:

Before:

1
2
3
4
5
var arr = ['a', 'b'];

arr.addObject(NaN);       // ['a', 'b', NaN]
arr.addObject(NaN);       // ['a', 'b', NaN, NaN]
arr.without(NaN);         // ['a', 'b', NaN, NaN]

After:

1
2
3
4
5
6
7
var arr = ['a', 'b'];

var arr = ['a', 'b'];

arr.addObject(NaN);       // ['a', 'b', NaN]
arr.addObject(NaN);       // ['a', 'b', NaN]
arr.without(NaN);         // ['a', 'b']

Addon authors should use ember-runtime-enumerable-includes-polyfill to fix the deprecation in a backwards-compatible way.

Added in PR #13553.

Deprecations Added in 2.11

renderToElement

until: 2.12.0
id: ember-views.render-to-element

Using the renderToElement is deprecated in favor of appendTo. Please refactor to use appendTo:

For example, if you had:

1
component.renderToElement('div');

Would be refactored to:

1
2
let element = document.createElement('div');
component.appendTo(element);

Note that both APIs are private, so no public API is being deprecated here.

{{render helper

until: 3.0.0
id: ember-template-compiler.deprecate-render

Using the {{render helper is deprecated in favor of using components. Please refactor uses of this helper to components:

For example, if you had:

1
{{render 'my-sidebar'}}
app/templates/my-sidebar.hbs
1
<p>template stuff here</p>
app/controllers/my-sidebar.js
1
2
export default Ember.Controller.extend({
});

You would refactor to a component like so:

1
{{my-sidebar}}
app/templates/components/my-sidebar.hbs
1
<p>template stuff here</p>
app/components/my-sidebar.js
1
2
export default Ember.Component.extend({
});

Note that the render helper has several unique behaviors that may require further refactoring work during migration to a component.

Rendering into a {{render}} helper that resolves to an {{outlet}}.

until: 3.0.0
id: ember-routing.top-level-render-helper

Before named outlets were introduced to Ember the render helper was used to declare slots for this.render in routes. This usage is not common in modern, idiomatic applications and is deprecated. In general, the pattern of named outlets or named render helpers is discouraged. Instead use of ember-elsewhere or another DOM-redirection library should better serve these use cases.

For example this code uses the render helper as a target for a special sidebar present on the index route. The special sidebar is in a template named index-sidebar:

app/templates/application.hbs
1
2
<div class="sidebar">{{render 'sidebar'}}</div>
<div class="main">{{outlet}}</div>
app/templates/index.hbs
1
Index Content
app/routes/index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
App.IndexRoute = Ember.Route.extend({
  renderTemplate() {
    this._super(...arguments);
    this.render('index-sidebar', { into: 'sidebar' });
  },
  actions: {
    willTransition() {
      this.disconnectOutlet({
        parentView: 'application',
        outlet: 'sidebar'
      });
    }
  }
});

It should be refactored to use ember-elsewhere. The sidebar content must be implemented as a component, in this case named index-sidebar. The logic previously used in the route file can be removed. The refactored example:

app/templates/application.hbs
1
2
<div class="sidebar">{{from-elsewhere name='sidebar'}}</div>
<div class="main">{{outlet}}</div>
app/templates/index.hbs
1
2
{{to-elsewhere named='sidebar' send=(component 'index-sidebar')}}
Index Content

For more informations of how to use ember-elsewhere, please visit the official documentation here.

Deprecations Added in 2.12

Ember.K

until: 3.0.0
id: ember-metal.ember-k

Using Ember.K is deprecated in favor of defining a function inline. See RFC #178.

You can use the addon ember-watson to automate the removal of Ember.K from your application.

Example object:

1
2
3
Ember.Object.extend({
  someFun: Ember.K
});

Command:

1
ember watson:remove-ember-k --empty

The result will be:

1
2
3
Ember.Object.extend({
  someFun() {}
});

If for some reason your app depends on the ability to chain Ember.K invocations, you can use the flag --return-this. It will replace Ember.K with a function that returns this.

Migrating from _lookupFactory to factoryFor

until: 2.13.0
id: container-lookupFactory

The private API method _lookupFactory is deprecated and replaced by factoryFor in public API. This API will return the original base class registered into or resolved by the container and a create function to generate a dependency-injected instance.

Addon creators and maintainers can use ember-factory-for-polyfill for addons supporting versions 2.3+, or ember-getowner-polyfill for 1.10+.

Before:

1
2
3
4
5
6
7
export default Component.extend(
  init() {
    this._super(...arguments);
    let Factory = getOwner(this)._lookupFactory('logger:main');
    this.logger = Factory.create({ level: 'low' });
  }
});

After:

1
2
3
4
5
6
7
export default Component.extend(
  init() {
    this._super(...arguments);
    let factory = getOwner(this).factoryFor('logger:main');
    this.logger = factory.create({ level: 'low' });
  }
});

Any methods or properties of the factory can be accessed through the class property when using factoryFor.

1
2
3
let factory = owner.factoryFor('widget:slow');
let klass = factory.class;
klass.hasSpeed('slow'); // true

Arguments in Component Lifecycle Hooks

until: 2.13.0
id: ember-views.lifecycle-hook-arguments

Previously, it was possible for component lifecycle hooks didInitAttrs, didReceiveAttrs, and didUpdateAttrs to receive arguments. However, this functionality was part of private API. Using the arguments is harmful to component performance, so they will trigger a deprecation. Alternative approaches for all three hooks are below:

didInitAttrs arguments

Since this lifecycle hook is already deprecated, we suggest taking this chance to address two deprecations at the same time.

Imagine you have a component that stores a timestamp when it is initialized.

Before:

1
2
3
4
5
Ember.Component.extend({
  didInitAttrs({ attrs }) {
    this.set('initialTimestamp', attrs.timestamp);
  }
});

After:

1
2
3
4
5
6
7
Ember.Component.extend({
  init() {
    this._super(...arguments);

    this.set('initialTimestamp', this.get('timestamp'));
  }
});
didReceiveAttrs and didUpdateAttrs arguments

This example for didReceiveAttrs below also applies to didUpdateAttrs, a similar hook that only runs on re-renders. Let's say you want to animate a thermometer widget showing the change between today's high and low temperatures.

Before:

1
2
3
4
5
6
7
Ember.Component.extend({
  didReceiveAttrs({ oldAttrs, newAttrs }) {
    if (oldAttrs.temp !== newAttrs.temp) {
      this.thermometer.move({ from: oldAttrs.temp, to: newAttrs.temp });
    }
  }
});

After:

1
2
3
4
5
6
7
8
9
10
11
Ember.Component.extend({
  didReceiveAttrs() {
    let oldTemp = this.get('_oldTemp');
    let newTemp = this.get('temp');

    if (oldTemp && oldTemp !== newTemp) {
      this.thermometer.move({ from: oldTemp, to: newTemp });
    }
    this.set('_oldTemp', newTemp);
  }
});

Additionally, ember-diff-attrs is an addon that spun out of the discussions on the RFC. It provides a dry way to track attribute changes for this lifecycle hook.

Deprecations Added in 2.13

Ember.Router.router renamed to Ember.Router._routerMicrolib

until: 2.16.0
id: ember-router.router

The private router property of the Ember.Router instance (commonly found as this.router in Ember.Route instances or via router:main in the container) has been renamed to _routerMicrolib to identify it as router.js, the microlib used within Ember.Router.

Addon and application developers that are using the internal router property of Ember.Router should replace those usages with Ember.Router._routerMicrolib.

This example demonstrates a common use case for .router.

Before:

1
2
3
4
5
6
7
8
9
10
export default Ember.Service.extend({
  getRouteNameFromUrl (url) {
    const router = getContainer(this).lookup('router:main');
    const routes = router.router.recognizer.recognize(url);

    if (routes && routes.length) {
      return routes[routes.length-1].handler;
    }
  }
});

After:

1
2
3
4
5
6
7
8
9
10
export default Ember.Service.extend({
  getRouteNameFromUrl (url) {
    const router = getContainer(this).lookup('router:main');
    const routes = router._routerMicrolib.recognizer.recognize(url);

    if (routes && routes.length) {
      return routes[routes.length-1].handler;
    }
  }
});

Ember.Map, Ember.MapWithDefault, and Ember.OrderedSet are deprecated

until: 2.16.0
id: ember-metal.map

The private classes Ember.Map, Ember.MapWithDefault, and Ember.OrderedSet are being deprecated and will be removed in a later version of Ember.

Developers using any of these classes should replace them by a custom implementation.

Deprecations Added in 2.14

Ember.MODEL_FACTORY_INJECTIONS removed

until: 2.17.0
id: ember-metal.model_factory_injections

The flag Ember.MODEL_FACTORY_INJECTIONS is no longer required, and can be safely removed from your ember >= 2.13 application.

This flag was added in 2013 for Ember Data compatibility with the Dependency Injection (DI) system. At the time, Ember's DI blurred the idea of a factory and a class which caused several issues, including one that resulted in this flag being required.

Since then, both Ember and Ember Data have addressed the various issues which lead to this flag. Most recently, Ember's DI system introduced the factoryFor API which separates factores and classes. With this step, the flag in question no longer has any affect, and can be safely removed.

Custom eventManger deprecated

until: 2.17.0
id: ember-views.event-dispatcher.canDispatchToEventManager

EventDispatcher has long supported custom eventManagers containing their own event dispatching rules to be defined on components. This feature was initially added to allow components to differentiate between certain types of touch input, however the addon that originally used this has long been deprecated.

If needed, an addon which allows for custom component eventManagers can be created. Please comment on the original RFC if you have a need for this in your app.

Upcoming deprecations in 2.16

Controller#content alias

until: 2.17.0
id: ember-runtime.controller.content-alias

For historical reasons, Controllers have a private property named content that aliases the model property.

With the introduction of this deprecation, if you wish to continue using content in the controller without a deprecation warning, you will have to add an explicit alias:

1
2
3
4
5
6
import Controller from '@ember/controller';
import { alias } from '@ember/object/computed';

export default Controller {
  content: alias('model')
}