title | alias | layout |
---|---|---|
Deprecations for v2.x |
guides/deprecations/ |
deprecations |
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).
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
}
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
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
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
export function initialize(appInstance) {
let store = appInstance.lookup('service:store');
store.pushPayload(`<payload here>`);
}
export default {
name: 'preload-store',
initialize: initialize
}
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.
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).
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
});
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.
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);
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.
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.
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:
// Ember 2.3 and later
import Ember from 'ember';
const { getOwner } = Ember;
export default Ember.Helper.extend({
init() {
this._super(...arguments);
this.customThing = getOwner(this).lookup('custom:thing');
}
});
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.
Using the above before example, the following demonstrates how to use the polyfill:
// Ember 2.3 and later
import Ember from 'ember';
import getOwner from 'ember-getowner-polyfill';
export default Ember.Helper.extend({
init() {
this._super(...arguments);
this.customThing = getOwner(this).lookup('custom:thing');
}
});
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:
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
}
});
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:
// app/controllers/foo-bar.js
export default Controller.extend({
someProp: Ember.computed('model.yolo', function() {
return this.get('model.yolo');
})
});
Would be refactored to:
// app/components/foo-bar.js
export default Component.extend({
someProp: Ember.computed('model.yolo', function() {
return this.get('model.yolo');
})
});
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
Ember.Backburner
was private throughout the Ember 2.x series and will be
removed after 2.8.
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] (https://guides.emberjs.com/v2.5.0/applications/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] (https://guides.emberjs.com/v2.5.0/object-model/computed-properties/) for further reading.
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.
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'];
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.
The Route#serialize
function was deprecated in favor of passing a serialize
function into the route's definition in the router map. For more detailed information see the Route Serializers RFC.
As an example, given a route like:
// app/routes/post.js
import Ember from 'ember';
export default Ember.Route.extend({
// ...
serialize(model) {
return { post_id: model.id };
}
});
// app/router.js
export default Router.map(function() {
this.route('post', {
path: '/post/:post_id'
});
});
You would refactor it like so:
// app/routes/post.js
import Ember from 'ember';
export default Ember.Route.extend({
// ...
});
// app/router.js
function serializePostRoute(model) {
return { post_id: model.id };
}
export default Router.map(function() {
this.route('post', {
path: '/post/:post_id',
serialize: serializePostRoute
});
});
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.
Using the {{render
helper is deprecated in favor of using components.
Please refactor uses of this helper to components:
For example, if you had:
// app/controllers/my-sidebar.js
export default Ember.Controller.extend({
});
You would refactor to a component like so:
// app/components/my-sidebar.js
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.
- 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.
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/routes/index.js
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.
Using Ember.K
is deprecated in favor of defining a function inline. See RFC 0178.
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
.