Skip to content
This repository has been archived by the owner on Mar 22, 2019. It is now read-only.

Latest commit

 

History

History
860 lines (628 loc) · 23.8 KB

v2.x.html.md

File metadata and controls

860 lines (628 loc) · 23.8 KB
title alias layout
Deprecations for v2.x
guides/deprecations/
deprecations

Deprecations Added in Ember 2.x

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

Deprecations Added in 2.1

Initializer Arity

until: 3.0.0
id: ember-application.app-initializer-initialize-arguments

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
}

Ember.Application#registry / Ember.ApplicationInstance#registry

until: 3.0.0
id: ember-application.app-instance-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.ApplicationInstance#container

until: 3.0.0
id: ember-application.app-instance-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:

// 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
}

Ember debug function options

until: 3.0.0
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).

Ember.Component#defaultLayout

until: 3.0.0
id: ember-views.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
});

Ember.Component#currentState

until: 2.3.0
id: ember-view.current-state

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.

Deprecations Added in 2.2

Function as test in Ember.deprecate, Ember.warn, Ember.assert

until: 2.5.0
id: ember-debug.deprecate-test-as-function
Deprecated behavior

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

Demo.

Refactoring

You have 3 options to refactor second argument from function to boolean:

  1. Use IIFE.
  2. Use !!Constructor for constructors.
  3. 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);

Demo.

In a future version functions will be treated as truthy values instead of being executed.

Deprecations added in 2.3

Injected container access

until: 3.0.0
id: ember-application.injected-container

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');
  }
});

Deprecations Added in 2.4

{{#render}} helper with block

until: 2.4.0
id: ember-template-compiler.deprecate-render-block

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:

{{#render 'foo'}}
  <p>Stuff Here</p>
{{/render}}
{{#render 'foo' someModel}}
  <p>Stuff Here</p>
{{/render}}

Deprecations Added in 2.6

Ember.Component#didInitAttrs

until: 3.0.0
id: ember-views.did-init-attrs

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:

{{my-component handle="@tomdale"}}

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
  }
});

Model param in {{render helper

until: 3.0.0
id: ember-template-compiler.deprecate-render-model

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:

{{render 'foo-bar' someModel}}
{{! app/templates/foo-bar.hbs }}
<p>{{someProp}} template stuff here</p>
// app/controllers/foo-bar.js
export default Controller.extend({
  someProp: Ember.computed('model.yolo', function() {
    return this.get('model.yolo');
  })
});

Would be refactored to:

{{foo-bar model=someModel}}
{{! app/templates/components/foo-bar.hbs }}
<p>{{someProp}} template stuff here</p>
// app/components/foo-bar.js
export default Component.extend({
  someProp: Ember.computed('model.yolo', function() {
    return this.get('model.yolo');
  })
});

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

Deprecations Added in 2.7

Ember.Backburner

until: 2.8.0
id: ember-metal.ember-backburner

Ember.Backburner was private throughout the Ember 2.x series and will be removed after 2.8.

Ember.Binding

until: 3.0.0
id: ember-metal.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:

  1. Refactor global bindings to services
  2. Refactor oneWay bindings to readOnly computed properties
  3. 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.

Deprecations Added in 2.8

Use Ember.String.htmlSafe over Ember.Handlebars.SafeString

until: 3.0.0
id: ember-htmlbars.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.

Enumerable#contains

until: 3.0.0
id: ember-runtime.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'];

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.

Deprecations Added in Pending Features

Route#serialize

until: 3.0.0
id: ember-routing.serialize-function

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
  });
});

Deprecations Added in 2.11

renderToElement

until: 2.12.0
id: ember-views.render-to-element

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 been deprecated here.

{{render helper

until: 3.0.0
id: ember-template-compiler.deprecate-render

Using the {{render helper is deprecated in favor of using components. Please refactor uses of this helper to components:

For example, if you had:

{{render 'my-sidebar'}}
{{! app/templates/my-sidebar.hbs }}
<p>template stuff here</p>
// app/controllers/my-sidebar.js
export default Ember.Controller.extend({
});

You would refactor to a component like so:

{{my-sidebar}}
{{! app/templates/components/my-sidebar.hbs }}
<p>template stuff here</p>
// 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 using Ember.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.

Rendering into a {{render}} helper that resolves to an {{outlet}}.

until: 3.0.0
id: ember-routing.top-level-render-helper

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/templates/application.hbs }}
<div class="sidebar">{{render 'sidebar'}}</div>
<div class="main">{{outlet}}</div>
{{! app/templates/index.hbs }}
Index Content
// 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:

{{! app/templates/application.hbs }}
<div class="sidebar">{{from-elsewhere name='sidebar'}}</div>
<div class="main">{{outlet}}</div>
{{! app/templates/index.hbs }}
{{to-elsewhere named='sidebar' send=(component 'index-sidebar')}}
Index Content

For more informations of how to use ember-elsewhere, please visit the official documentation here.