Deprecations Added in Ember 2.x
What follows is a list of deprecations introduced to Ember during the 2.x cycle.
For more information on deprecations in Ember, see the main deprecations page.
Deprecations Added in 2.0
§
Link-to's currentWhen
When developers wanted to customize in what route a {{link-to}}
should be considered "active" they
could pass it using currentWhen
.
Now currentWhen
is deprecated in favour of the hyphenated current-when
, with the exact
same semantics:
§ Ember.String.fmt
Ember.String.fmt
was designed at a time when interpolating values in a JavaScript
string was cumbersome. With template strings, it has become pratical to do it, and
they are recommended over Ember.String.fmt
.
To use the examples from the documentation, you should update your code from:
let firstName = 'John';
let lastName = 'Doe';
"Hello %@ %@".fmt('John', 'Doe'); // "Hello John Doe"
"Hello %@2, %@1".fmt('John', 'Doe'); // "Hello Doe, John"
To the following:
let firstName = 'John';
let lastName = 'Doe';
`Hello ${firstName} ${lastName}` // "Hello John Doe"
`Hello ${lastName}, ${firstName}` // "Hello Doe, John"
Deprecations Added in 2.1
§ Ember.ApplicationInstance#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:
export function initialize(appInstance) {
let store = appInstance.container.lookup('service:store');
store.pushPayload(`<payload here>`);
}
export default {
name: 'preload-store',
initialize: initialize
}
After:
export function initialize(appInstance) {
let store = appInstance.lookup('service:store');
store.pushPayload(`<payload here>`);
}
export default {
name: 'preload-store',
initialize: initialize
}
§ Ember.Application#registry / Ember.ApplicationInstance#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:
app.registry.resolve
->app.resolveRegistration
app.registry.register
->app.register
app.registry.unregister
->app.unregister
app.registry.has
->app.hasRegistration
app.registry.option
->app.registerOption
app.registry.options
->app.registerOptions
app.registry.getOptions
->app.registeredOptions
app.registry.optionsForType
->app.registerOptionsForType
app.registry.getOptionsForType
->app.registeredOptionsForType
app.registry.injection
->app.inject
§ Ember.Component#currentState
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.
§ Ember debug function options
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.
Ember.deprecate
Ember.deprecateFunc
Ember.computed.deprecatingAlias
Ember.warn
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
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:
// Ember < 2.1
import layout from '../templates/some-thing-lol';
export default Ember.Component.extend({
defaultLayout: layout
});
After:
// Ember 2.1 and later
import layout from '../templates/some-thing-lol';
export default Ember.Component.extend({
layout: layout
});
§ Initializer Arity
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:
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):
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):
export function initialize() {
let application = arguments[1] || arguments[0];
application.inject('route', 'service:session');
}
export default {
name: 'inject-session',
initialize: initialize
}
Deprecations Added in 2.2
§ Function as test in Ember.deprecate, Ember.warn, Ember.assert
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:
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);
Refactoring
You have 3 options to refactor second argument from function
to boolean
:
- Use IIFE.
- Use
!!Constructor
for constructors. - Pass
boolean
directly instead of wrapping it in function.
Example:
// ... 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);
In a future version functions will be treated as truthy values instead of being executed.
Deprecations Added in 2.3
§ Injected container access
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:
// 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.
ember install ember-getowner-polyfill
After it installs, use as follows:-
// 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
The {{render}}
helper was never intended to support a block form, but unfortunately (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:
<p>Stuff Here</p>
<p>Stuff Here</p>
Deprecations Added in 2.6
§ Ember.Component#didInitAttrs
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:
Before:
export default Ember.Component.extend({
didInitAttrs() {
this._super(...arguments);
this.get('handle'); // @tomdale
}
});
After:
export default Ember.Component.extend({
init() {
this._super(...arguments);
this.get('handle'); // @tomdale
}
});
§ 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
§ Model param in render helper
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:
export default Controller.extend({
someProp: Ember.computed('model.yolo', function() {
return this.get('model.yolo');
})
});
Would be refactored to:
<p> template stuff here</p>
export default Component.extend({
someProp: Ember.computed('model.yolo', function() {
return this.get('model.yolo');
})
});
Deprecations Added in 2.7
§ Ember.Backburner
Ember.Backburner
was private throughout the Ember 2.x series and will be
removed after 2.8.
§ Ember.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:
- Refactor global bindings to services
- Refactor
oneWay
bindings toreadOnly
computed properties - 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:
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:
export default Ember.Component.extend({
boringObjectBinding: 'MyApp.boringObject'
});
Refactoring local bindings to computed properties can be achieved with less work:
If you had this:
export default Ember.Component.extend({
thingContainer: …,
thingOneBinding: Ember.Binding.oneWay('thingContainer.thingOne'),
thingTwoBinding: 'thingContainer.thingTwo'
});
You could change it to this:
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
§ 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:
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:
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:
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:
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:
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.
§ Use Ember.String.htmlSafe over Ember.Handlebars.SafeString
Creating safe strings. Before:
import Ember from 'ember';
const { computed } = Ember;
export default Ember.Component.extend({
myString: computed(function(){
return new Ember.Handlebars.SafeString(someString);
});
})
After:
import Ember from 'ember';
const { computed } = Ember;
export default Ember.Component.extend({
myString: computed(function(){
return Ember.String.htmlSafe(someString);
});
)};
Detecting safe strings. Before:
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
save() {
let myString = this.get('myString');
if (myString instanceof Ember.Handlebars.SafeString) {
// ...
}
}
}
});
After:
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.
Deprecations Added in 2.11
§ {{render helper
Using the {{render
helper is deprecated in favor of using components.
Please refactor uses of this helper to components:
For example, if you had:
export default Ember.Controller.extend({
});
You would refactor to a component like so:
Note that the render helper has several unique behaviors that may require further refactoring work during migration to a component.
- When using the render helper with no model argument, the controller instance is a singleton. For example the same controller instance is shared between
{{render 'post'}}
, any other helper usage of{{render 'post'}}
, a route template named post, and dependency injections usingEmber.inject.service('post')
. - When sendAction is called in a rendered controller, or when
{{action
is used in a render helper template, the bubbling target for those actions is the router and current active route. With components, those same actions would target only the component instance without bubbling.
§ renderToElement
Using the renderToElement
is deprecated in favor of appendTo
.
Please refactor to use appendTo
:
For example, if you had:
component.renderToElement('div');
Would be refactored to:
let element = document.createElement('div');
component.appendTo(element);
Note that both APIs are private, so no public API is being deprecated here.
§ Rendering into a {{render}} helper that resolves to an {{outlet}}
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.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:
For more informations of how to use ember-elsewhere
, please visit the official
documentation here.
Deprecations Added in 2.12
§ Arguments in Component Lifecycle Hooks
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:
Ember.Component.extend({
didInitAttrs({ attrs }) {
this.set('initialTimestamp', attrs.timestamp);
}
});
After:
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:
Ember.Component.extend({
didReceiveAttrs({ oldAttrs, newAttrs }) {
if (oldAttrs.temp !== newAttrs.temp) {
this.thermometer.move({ from: oldAttrs.temp, to: newAttrs.temp });
}
}
});
After:
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.
§ 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:
Ember.Object.extend({
someFun: Ember.K
});
Command:
ember watson:remove-ember-k --empty
The result will be:
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
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:
export default Component.extend(
init() {
this._super(...arguments);
let Factory = getOwner(this)._lookupFactory('logger:main');
this.logger = Factory.create({ level: 'low' });
}
});
After:
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
.
let factory = owner.factoryFor('widget:slow');
let klass = factory.class;
klass.hasSpeed('slow'); // true
Deprecations Added in 2.13
§ Ember.Router.router renamed to Ember.Router._routerMicrolib
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:
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:
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
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
§ Custom eventManager deprecated
EventDispatcher
has long supported custom eventManager
s 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 eventManager
s can be
created. Please comment on the original RFC
if you have a need for this in your app.
§ Ember.MODELFACTORYINJECTIONS removed
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.
Deprecations Added in 2.16
§ Controller#content alias
For historical reasons, Controller
s 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:
import Controller from '@ember/controller';
import { alias } from '@ember/object/computed';
export default Controller {
content: alias('model')
}
§ Ember 2 Legacy
Ember provides ember-2-legacy which is an addon to help app with deprecations during the transition from the 2.x series to 3.x. This addon provides app owners more time to upgrade off of the deprecations slated for removal in 3.0.0 and depcouples that work from upgrading to the 3.x series.
This addon will be supported until 3.4 at which point it will be deprecated.
Once you have removed all deprecations, you can remove the addon with the command:
npm uninstall ember-2-legacy
Deprecations Added in 2.18
§ targetObject
The targetObject
property on Component
was intended as a "write-only API", meaning that the user was supposed to define their own if they desired, but not read the default one. It was never intended for this.get('targetObject')
to be used, so it is now deprecated. The recommendation is to use target
instead.