One of the ways that Ember releases guarantee stability is by following Semantic Versioning (SemVer). For the Ember project this means that any feature that is to be removed must first be deprecated, and only removed when a major version is released. It also means that new features are introduced in a backwards compatible way.
To give the project a path forward when a breaking change is mandatory, we've released the @ember/optional-features
addon.
This addon does nothing by default, but provides a command-line interface to enable and disable breaking changes in Ember.
Installation
To get started with optional features, you must install the addon:
ember install @ember/optional-features
This will make three new commands available to Ember CLI within your project, feature:list
, feature:enable
, and feature:disable
.
Listing features
The optional features available to your project will depend on the Ember version your project is using.
To see which optional features are available, you can run the following command:
$ ember feature:list
Usage:
To list all available features, run ember feature:list.
To enable a feature, run ember feature:enable some-feature.
To disable a feature, run ember feature:disable some-feature.
Available features:
application-template-wrapper (Default: true)
Wrap the top-level application template (application.hbs) with a `<div class="ember-view">` element.
More information: https://github.com/emberjs/rfcs/pull/280
jquery-integration (Default: true)
Adds jQuery to the Ember application.
More information: https://github.com/emberjs/rfcs/pull/294
Features
Once you see a feature that you would like to toggle for your project you can run one of two commands, ember feature:enable <feature>
and ember feature:disable <feature>
.
Let us disable jquery-integration
to see what happens:
$ ember feature:disable jquery-integration
Disabled jquery-integration. Be sure to commit config/optional-features.json to source control!
As we can see from the warning, @ember/optional-features
has created a file in config/optional-features.json
to store the configuration for your project.
We commit it to our repository and we are off to the races!
jquery-integration
jQuery is commonly used for event handling and many popular libraries for charting and UI components. Ember was originally built using jQuery, but has since refactored and is no longer dependent on it. jQuery can still be used as in independent library alongside Ember, however.
There are a few APIs that exist in Ember still that directly expose jQuery,
Disabling this optional feature disables those APIs, but does not remove jQuery
itself. jQuery is provided by the @ember/jquery
addon, independent of the
integration APIs.
Migrating Away from and disabling Ember jQuery integration APIs
To disable this feature, first migrate away from the jQuery integration APIs. Below is a list of the jQuery specific APIs in Ember, and how to migrate away from them.
this.$()
in Classic Ember components. This creates a jQuery selector that targets the component's element. You can migrate away by usingthis.element
instead, which is the actual DOM element for the component. If you want to continue using jQuery via@ember/jquery
, you can do so withthis.element
:
import $ from 'jquery';
import Component from '@ember/copmonent';
export default class MyComponent extends Component {
didInsertElement() {
let el = $(this.element);
// ...
}
}
Event handlers on Classic components, such as
click()
andmouseEnter()
. These APIs still work without the integration enabled, but they no longer receive a jQuery event, they receive a native Event instead. You can convert to using native events incrementally by using theember-jquery-legacy
addon, which provides a function that converts a jQuery event into a native event safely. Once all of your event handlers have been converted, you can disable jQuery integration.Ember.$()
, which is an alias for the global jQuery. This can either be replaced with alternative APIs, or by installing@ember/jquery
and importing it directly.Global acceptance test helpers like
find()
orclick()
. These can be replaced with the@ember/test-helpers
, which is the default for test helpers now.this.$()
in component tests. This can be replaced with corresponding test helpers from@ember/test-helpers
.
Note that if you disable these APIs, then all addons you use must also work without them, as they will not be available at all.
Next, follow the instructions above to install @ember/optional-features
, and
run the following command to change @ember/optional-features
:
ember feature:disable jquery-integration
Including jQuery without integration APIs
If you would like to include jQuery without the Ember integration APIs, you can
install @ember/jquery
:
ember install @ember/jquery
This will allow you to import jQuery from jquery
:
import $ from 'jquery';
Including jQuery with integration APIs
To include jQuery in your Ember app and enable the jQuery integration APIs such
as this.$()
, follow the instructions above to install @ember/optional-features
.
Next, enable the feature:
ember feature:enable jquery-integration
Then, install the @ember/jquery
addon:
ember install @ember/jquery
Now, almost anywhere in your app, you can use the various jQuery integration.
Removing jQuery completely
If you are working on an application that already has jQuery installed, and would like to remove it, follow these steps.
First, refactor your own code to not depend on jQuery. See the section above on
how to do this. Keep in mind that you will have to remove jQuery usage entirely,
you cannot use solutions that replace the integration API with jQuery
independently such as $(this.element)
.
Next, follow the instructions above to install @ember/optional-features
, and
run the following command to change @ember/optional-features
:
ember feature:disable jquery-integration
Then, remove @ember/jquery
from your package.json.
This will remove jQuery from your vendor.js bundle and disable any use of jQuery in Ember itself. Now your app will be about 30KB lighter!
application-template-wrapper
With this feature enabled Ember creates a wrapping div around the entire
rendered application. Effectively, it is creating a <div class="ember-view">
element which wraps the contents of an application's
app/templates/application.hbs
file.
When disabled, this div will not be output. This is usually desirable, but may break the styling of an existing application in subtle ways:
- Perhaps the application relied on the root
.ember-view
for styles (CSS). - Perhaps the
<div>
itself was the target of styles (e.g.body > div > .some-child
). - The presence of a wrapping
<div>
means the application is contained in a block-layout element. When removed, and depending on if the application specifies arootElement
inconfig/environment.js
, the application may no longer be contained in a block-layout element.
If your application relies on those behaviors it is still recommended that
you disable this feature, and simply add an appropriate element to
app/templates/application.hbs
wrapping that template's {{outlet}}
.
For more information, see RFC #280.
template-only-glimmer-components
With this feature disabled Ember will create an implicit element for components which have no JavaScript file ("template-only components").
Enabling this feature will result in only the contents of the template being rendered, and additionally in no classic Ember component instance being instantiated to provide it context.
Some examples of how enabling this feature impacts app code are:
- In template-only component templates statements like
{{this}}
,{{this.foo}}
and{{foo}}
will beundefined
. Accessing arguments as{{@foo}}
will continue to work. - If this feature is enabled in an application with existing template-only
components, the removal of the wrapping
<div>
will happen to all uses of those template-only components. This can impact style and logic in a breaking manner. - Passing classes to an invocation (i.e.
{{my-component class="..."}}
) will no longer result in those classes being present on any element. This could be a change in behavior which impacts any reflected attribute passed as an argument, such asid=
ortagName=
. - Templates can use
...attributes
to target attributes and element modifiers passed from an angle bracket invocation.
Enabling this feature makes template-only components more consistent with
angle-bracket invocation and with Glimmer components. Additionally it improves
the performance of template-only components (there is no JS object instantiated
to provide context) and makes them an excellent replacement for use of Ember's
more complex and now discouraged API {{partial
.
It is recommended that you enable this feature. Existing applications adopting
this optional feature should add a .js
file for any existing template-only
components containing a basic Ember component class. This will maintain
backwards compatibility for existing templates while new template-only
components gain the advantages of this feature.
For more information, see RFC #278.
default-async-observers
With this feature enabled, Ember will run all observers in the application asynchronously by default. This leads to observers running in the run loop after the one in which the observed properties were updated.
If the feature is disabled, observers run synchronously and will be invoked as soon as their observed properties update.
Async observers are more performant than those that run synchronously
and can help you to manage your application state in a more predictable manner.
This is one of the reasons, why the default-async-observers
feature is
enabled by default in newly created, modern Ember applications.
The default-async-observers
feature affects the behavior of observers application-wide,
but you can still instruct individual observers to run synchronously or async
manually. By using the sync: true
option, observers who are otherwise async by default
can be marked as synchronous manually. Similarly, observers
can be set to run asynchronously using the sync: false
option.
import { observer } from '@ember/object';
Image.extend({
onImageSizeChange: observer({
dependentKeys: ['width', 'height'],
fn() {
// Fires async after width or height have updated
},
sync: false,
})
});
While the default-async-observers
feature is only enabled by default in modern Ember applications,
you can enable this optional feature in older apps (Ember 3.13+) as follows:
$ ember feature:enable default-async-observers
# Enable async observers application-wide. Be sure to commit config/optional-features.json to source control!