title | alias | layout |
---|---|---|
Deprecations for v1.x |
guides/deprecations/ |
deprecations |
What follows is a list of deprecations introduced to Ember.js during the 1.x cycle.
For more information on deprecations in Ember, see the [main deprecations page] (/deprecations).
ContainerViews have been observable as arrays, where the items in the array are childViews. This introduces complexity into container views despite the feature being a rarely used one.
Ember.DeferredMixin
and Ember.Deferred
have been deprecated in favor
of using RSVP.Promise
s.
As part of the Ember.DeferredMixin
deprecation, using .then
on an
Ember.Application instance itself has been deprecated.
You can use the ready
hook or initializers to defer/advance readiness
instead.
Previous to Ember 1.8, views would commonly be fetched from the global scope:
Since Ember 1.8, views are more appropriately resolved on the application via strings:
They may also be fetched via a binding:
In general, it is recommended that your Ember application avoid accessing globals from a template.
Most of Ember's provided views are already accessed via helpers. For example, the Ember.TextField view is used via the input helper.
The Ember.Select view has not been upgraded to have a helper. Instead, it was suggested that you call it via the global class name:
Since this lookup is now deprecated, the select view has been registered
on an application as select
. The new usage is:
See the updated Ember.Select documentation and the built-in views guide for more details and examples.
If the code triggering this deprecation is being fired from a library, that library may need to update its suggested usage.
One solution for such a library is to provide mixins instead of classes:
// usage is {{view "list"}}
var App.ListView = Ember.View.extend(ListView);
A more advanced solution is to use an initializer to register the plugin's views on the the application:
// usage is {{view "list"}}
Ember.Application.initializer({
name: 'list-view',
initialize: function(container, application) {
container.register('view:list', ListView);
}
});
More details on how to register an Ember.js framework component are available in the initializer API documentation and the dependency injection guide.
Prior to this release, if you were using location: 'hash'
(which is the default), you were able to link to a route with a location.hash
that didn't contain the expected leading forward slash. e.g. #foo
instead of the correct #/foo
. Very few, if any, should be impacted by this since the router always produces the correct form.
Doing so is ambiguous because you may also be trying to link to an element on the page who's id matches <div id="foo">
and it also erroneously will create an extra history state if a user clicks on something that transitions to that route again, since it will change location.hash === '#/foo'
.
This ability will be removed quickly to allow us to mimick the browser's behavior of scrolling the page to an element who's id matches, but in our case doing so after the transition ends and everything is rendered. Once this feature is added, you'll be able to link to id's even with doubled up hashes: #/foo#some-id
as well as the expected #some-id
.
In Ember 1.9, the each
and with
helpers come in two flavors: a "context-switching" flavor and a "named-parameter" flavor.
This has proven to be one of the more confusing parts of the Ember templating system. It is also not clear to beginners which to use, and when they choose the context-shifting form, they lose access to values in the outer context that may be important.
Because the helper itself offers no clue about the context-shifting behavior, it is easy (even for more experienced Ember developers) to get confused when skimming a template about which object a value refers to.
The context-shifting forms of #each
and #with
have been deprecated in favor of the named-parameter forms. In Ember 1.12, the in
and as
syntax are further deprecated in favor of block params syntax. See the deprecation notes for in and deprecation notes for as.
To transition your code to the new syntax, you can change templates that look like this:
with:
In preparation for further work on HTMLBars, the context switching form of {{each}}
is deprecated. This is mostly a "mechanical" refactor and dramatically
simplifies how to think about the context in your templates. This change should be entirely mechanical.
In prior versions you may have done one of the following:
You should now be using:
Experienced Ember users have enjoyed the use of proxying behavior in
the Ember.ObjectController
class since 1.0. However, this behavior
will be removed in Ember 2.0 as the framework migrates to routable components.
New users hit three roadbumps when learning about the object controller pattern.
- Given a certain model, which of the three controller options should I be using?
- Which controller is generated by the framework if I do not specify one?
- When using an object controller, why should the
this
context not be passed to actions if it has the properties of my model?
For these reasons, the Road to Ember 2.0 RFC listed object controllers as a concept to be removed from the framework.
To migrate from an explicitly defined object controller, first convert
the class definition to inherit from Ember.Controller
. For example:
import Ember from "ember";
// Change:
export default Ember.ObjectController.extend({
// To:
export default Ember.Controller.extend({
// ...
Next update any use of {{modelPropertyName}}
in templates with {{model.modelPropertyName}}
.
You should also review any computed property dependent keys, observer keys, and get
and set
statements on the route and controller. An example of how to make this migration can
be found in this PR to the Ghost project.
If a controller is not explicitly defined, but instead is being auto-generated by the framework, it will only throw a deprecation message if the proxying behavior is being used.
Added in PR #10062.
Previously, initializers had access to an object that allowed them to both register new classes and get instances of those classes.
If you have an initializer that gets instances of a class, you need to change it to use an instance initializer.
Change code that looks like this:
App.initializer({
name: "clock",
initialize: function(container, application) {
application.register("clock:main", Clock);
var clock = container.lookup("clock:main");
clock.setStartTime(Date.now());
}
});
To:
App.initializer({
name: "clock",
initialize: function(registry, application) {
application.register("clock:main", Clock);
}
});
App.instanceInitializer({
name: "clock",
initialize: function(instance) {
var clock = instance.container.lookup("clock:main");
clock.setStartTime(Date.now());
}
});
Added in PR #10256.
Content in Handlebars templates is automatically HTML-escaped to help
developers prevent inadvertently introducing cross-site scripting (XSS)
vulnerabilities into their applications. If you want to display trusted
content as HTML, you can use a SafeString
, a special string that tells Ember
that the content should be displayed without escaping.
While this works great for HTML, there are some cases where you may bind user
data that the browser interprets as CSS, not HTML. For example, you may bind
the style
attribute of an element:
Handlebars only escapes HTML, not CSS, so this may introduce a potential XSS
vulnerability into your application if a malicious user is able to provide data
used in the myStyle
property.
Starting in Ember 1.11, you will receive a warning if you attempt to bind the
style
attribute of an element. Once you have verified that the content being
displayed is trusted and properly escaped, you can disable the warning by
making the content a SafeString
. For example:
myStyle: Ember.computed('color', function() {
var color = escapeCSS(this.get('color'));
return new Ember.Handlebars.SafeString("color: " + color);
})
You can learn more about SafeString
s and writing code that prevents XSS
attacks by reading the Writing
Helpers guide.
The in
syntax is used to name an iterated value with {{each}}
. Block
params, introduced Ember 1.10, obsoletes the in
syntax.
Each helpers should be updated to use block params. For example this helper:
Can be converted as follows:
For "itemController" :
Can be converted as follows:
Renaming a value using {{with}}
has been possible using the as
syntax. Block params,
introduces in Ember 1.10, obsolete the as
syntax.
With helpers should be updated to use block params. For example this helper:
Can be converted as follows:
Ember.js 1.12 introduces an improved syntax for computed properties with a setter. Previously, computed properties with a setter implemented that setter by inspecting the number of arguments passed to the computed property's descriptor.
For example, this computed property splits a full name into two parts when set:
fullName: Ember.computed("firstName", "lastName", function(key, newName) {
if (arguments.length > 1) {
var parts = newName.split(" ");
this.setProperties({ firstName: parts[0], lastName: parts[1] });
return newName;
} else {
return this.get("firstName") + " " + this.get("lastName");
}
});
These uses should be converted to use the new discrete getter and setter syntax introduced in 1.12:
fullName: Ember.computed("firstName", "lastName", {
get: function() {
return this.get("firstName") + " " + this.get("lastName");
},
set: function(key, newName) {
var parts = newName.split(" ");
this.setProperties({ firstName: parts[0], lastName: parts[1] });
return newName;
}
});
For further reading, review the RFC describing this feature and the pull request of the initial implementation.
Ember.js 1.13 is the last minor release of Ember 1.x before 2.0. Consequently, it has a rather large number of notable deprecations.
Ember 1.x encouraged a Model-View-Controller-Route architecture. Since then, the web has consolidated around a Model-Component-Route pattern for web development that Ember 2.0 also embraces.
Views are removed from the Ember 2.0 API. However a single release is likely insufficient for large apps to upgrade their entire codebase away from routeable views, and consequently Ember is providing extended support for views via the ember-legacy-views addon. This addon will remain compatible with Ember until v2.4 of the framework is released.
In most cases Ember views can be replaced with a component. For example this view and usage:
// app/views/show-menu.js
import Ember from "ember";
// Usage: {{view "show-menu"}}
export default Ember.View.extend({
templateName: 'some-menu',
title: 'My Menu'
});
Can be replaced with this component:
// app/components/show-menu.js
import Ember from "ember";
// Usage: {{show-menu}}
export default Ember.Component.extend({
title: 'My Menu'
});
Note that a component is always its own context in a template. A view's template
may have had access to other variables that were present where it was called,
such as a controller
. A component template will always be isolated, and
if data is needed it should be passed upon invocation. For example:
Some notable differences exist between yielded view blocks and yielded component blocks. A view could be used this way:
// app/views/reverse-name.js
import Ember from "ember";
export default Ember.View.extend({
reversedName: Ember.computed('name', function() {
return this.get('name').split("").reverse().join("");
})
});
Components introduced block params. This concept achieves data passing
without the confusion over what {{view}}
refers to. For example:
// app/components/reverse-name.js
import Ember from "ember";
export default Ember.Component.extend({
reversedName: Ember.computed('name', function() {
return this.get('name').split("").reverse().join("");
})
});
Just as passing values to a component allow access to those values in the isolated template of that component, yielding block params allow for values from the component to be passed to the location the component was called at.
When a template for a given route is rendered, if there is a view with the same name that view will also be used. For example this view is attached to the rendered route template:
// app/router.js
import Ember from "ember";
export default Ember.Router.map(function() {
this.route('about');
});
// app/views/about.js
import Ember from "ember";
export default Ember.View.extend({
classNameBindings: ['controller.isActive:active']
});
There are only two reasons a view may be used for a route.
- To set attribute bindings
- To attach event handlers
You should migrate away from routed views. For example to attach bindings an element in the template is sufficient:
// app/router.js
import Ember from "ember";
export default Ember.Router.map(function() {
this.route('about');
});
If attaching events or sharing DOM is necessary, consider a component:
// app/router.js
import Ember from "ember";
export default Ember.Router.map(function() {
this.route('about');
});
// app/components/active-layout.js
import Ember from "ember";
export default Ember.Component.extend({
classNameBindings: ['isActive:active'],
click() {
// Handle click
}
});
View and controller keywords provided a way to access the view or controller backing a template, even across scopes that you might expect to be isolated. In many ways their behavior is emergent, and generally is poorly designed.
Accessing data via {{view.someProp}}
or {{controller.thisThing}}
can
nearly always be replaced by proper use of data passing and block params. See
the guide on differences in yielded blocks
for a complete example of using block params over the {{view}}
keyword.
As a consequence of the deprecation of Ember.View
, many internal views have
been ported to component. Ember.LinkView
, the class that backs the
{{link-to}}
helper, have been ported to Ember.LinkComponent
.
Most uses of Ember.LinkView
can be directly ported to the Ember.LinkComponent
class.
Using the Ember.Select
global and its view helper form ({{view 'select'}}
)
is deprecated. Ember.Select
in Ember 1.x is implemented in a legacy coding
style that uses patterns such as observers and two-way data binding that
can cause pathological performance problems and provide an experience that
is not consistent with idiomatic Ember 2.0 development.
Legacy support of Ember.Select
will be provided via the ember-legacy-views addon.
Ember 2.0 provides several new features that make it much more straightforward to implement <select> tag functionality via the data-down, actions-up paradigm in one's codebase. For example, to create a component that displays a prompt and a list of dropdown options, the following code could be used:
// app/components/my-select.js
import Ember from "ember";
export default Ember.Component.extend({
content: null,
selectedValue: null,
didInitAttrs(attrs) {
this._super(...arguments);
var content = this.get('content');
if (!content) {
this.set('content', []);
}
},
actions: {
change() {
const changeAction = this.get('action');
const selectedEl = this.$('select')[0];
const selectedIndex = selectedEl.selectedIndex;
const content = this.get('content');
const selectedValue = content[selectedIndex];
this.set('selectedValue', selectedValue);
changeAction(selectedValue);
}
}
});
// is-equal helper is necessary to determine which option is currently selected.
// app/helpers/is-equal.js
import Ember from "ember";
export default Ember.Helper.helper(function([leftSide, rightSide]) {
return leftSide === rightSide;
});
This component could be used in a template by supplying it an array of strings
as content
and an action to call when the user makes a selection as change
:
A more complete example of a select
component that mimics many features of the
deprecated Ember.Select
is available in this jsbin.
Here is an example jsbin showing usage of the select tag directly in a template without a component.
There are many Ember-CLI addons that provide select-like functionality.
emberx-select in particular
aims to provide a select component based on the native html select. Alternative
select
component implementations can be iterated upon in addons to identify
best practices and perhaps moved into an official Ember 2.x implementation in
the future.
This two little monsters served us well for a long time. They provided semantics that allowed to
perform in-place modifications of an existing array instead of replacing the entire array like the
regular map/reduce
methods in javascript do, which was necessary to avoid expensive repaintings when
rendering lists with the {{each}}
helper.
But now that with the new glimmer engine you can generate a entirely new array and let glimmer handle the diffing for you they've become an unnecesary and overcomplicated abstraction that adds no value over the native counterparts.
They will be extracted as an ember addon in case you need to keep using them, but the recomendation now is to just use the native array methods or those in libraries like Underscore/Lodash.
Before observers are deprecated due to the negative performance implications they have for Ember internals and applications.
Typically they were used to have access to the old value of a property when it's about to change, but you can get same functionality in an even more efficient way with just a few lines of code:
function fooObserver(obj){
var newFoo = obj.get('foo');
if (obj._oldFoo !== newFoo) {
// do your stuff here
obj._oldFoo = newFoo;
}
}
addObserver(obj, 'foo', fooObserver);
fooObserver(obj); // Optionally call the observer immediately
The related function Ember.addBeforeObserver
, Ember.removeBeforeObserver
and Ember.beforeObserversFor
are deprecated too.
Just like Ember.ObjectController
, Ember.ArrayController
will be removed in
Ember 2.0 for the same reasons mentioned in 1.11's ObjectController
deprecation.
To migrate from an explicitly defined array controller, first convert
the class definition to inherit from Ember.Controller
. For example:
import Ember from "ember";
// Change:
export default Ember.ArrayController.extend({
// To:
export default Ember.Controller.extend({
// ...
Next update any use of {{modelPropertyName}}
in templates with {{model.modelPropertyName}}
.
You should also review any computed property dependent keys, observer keys, and get
and set
statements on the route and controller. An example of how to make this migration can
be found in this PR to the Ghost project.
Opposite to 1.11's ObjectController deprecation, if a controller is not explicitly defined, but instead is being auto-generated by the framework, it will not throw a deprecation message even if the proxying behavior is being used.
Added in PR #11476.
needs
for controllers will be removed in Ember 2.0. You can migrate away from this using Ember.inject.controller
Lets say you have a post
controller like this:
import Ember from 'ember';
export default Ember.Controller.extend({
needs: ['comments'],
newComments: Ember.computed.alias('controllers.comments.newest')
});
You can upgrade to Ember.inject.controller
like this:
import Ember from 'ember';
export default Ember.Controller.extend({
comments: Ember.inject.controller(),
newComments: Ember.computed.alias('comments.newest')
});
You can read more about Ember.inject.controller
in the Ember API documentation