Skip to content

Latest commit

 

History

History
849 lines (630 loc) · 25.8 KB

v1.x.html.md

File metadata and controls

849 lines (630 loc) · 25.8 KB
title alias layout
Deprecations for v1.x
guides/deprecations/
deprecations

Deprecations Added in Ember 1.x

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).

Deprecations Added in 1.7

Observing container views like arrays.

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.

Ember.DeferredMixin and Ember.Deferred have been deprecated in favor of using RSVP.Promises.

.then on Ember.Application.

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.

Deprecations Added in 1.8

Global lookup of views

Previous to Ember 1.8, views would commonly be fetched from the global scope:

{{view App.SomeView}}
{{each item in items itemViewClass=App.SomeView}}

Since Ember 1.8, views are more appropriately resolved on the application via strings:

{{view "some"}}
{{each item in items itemViewClass="some"}}

They may also be fetched via a binding:

{{view view.someViewViaTheCurrentView}}
{{each itemViewClass=someViewViaAControllerProperty}}

In general, it is recommended that your Ember application avoid accessing globals from a template.

New usage of Ember.Select

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:

{{view Ember.Select content=manyItems}}

Since this lookup is now deprecated, the select view has been registered on an application as select. The new usage is:

{{view "select" content=manyItems}}

See the updated Ember.Select documentation and the built-in views guide for more details and examples.

Ember.js libraries and plugins

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.

Hash Location Paths Without Leading Slashes.

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.

Deprecations Added in 1.9

More Consistent Handlebars Scope

In Ember 1.9, the each and with helpers come in two flavors: a "context-switching" flavor and a "named-parameter" flavor.

{{#each post in posts}}
  {{!-- the context in here is the same as the outside context,
        and `post` references the current iteration --}}
{{/each}}

{{#each posts}}
  {{!-- the context in here has shifted to the individual post.
        the outer context is no longer accessible --}}
{{/each}}

{{#with post as otherPost}}
  {{!-- the context in here is the same as the outside context }}
{{/with}}

{{#with post}}
  {{!-- the context in here has shifted to the post.
        the outer context is no longer accessible --}}
{{/with}}

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.

Transition Plan

To transition your code to the new syntax, you can change templates that look like this:

{{#each people}}
  <p>{{firstName}} {{lastName}}</p>
  <p>{{address}}</p>
{{/each}}

with:

{{#each person in people}}
  <p>{{person.firstName}} {{person.lastName}}</p>
  <p>{{person.address}}</p>
{{/each}}

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:

<ul>
  {{#each}}
    <li>{{name}}</li>
  {{/each}}
</ul>
<ul>
  {{#each people}}
    <li>{{name}}</li>
  {{/each}}
</ul>

You should now be using:

<ul>
  {{#each person in this}}
    <li>{{person.name}}</li>
  {{/each}}
</ul>
<ul>
  {{#each person in people}}
    <li>{{person.name}}</li>
  {{/each}}
</ul>

Deprecations Added in 1.11

ObjectController

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.

Access to Instances in Initializers

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.

Warnings Added in 1.11

Binding Style Attributes

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:

<div style={{myStyle}}></div>

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 SafeStrings and writing code that prevents XSS attacks by reading the Writing Helpers guide.

Deprecations Added in 1.12

in syntax for {{each}}

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:

{{#each foo in bar}}

Can be converted as follows:

{{#each bar as |foo|}}

For "itemController" :

{{#each foo in bar itemController="abc"}}

Can be converted as follows:

{{#each bar itemController="abc" as |foo|}}

as syntax for {{with}}

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:

{{#with foo as bar}}

Can be converted as follows:

{{#with foo as |bar|}}

Computed Properties With a Shared Getter And Setter

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.

Deprecations Added in 1.13

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.View

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.

Migrating away from the view helper

In most cases Ember views can be replaced with a component. For example this view and usage:

{{! app/templates/show-menu.hbs }}
{{view.title}}
// 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/templates/components/show-menu.hbs }}
{{title}}
// 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:

{{show-menu options=controller.menuOptions}}
Differences in yielded blocks

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("");
  })
});
{{#view "reverse-name" name=controller.name}}
  {{view.reversedName}}
{{/view}}

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("");
  })
});
// app/templates/components/reverse-name.hbs
{{yield reversedName}}
{{#reverse-name name=controller.name as |reversedName|}}
  {{reversedName}}
{{/reverse-name}}

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.

Routable Views

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');
});
{{! app/templates/about.hbs }}
<div class="{{if isActive 'active'}}">
  <!-- something something -->
</div>

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/templates/about.hbs }}
{{#active-layout isActive=isActive}}
  <!-- something something -->
{{/active-layout}}
// app/components/active-layout.js
import Ember from "ember";

export default Ember.Component.extend({
  classNameBindings: ['isActive:active'],
  click() {
    // Handle click
  }
});
view and controller template keywords

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.

Ember.LinkView

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.

Ember.Select

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/templates/components/my-select.hbs}}
<select {{action 'change' on='change'}}>
  {{#each content key="@index" as |item|}}
    <option value="{{item}}" selected={{is-equal item selectedValue}}>
      {{item}}
    </option>
  {{/each}}
</select>
// 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:

{{! app/templates/application.hbs}}

The currently selected item: {{mySelectedItem}}.

{{! myItems is an array of strings: ['first', 'second', 'third',...] }}
{{! This uses the `action` and `mut` helpers to pass in an action that
    will update the (outer scope) `mySelectedItem` property with the user's
    selection. }}
{{my-select content=myItems action=(action (mut mySelectedItem))}}

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.

Ember.ReduceComputed / Ember.ArrayComputed

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.

beforeObserver

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.

ArrayController

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.

Controller.needs

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