diff --git a/broccoli/amd-compat-entrypoints/ember.debug.js b/broccoli/amd-compat-entrypoints/ember.debug.js
index 8493dde592d..198b2300139 100644
--- a/broccoli/amd-compat-entrypoints/ember.debug.js
+++ b/broccoli/amd-compat-entrypoints/ember.debug.js
@@ -56,18 +56,6 @@ d('@ember/-internals/runtime/index', emberinternalsRuntimeIndex);
import * as emberinternalsRuntimeLibExtRsvp from '@ember/-internals/runtime/lib/ext/rsvp';
d('@ember/-internals/runtime/lib/ext/rsvp', emberinternalsRuntimeLibExtRsvp);
-import * as emberinternalsRuntimeLibMixinsContainerProxy from '@ember/-internals/runtime/lib/mixins/container_proxy';
-d(
- '@ember/-internals/runtime/lib/mixins/container_proxy',
- emberinternalsRuntimeLibMixinsContainerProxy
-);
-
-import * as emberinternalsRuntimeLibMixinsRegistryProxy from '@ember/-internals/runtime/lib/mixins/registry_proxy';
-d(
- '@ember/-internals/runtime/lib/mixins/registry_proxy',
- emberinternalsRuntimeLibMixinsRegistryProxy
-);
-
import * as emberinternalsStringIndex from '@ember/-internals/string/index';
d('@ember/-internals/string/index', emberinternalsStringIndex);
@@ -185,12 +173,6 @@ d('@ember/engine/instance', emberEngineInstance);
import * as emberEngineLibEngineParent from '@ember/engine/lib/engine-parent';
d('@ember/engine/lib/engine-parent', emberEngineLibEngineParent);
-import * as emberEnumerableIndex from '@ember/enumerable/index';
-d('@ember/enumerable/index', emberEnumerableIndex);
-
-import * as emberEnumerableMutable from '@ember/enumerable/mutable';
-d('@ember/enumerable/mutable', emberEnumerableMutable);
-
import * as emberHelperIndex from '@ember/helper/index';
d('@ember/helper/index', emberHelperIndex);
@@ -227,9 +209,6 @@ d('@ember/object/lib/computed/computed_macros', emberObjectLibComputedComputedMa
import * as emberObjectMixin from '@ember/object/mixin';
d('@ember/object/mixin', emberObjectMixin);
-import * as emberObjectObservable from '@ember/object/observable';
-d('@ember/object/observable', emberObjectObservable);
-
import * as emberObjectObservers from '@ember/object/observers';
d('@ember/object/observers', emberObjectObservers);
diff --git a/package.json b/package.json
index c0489cc1a03..7d6a433d95f 100644
--- a/package.json
+++ b/package.json
@@ -205,8 +205,6 @@
"@ember/-internals/routing/index.js": "ember-source/@ember/-internals/routing/index.js",
"@ember/-internals/runtime/index.js": "ember-source/@ember/-internals/runtime/index.js",
"@ember/-internals/runtime/lib/ext/rsvp.js": "ember-source/@ember/-internals/runtime/lib/ext/rsvp.js",
- "@ember/-internals/runtime/lib/mixins/container_proxy.js": "ember-source/@ember/-internals/runtime/lib/mixins/container_proxy.js",
- "@ember/-internals/runtime/lib/mixins/registry_proxy.js": "ember-source/@ember/-internals/runtime/lib/mixins/registry_proxy.js",
"@ember/-internals/string/index.js": "ember-source/@ember/-internals/string/index.js",
"@ember/-internals/utility-types/index.js": "ember-source/@ember/-internals/utility-types/index.js",
"@ember/-internals/utils/index.js": "ember-source/@ember/-internals/utils/index.js",
@@ -247,8 +245,6 @@
"@ember/engine/instance.js": "ember-source/@ember/engine/instance.js",
"@ember/engine/lib/engine-parent.js": "ember-source/@ember/engine/lib/engine-parent.js",
"@ember/engine/parent.js": "ember-source/@ember/engine/parent.js",
- "@ember/enumerable/index.js": "ember-source/@ember/enumerable/index.js",
- "@ember/enumerable/mutable.js": "ember-source/@ember/enumerable/mutable.js",
"@ember/helper/index.js": "ember-source/@ember/helper/index.js",
"@ember/instrumentation/index.js": "ember-source/@ember/instrumentation/index.js",
"@ember/modifier/index.js": "ember-source/@ember/modifier/index.js",
@@ -263,7 +259,6 @@
"@ember/object/lib/computed/computed_macros.js": "ember-source/@ember/object/lib/computed/computed_macros.js",
"@ember/object/lib/computed/reduce_computed_macros.js": "ember-source/@ember/object/lib/computed/reduce_computed_macros.js",
"@ember/object/mixin.js": "ember-source/@ember/object/mixin.js",
- "@ember/object/observable.js": "ember-source/@ember/object/observable.js",
"@ember/object/observers.js": "ember-source/@ember/object/observers.js",
"@ember/owner/index.js": "ember-source/@ember/owner/index.js",
"@ember/renderer/index.js": "ember-source/@ember/renderer/index.js",
diff --git a/packages/@ember/-internals/glimmer/lib/component.ts b/packages/@ember/-internals/glimmer/lib/component.ts
index 86b1e748071..facb381a739 100644
--- a/packages/@ember/-internals/glimmer/lib/component.ts
+++ b/packages/@ember/-internals/glimmer/lib/component.ts
@@ -59,150 +59,6 @@ function matches(el: Element, selector: string): boolean {
@module @ember/component
*/
-interface ComponentMethods {
- // Overrideable methods are defined here since you can't `declare` a method in a class
-
- /**
- Called when the attributes passed into the component have been updated.
- Called both during the initial render of a container and during a rerender.
- Can be used in place of an observer; code placed here will be executed
- every time any attribute updates.
- @method didReceiveAttrs
- @public
- @since 1.13.0
- */
- didReceiveAttrs(): void;
-
- /**
- Called when the attributes passed into the component have been updated.
- Called both during the initial render of a container and during a rerender.
- Can be used in place of an observer; code placed here will be executed
- every time any attribute updates.
- @event didReceiveAttrs
- @public
- @since 1.13.0
- */
-
- /**
- Called after a component has been rendered, both on initial render and
- in subsequent rerenders.
- @method didRender
- @public
- @since 1.13.0
- */
- didRender(): void;
-
- /**
- Called after a component has been rendered, both on initial render and
- in subsequent rerenders.
- @event didRender
- @public
- @since 1.13.0
- */
-
- /**
- Called before a component has been rendered, both on initial render and
- in subsequent rerenders.
- @method willRender
- @public
- @since 1.13.0
- */
- willRender(): void;
-
- /**
- Called before a component has been rendered, both on initial render and
- in subsequent rerenders.
- @event willRender
- @public
- @since 1.13.0
- */
-
- /**
- Called when the attributes passed into the component have been changed.
- Called only during a rerender, not during an initial render.
- @method didUpdateAttrs
- @public
- @since 1.13.0
- */
- didUpdateAttrs(): void;
-
- /**
- Called when the attributes passed into the component have been changed.
- Called only during a rerender, not during an initial render.
- @event didUpdateAttrs
- @public
- @since 1.13.0
- */
-
- /**
- Called when the component is about to update and rerender itself.
- Called only during a rerender, not during an initial render.
- @method willUpdate
- @public
- @since 1.13.0
- */
- willUpdate(): void;
-
- /**
- Called when the component is about to update and rerender itself.
- Called only during a rerender, not during an initial render.
- @event willUpdate
- @public
- @since 1.13.0
- */
-
- /**
- Called when the component has updated and rerendered itself.
- Called only during a rerender, not during an initial render.
- @method didUpdate
- @public
- @since 1.13.0
- */
- didUpdate(): void;
-
- /**
- Called when the component has updated and rerendered itself.
- Called only during a rerender, not during an initial render.
- @event didUpdate
- @public
- @since 1.13.0
- */
-
- /**
- The HTML `id` of the component's element in the DOM. You can provide this
- value yourself but it must be unique (just as in HTML):
-
- ```handlebars
- {{my-component elementId="a-really-cool-id"}}
- ```
-
- ```handlebars
-
- ```
- If not manually set a default value will be provided by the framework.
- Once rendered an element's `elementId` is considered immutable and you
- should never change it. If you need to compute a dynamic value for the
- `elementId`, you should do this when the component or element is being
- instantiated:
-
- ```javascript
- export default class extends Component {
- init() {
- super.init(...arguments);
-
- var index = this.get('index');
- this.set('elementId', `component-id${index}`);
- }
- }
- ```
-
- @property elementId
- @type String
- @public
- */
- layoutName?: string;
-}
-
// A zero-runtime-overhead private symbol to use in branding the component to
// preserve its type parameter.
declare const SIGNATURE: unique symbol;
@@ -789,28 +645,12 @@ declare const SIGNATURE: unique symbol;
@extends Ember.CoreView
@public
*/
-// This type param is used in the class, so must appear here.
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-interface Component extends CoreView, ComponentMethods {}
-
class Component
- extends CoreView.extend(
- {
- // These need to be overridable via extend/create but should still
- // have a default. Defining them here is the best way to achieve that.
- didReceiveAttrs() {},
- didRender() {},
- didUpdate() {},
- didUpdateAttrs() {},
- willRender() {},
- willUpdate() {},
- } as ComponentMethods,
- {
- concatenatedProperties: ['attributeBindings', 'classNames', 'classNameBindings'],
- classNames: EMPTY_ARRAY,
- classNameBindings: EMPTY_ARRAY,
- }
- )
+ extends CoreView.extend({
+ concatenatedProperties: ['attributeBindings', 'classNames', 'classNameBindings'],
+ classNames: EMPTY_ARRAY,
+ classNameBindings: EMPTY_ARRAY,
+ })
implements PropertyDidChange
{
isComponent = true;
@@ -1657,6 +1497,98 @@ class Component
// End ViewMixin
+ // Begin lifecycle hooks
+
+ /**
+ Called when the attributes passed into the component have been updated.
+ Called both during the initial render of a container and during a rerender.
+ Can be used in place of an observer; code placed here will be executed
+ every time any attribute updates.
+ @method didReceiveAttrs
+ @public
+ @since 1.13.0
+ */
+ didReceiveAttrs(): void {}
+
+ /**
+ Called after a component has been rendered, both on initial render and
+ in subsequent rerenders.
+ @method didRender
+ @public
+ @since 1.13.0
+ */
+ didRender(): void {}
+
+ /**
+ Called after a component has been rendered, both on initial render and
+ in subsequent rerenders.
+ @event didRender
+ @public
+ @since 1.13.0
+ */
+
+ /**
+ Called before a component has been rendered, both on initial render and
+ in subsequent rerenders.
+ @method willRender
+ @public
+ @since 1.13.0
+ */
+ willRender(): void {}
+
+ /**
+ Called before a component has been rendered, both on initial render and
+ in subsequent rerenders.
+ @event willRender
+ @public
+ @since 1.13.0
+ */
+
+ /**
+ Called when the attributes passed into the component have been changed.
+ Called only during a rerender, not during an initial render.
+ @method didUpdateAttrs
+ @public
+ @since 1.13.0
+ */
+ didUpdateAttrs(): void {}
+
+ /**
+ Called when the attributes passed into the component have been changed.
+ Called only during a rerender, not during an initial render.
+ @event didUpdateAttrs
+ @public
+ @since 1.13.0
+ */
+
+ /**
+ Called when the component is about to update and rerender itself.
+ Called only during a rerender, not during an initial render.
+ @method willUpdate
+ @public
+ @since 1.13.0
+ */
+ willUpdate(): void {}
+
+ /**
+ Called when the component is about to update and rerender itself.
+ Called only during a rerender, not during an initial render.
+ @event willUpdate
+ @public
+ @since 1.13.0
+ */
+
+ /**
+ Called when the component has updated and rerendered itself.
+ Called only during a rerender, not during an initial render.
+ @method didUpdate
+ @public
+ @since 1.13.0
+ */
+ didUpdate(): void {}
+
+ // End lifecycle hooks
+
static isComponentFactory = true;
static toString() {
diff --git a/packages/@ember/-internals/package.json b/packages/@ember/-internals/package.json
index 58d115f325f..9a9ef015325 100644
--- a/packages/@ember/-internals/package.json
+++ b/packages/@ember/-internals/package.json
@@ -29,7 +29,6 @@
"@ember/debug": "workspace:*",
"@ember/destroyable": "workspace:*",
"@ember/engine": "workspace:*",
- "@ember/enumerable": "workspace:*",
"@ember/helper": "workspace:*",
"@ember/instrumentation": "workspace:*",
"@ember/modifier": "workspace:*",
diff --git a/packages/@ember/-internals/runtime/index.ts b/packages/@ember/-internals/runtime/index.ts
index c60cf8c9ccc..fb977fe1c7a 100644
--- a/packages/@ember/-internals/runtime/index.ts
+++ b/packages/@ember/-internals/runtime/index.ts
@@ -1,5 +1 @@
-export { default as RegistryProxyMixin } from './lib/mixins/registry_proxy';
-export { default as ContainerProxyMixin } from './lib/mixins/container_proxy';
-export { default as MutableEnumerable } from '@ember/enumerable/mutable';
-
export { default as RSVP, onerrorDefault } from './lib/ext/rsvp'; // just for side effect of extending Ember.RSVP
diff --git a/packages/@ember/-internals/runtime/lib/mixins/container_proxy.ts b/packages/@ember/-internals/runtime/lib/mixins/container_proxy.ts
deleted file mode 100644
index 9b9a870a7c9..00000000000
--- a/packages/@ember/-internals/runtime/lib/mixins/container_proxy.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { schedule, join } from '@ember/runloop';
-/**
-@module ember
-*/
-import type Container from '@ember/-internals/container/lib/container';
-import Mixin from '@ember/object/mixin';
-import type { ContainerProxy } from '@ember/-internals/owner';
-
-// This is defined as a separate interface so that it can be used in the definition of
-// `Owner` without also including the `__container__` property.
-
-/**
- ContainerProxyMixin is used to provide public access to specific
- container functionality.
-
- @class ContainerProxyMixin
- @extends ContainerProxy
- @private
-*/
-interface ContainerProxyMixin extends ContainerProxy {
- /** @internal */
- __container__: Container;
-}
-const ContainerProxyMixin = Mixin.create({
- /**
- The container stores state.
-
- @private
- @property {Ember.Container} __container__
- */
- __container__: null,
-
- ownerInjection() {
- return this.__container__.ownerInjection();
- },
-
- lookup(fullName: string, options: object) {
- return this.__container__.lookup(fullName, options);
- },
-
- destroy() {
- let container = this.__container__;
-
- if (container) {
- join(() => {
- container.destroy();
- schedule('destroy', container, 'finalizeDestroy');
- });
- }
-
- this._super();
- },
-
- factoryFor(fullName: string) {
- return this.__container__.factoryFor(fullName);
- },
-});
-
-export default ContainerProxyMixin;
diff --git a/packages/@ember/-internals/runtime/lib/mixins/registry_proxy.ts b/packages/@ember/-internals/runtime/lib/mixins/registry_proxy.ts
deleted file mode 100644
index 1399a185d49..00000000000
--- a/packages/@ember/-internals/runtime/lib/mixins/registry_proxy.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
-@module ember
-*/
-
-import type { Registry } from '@ember/-internals/container';
-import type { RegistryProxy } from '@ember/-internals/owner';
-import type { AnyFn } from '@ember/-internals/utility-types';
-
-import { assert } from '@ember/debug';
-import Mixin from '@ember/object/mixin';
-
-/**
- RegistryProxyMixin is used to provide public access to specific
- registry functionality.
-
- @class RegistryProxyMixin
- @extends RegistryProxy
- @private
-*/
-interface RegistryProxyMixin extends RegistryProxy {
- /** @internal */
- __registry__: Registry;
-}
-const RegistryProxyMixin = Mixin.create({
- __registry__: null,
-
- resolveRegistration(fullName: string) {
- assert('fullName must be a proper full name', this.__registry__.isValidFullName(fullName));
- return this.__registry__.resolve(fullName);
- },
-
- register: registryAlias('register'),
- unregister: registryAlias('unregister'),
- hasRegistration: registryAlias('has'),
- registeredOption: registryAlias('getOption'),
- registerOptions: registryAlias('options'),
- registeredOptions: registryAlias('getOptions'),
- registerOptionsForType: registryAlias('optionsForType'),
- registeredOptionsForType: registryAlias('getOptionsForType'),
-});
-
-type AliasMethods =
- | 'register'
- | 'unregister'
- | 'has'
- | 'getOption'
- | 'options'
- | 'getOptions'
- | 'optionsForType'
- | 'getOptionsForType';
-
-function registryAlias(name: N) {
- return function (this: RegistryProxyMixin, ...args: Parameters) {
- // We need this cast because `Parameters` is deferred so that it is not
- // possible for TS to see it will always produce the right type. However,
- // since `AnyFn` has a rest type, it is allowed. See discussion on [this
- // issue](https://github.com/microsoft/TypeScript/issues/47615).
- return (this.__registry__[name] as AnyFn)(...args);
- };
-}
-
-export default RegistryProxyMixin;
diff --git a/packages/@ember/-internals/runtime/tests/mixins/container_proxy_test.js b/packages/@ember/-internals/runtime/tests/mixins/container_proxy_test.js
deleted file mode 100644
index 1f9cbcfa175..00000000000
--- a/packages/@ember/-internals/runtime/tests/mixins/container_proxy_test.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import { getOwner } from '@ember/-internals/owner';
-import { Container, Registry } from '@ember/-internals/container';
-import ContainerProxy from '../../lib/mixins/container_proxy';
-import EmberObject from '@ember/object';
-import { run, schedule } from '@ember/runloop';
-import { moduleFor, AbstractTestCase } from 'internal-test-helpers';
-import { destroy } from '@glimmer/destroyable';
-
-moduleFor(
- '@ember/-internals/runtime/mixins/container_proxy',
- class extends AbstractTestCase {
- beforeEach() {
- this.Owner = EmberObject.extend(ContainerProxy);
- this.instance = this.Owner.create();
-
- this.registry = new Registry();
-
- this.instance.__container__ = new Container(this.registry, {
- owner: this.instance,
- });
- }
-
- ['@test provides ownerInjection helper method'](assert) {
- let result = this.instance.ownerInjection();
-
- assert.equal(getOwner(result), this.instance, 'returns an object with an associated owner');
- }
-
- ['@test actions queue completes before destruction'](assert) {
- assert.expect(1);
-
- this.registry.register(
- 'service:auth',
- class extends EmberObject {
- willDestroy() {
- assert.ok(getOwner(this).lookup('service:auth'), 'can still lookup');
- }
- }
- );
-
- let service = this.instance.lookup('service:auth');
-
- run(() => {
- schedule('actions', service, 'destroy');
- this.instance.destroy();
- });
- }
-
- '@test being destroyed by @ember/destroyable properly destroys the container and created instances'(
- assert
- ) {
- assert.expect(1);
-
- this.registry.register(
- 'service:foo',
- class FooService extends EmberObject {
- willDestroy() {
- assert.ok(true, 'is properly destroyed');
- }
- }
- );
-
- this.instance.lookup('service:foo');
-
- run(() => {
- destroy(this.instance);
- });
- }
- }
-);
diff --git a/packages/@ember/-internals/views/lib/system/event_dispatcher.ts b/packages/@ember/-internals/views/lib/system/event_dispatcher.ts
index db3512393fc..f2f1c19b4f9 100644
--- a/packages/@ember/-internals/views/lib/system/event_dispatcher.ts
+++ b/packages/@ember/-internals/views/lib/system/event_dispatcher.ts
@@ -350,13 +350,13 @@ export default class EventDispatcher extends EmberObject {
destroy() {
if (this._didSetup === false) {
- return;
+ return this;
}
let rootElement = this._sanitizedRootElement;
if (!rootElement) {
- return;
+ return this;
}
for (let event in this._eventHandlers) {
@@ -365,7 +365,7 @@ export default class EventDispatcher extends EmberObject {
rootElement.classList.remove(ROOT_ELEMENT_CLASS);
- return this._super(...arguments);
+ return super.destroy();
}
toString() {
diff --git a/packages/@ember/array/package.json b/packages/@ember/array/package.json
index 40ae1570bf6..de9b0400387 100644
--- a/packages/@ember/array/package.json
+++ b/packages/@ember/array/package.json
@@ -12,7 +12,6 @@
"@ember/-internals": "workspace:*",
"@ember/application": "workspace:*",
"@ember/debug": "workspace:*",
- "@ember/enumerable": "workspace:*",
"@ember/object": "workspace:*",
"@ember/runloop": "workspace:*",
"@ember/utils": "workspace:*",
diff --git a/packages/@ember/controller/index.ts b/packages/@ember/controller/index.ts
index 38575db7073..f6bd7021d54 100644
--- a/packages/@ember/controller/index.ts
+++ b/packages/@ember/controller/index.ts
@@ -3,9 +3,7 @@ import { computed, get } from '@ember/object';
import { FrameworkObject } from '@ember/object/-internals';
import { inject as metalInject } from '@ember/-internals/metal';
import type { DecoratorPropertyDescriptor, ElementDescriptor } from '@ember/-internals/metal';
-import Mixin from '@ember/object/mixin';
import type { RouteArgs } from '@ember/routing/-internals';
-import { symbol } from '@ember/-internals/utils';
import type { Transition } from 'router_js';
export type ControllerQueryParamType = 'boolean' | 'number' | 'array' | 'string';
@@ -14,274 +12,113 @@ export type ControllerQueryParam =
| Record
| Record;
-const MODEL = symbol('MODEL');
+const MODEL = Symbol('MODEL');
/**
@module @ember/controller
*/
+// NOTE: This doesn't actually extend EmberObject.
/**
- @class ControllerMixin
- @namespace Ember
- @private
+ @class Controller
+ @extends EmberObject
+ @public
*/
-interface ControllerMixin {
- /** @internal */
- _qpDelegate: unknown | null;
-
- isController: true;
-
+class Controller extends FrameworkObject.extend({
+ concatenatedProperties: ['queryParams'],
+}) {
/**
- The object to which actions from the view should be sent.
+ This property is updated to various different callback functions depending on
+ the current "state" of the backing route. It is used by
+ `Controller.prototype._qpChanged`.
- For example, when a Handlebars template uses the `{{action}}` helper,
- it will attempt to send the action to the view's controller's `target`.
+ The methods backing each state can be found in the `Route.prototype._qp` computed
+ property return value (the `.states` property). The current values are listed here for
+ the sanity of future travelers:
- By default, the value of the target property is set to the router, and
- is injected when a controller is instantiated. This injection is applied
- as part of the application's initialization process. In most cases the
- `target` property will automatically be set to the logical consumer of
- actions for the controller.
+ * `inactive` - This state is used when this controller instance is not part of the active
+ route hierarchy. Set in `Route.prototype._reset` (a `router.js` microlib hook) and
+ `Route.prototype.actions.finalizeQueryParamChange`.
+ * `active` - This state is used when this controller instance is part of the active
+ route hierarchy. Set in `Route.prototype.actions.finalizeQueryParamChange`.
+ * `allowOverrides` - This state is used in `Route.prototype.setup` (`route.js` microlib hook).
- @property target
- @default null
- @public
+ @method _qpDelegate
+ @private
*/
- target: unknown | null;
+ /** @internal */
+ declare _qpDelegate: unknown; // Set by route
- /**
- The controller's current model. When retrieving or modifying a controller's
- model, this property should be used instead of the `content` property.
+ /* ducktype as a controller */
+ isController = true;
- @property model
- @public
- */
- model: T;
+ declare namespace: unknown;
/**
- Defines which query parameters the controller accepts.
- If you give the names `['category','page']` it will bind
- the values of these query parameters to the variables
- `this.category` and `this.page`.
-
- By default, query parameters are parsed as strings. This
- may cause unexpected behavior if a query parameter is used with `toggleProperty`,
- because the initial value set for `param=false` will be the string `"false"`, which is truthy.
-
- To avoid this, you may specify that the query parameter should be parsed as a boolean
- by using the following verbose form with a `type` property:
- ```javascript
- queryParams: [{
- category: {
- type: 'boolean'
- }
- }]
- ```
- Available values for the `type` parameter are `'boolean'`, `'number'`, `'array'`, and `'string'`.
- If query param type is not specified, it will default to `'string'`.
-
- @for Ember.ControllerMixin
- @property queryParams
- @public
- */
- queryParams: Readonly>;
+ The object to which actions from the view should be sent.
- /**
- Transition the application into another route. The route may
- be either a single route or route path:
-
- ```javascript
- aController.transitionToRoute('blogPosts');
- aController.transitionToRoute('blogPosts.recentEntries');
- ```
-
- Optionally supply a model for the route in question. The model
- will be serialized into the URL using the `serialize` hook of
- the route:
-
- ```javascript
- aController.transitionToRoute('blogPost', aPost);
- ```
-
- If a literal is passed (such as a number or a string), it will
- be treated as an identifier instead. In this case, the `model`
- hook of the route will be triggered:
-
- ```javascript
- aController.transitionToRoute('blogPost', 1);
- ```
-
- Multiple models will be applied last to first recursively up the
- route tree.
-
- ```app/router.js
- Router.map(function() {
- this.route('blogPost', { path: ':blogPostId' }, function() {
- this.route('blogComment', { path: ':blogCommentId', resetNamespace: true });
- });
- });
- ```
-
- ```javascript
- aController.transitionToRoute('blogComment', aPost, aComment);
- aController.transitionToRoute('blogComment', 1, 13);
- ```
-
- It is also possible to pass a URL (a string that starts with a
- `/`).
-
- ```javascript
- aController.transitionToRoute('/');
- aController.transitionToRoute('/blog/post/1/comment/13');
- aController.transitionToRoute('/blog/posts?sort=title');
- ```
-
- An options hash with a `queryParams` property may be provided as
- the final argument to add query parameters to the destination URL.
-
- ```javascript
- aController.transitionToRoute('blogPost', 1, {
- queryParams: { showComments: 'true' }
- });
-
- // if you just want to transition the query parameters without changing the route
- aController.transitionToRoute({ queryParams: { sort: 'date' } });
- ```
-
- See also [replaceRoute](/ember/release/classes/Ember.ControllerMixin/methods/replaceRoute?anchor=replaceRoute).
-
- @for Ember.ControllerMixin
- @method transitionToRoute
- @deprecated Use transitionTo from the Router service instead.
- @param {String} [name] the name of the route or a URL
- @param {...Object} models the model(s) or identifier(s) to be used
- while transitioning to the route.
- @param {Object} [options] optional hash with a queryParams property
- containing a mapping of query parameters
- @return {Transition} the transition object associated with this
- attempted transition
- @public
- */
- transitionToRoute(...args: RouteArgs): Transition;
-
- /**
- Transition into another route while replacing the current URL, if possible.
- This will replace the current history entry instead of adding a new one.
- Beside that, it is identical to `transitionToRoute` in all other respects.
-
- ```javascript
- aController.replaceRoute('blogPosts');
- aController.replaceRoute('blogPosts.recentEntries');
- ```
-
- Optionally supply a model for the route in question. The model
- will be serialized into the URL using the `serialize` hook of
- the route:
-
- ```javascript
- aController.replaceRoute('blogPost', aPost);
- ```
-
- If a literal is passed (such as a number or a string), it will
- be treated as an identifier instead. In this case, the `model`
- hook of the route will be triggered:
-
- ```javascript
- aController.replaceRoute('blogPost', 1);
- ```
-
- Multiple models will be applied last to first recursively up the
- route tree.
-
- ```app/router.js
- Router.map(function() {
- this.route('blogPost', { path: ':blogPostId' }, function() {
- this.route('blogComment', { path: ':blogCommentId', resetNamespace: true });
- });
- });
- ```
-
- ```
- aController.replaceRoute('blogComment', aPost, aComment);
- aController.replaceRoute('blogComment', 1, 13);
- ```
-
- It is also possible to pass a URL (a string that starts with a
- `/`).
-
- ```javascript
- aController.replaceRoute('/');
- aController.replaceRoute('/blog/post/1/comment/13');
- ```
-
- @for Ember.ControllerMixin
- @method replaceRoute
- @deprecated Use replaceWith from the Router service instead.
- @param {String} [name] the name of the route or a URL
- @param {...Object} models the model(s) or identifier(s) to be used
- while transitioning to the route.
- @param {Object} [options] optional hash with a queryParams property
- containing a mapping of query parameters
- @return {Transition} the transition object associated with this
- attempted transition
- @public
- */
- replaceRoute(...args: RouteArgs): Transition;
-}
-const ControllerMixin = Mixin.create({
- // Support the action hash which is still used internally
- mergedProperties: ['actions'],
+ For example, when a Handlebars template uses the `{{action}}` helper,
+ it will attempt to send the action to the view's controller's `target`.
- /* ducktype as a controller */
- isController: true,
+ By default, the value of the target property is set to the router, and
+ is injected when a controller is instantiated. This injection is applied
+ as part of the application's initialization process. In most cases the
+ `target` property will automatically be set to the logical consumer of
+ actions for the controller.
- concatenatedProperties: ['queryParams'],
+ @property target
+ @default null
+ @public
+ */
+ declare target: unknown;
- target: null,
-
- store: null,
-
- init() {
- this._super(...arguments);
+ init(properties: object | undefined) {
+ super.init(properties);
let owner = getOwner(this);
if (owner) {
this.namespace = owner.lookup('application:main');
this.target = owner.lookup('router:main');
}
- },
+ }
- model: computed({
- get() {
- return this[MODEL];
- },
+ declare [MODEL]: T;
- set(_key, value) {
- return (this[MODEL] = value);
- },
- }),
+ @computed
+ get model() {
+ return this[MODEL]!;
+ }
- queryParams: null,
+ set model(value: T) {
+ this[MODEL] = value;
+ }
/**
- This property is updated to various different callback functions depending on
- the current "state" of the backing route. It is used by
- `Controller.prototype._qpChanged`.
-
- The methods backing each state can be found in the `Route.prototype._qp` computed
- property return value (the `.states` property). The current values are listed here for
- the sanity of future travelers:
-
- * `inactive` - This state is used when this controller instance is not part of the active
- route hierarchy. Set in `Route.prototype._reset` (a `router.js` microlib hook) and
- `Route.prototype.actions.finalizeQueryParamChange`.
- * `active` - This state is used when this controller instance is part of the active
- route hierarchy. Set in `Route.prototype.actions.finalizeQueryParamChange`.
- * `allowOverrides` - This state is used in `Route.prototype.setup` (`route.js` microlib hook).
-
- @method _qpDelegate
- @private
- */
- _qpDelegate: null, // set by route
+ Defines which query parameters the controller accepts.
+ If you give the names `['category','page']` it will bind
+ the values of these query parameters to the variables
+ `this.category` and `this.page`.
+
+ By default, query parameters are parsed as strings. This
+ may cause unexpected behavior if a query parameter is used with `toggleProperty`,
+ because the initial value set for `param=false` will be the string `"false"`, which is truthy.
+
+ To avoid this, you may specify that the query parameter should be parsed as a boolean
+ by using the following verbose form with a `type` property:
+ ```javascript
+ queryParams: [{
+ category: {
+ type: 'boolean'
+ }
+ }]
+ ```
+ Available values for the `type` parameter are `'boolean'`, `'number'`, `'array'`, and `'string'`.
+ If query param type is not specified, it will default to `'string'`.
+
+ @for Ember.Controller
+ @property queryParams
+ @public
+ */
+ declare queryParams: Readonly>;
/**
During `Route#setup` observers are created to invoke this method
@@ -302,18 +139,150 @@ const ControllerMixin = Mixin.create({
let delegate = controller._qpDelegate;
let value = get(controller, prop);
delegate(prop, value);
- },
-});
+ }
-// NOTE: This doesn't actually extend EmberObject.
-/**
- @class Controller
- @extends EmberObject
- @uses Ember.ControllerMixin
- @public
-*/
-interface Controller<_T = unknown> extends FrameworkObject, ControllerMixin<_T> {}
-class Controller<_T = unknown> extends FrameworkObject.extend(ControllerMixin) {}
+ /**
+ Transition the application into another route. The route may
+ be either a single route or route path:
+
+ ```javascript
+ aController.transitionToRoute('blogPosts');
+ aController.transitionToRoute('blogPosts.recentEntries');
+ ```
+
+ Optionally supply a model for the route in question. The model
+ will be serialized into the URL using the `serialize` hook of
+ the route:
+
+ ```javascript
+ aController.transitionToRoute('blogPost', aPost);
+ ```
+
+ If a literal is passed (such as a number or a string), it will
+ be treated as an identifier instead. In this case, the `model`
+ hook of the route will be triggered:
+
+ ```javascript
+ aController.transitionToRoute('blogPost', 1);
+ ```
+
+ Multiple models will be applied last to first recursively up the
+ route tree.
+
+ ```app/router.js
+ Router.map(function() {
+ this.route('blogPost', { path: ':blogPostId' }, function() {
+ this.route('blogComment', { path: ':blogCommentId', resetNamespace: true });
+ });
+ });
+ ```
+
+ ```javascript
+ aController.transitionToRoute('blogComment', aPost, aComment);
+ aController.transitionToRoute('blogComment', 1, 13);
+ ```
+
+ It is also possible to pass a URL (a string that starts with a
+ `/`).
+
+ ```javascript
+ aController.transitionToRoute('/');
+ aController.transitionToRoute('/blog/post/1/comment/13');
+ aController.transitionToRoute('/blog/posts?sort=title');
+ ```
+
+ An options hash with a `queryParams` property may be provided as
+ the final argument to add query parameters to the destination URL.
+
+ ```javascript
+ aController.transitionToRoute('blogPost', 1, {
+ queryParams: { showComments: 'true' }
+ });
+
+ // if you just want to transition the query parameters without changing the route
+ aController.transitionToRoute({ queryParams: { sort: 'date' } });
+ ```
+
+ See also [replaceRoute](/ember/release/classes/Ember.Controller/methods/replaceRoute?anchor=replaceRoute).
+
+ @for Ember.Controller
+ @method transitionToRoute
+ @deprecated Use transitionTo from the Router service instead.
+ @param {String} [name] the name of the route or a URL
+ @param {...Object} models the model(s) or identifier(s) to be used
+ while transitioning to the route.
+ @param {Object} [options] optional hash with a queryParams property
+ containing a mapping of query parameters
+ @return {Transition} the transition object associated with this
+ attempted transition
+ @public
+ */
+ declare transitionToRoute: (...args: RouteArgs) => Transition;
+
+ /**
+ Transition into another route while replacing the current URL, if possible.
+ This will replace the current history entry instead of adding a new one.
+ Beside that, it is identical to `transitionToRoute` in all other respects.
+
+ ```javascript
+ aController.replaceRoute('blogPosts');
+ aController.replaceRoute('blogPosts.recentEntries');
+ ```
+
+ Optionally supply a model for the route in question. The model
+ will be serialized into the URL using the `serialize` hook of
+ the route:
+
+ ```javascript
+ aController.replaceRoute('blogPost', aPost);
+ ```
+
+ If a literal is passed (such as a number or a string), it will
+ be treated as an identifier instead. In this case, the `model`
+ hook of the route will be triggered:
+
+ ```javascript
+ aController.replaceRoute('blogPost', 1);
+ ```
+
+ Multiple models will be applied last to first recursively up the
+ route tree.
+
+ ```app/router.js
+ Router.map(function() {
+ this.route('blogPost', { path: ':blogPostId' }, function() {
+ this.route('blogComment', { path: ':blogCommentId', resetNamespace: true });
+ });
+ });
+ ```
+
+ ```
+ aController.replaceRoute('blogComment', aPost, aComment);
+ aController.replaceRoute('blogComment', 1, 13);
+ ```
+
+ It is also possible to pass a URL (a string that starts with a
+ `/`).
+
+ ```javascript
+ aController.replaceRoute('/');
+ aController.replaceRoute('/blog/post/1/comment/13');
+ ```
+
+ @for Ember.Controller
+ @method replaceRoute
+ @deprecated Use replaceWith from the Router service instead.
+ @param {String} [name] the name of the route or a URL
+ @param {...Object} models the model(s) or identifier(s) to be used
+ while transitioning to the route.
+ @param {Object} [options] optional hash with a queryParams property
+ containing a mapping of query parameters
+ @return {Transition} the transition object associated with this
+ attempted transition
+ @public
+ */
+ declare replaceRoute: (...args: RouteArgs) => Transition;
+}
/**
Creates a property that lazily looks up another controller in the container.
@@ -366,7 +335,7 @@ export function inject(
return metalInject('controller', ...args);
}
-export { Controller as default, ControllerMixin };
+export default Controller;
/**
A type registry for Ember `Controller`s. Meant to be declaration-merged so string
diff --git a/packages/@ember/debug/package.json b/packages/@ember/debug/package.json
index a4bef6ab2e3..2772bd52d89 100644
--- a/packages/@ember/debug/package.json
+++ b/packages/@ember/debug/package.json
@@ -12,7 +12,6 @@
"@ember/application": "workspace:*",
"@ember/array": "workspace:*",
"@ember/engine": "workspace:*",
- "@ember/enumerable": "workspace:*",
"@ember/object": "workspace:*",
"@ember/owner": "workspace:*",
"@ember/routing": "workspace:*",
diff --git a/packages/@ember/engine/index.ts b/packages/@ember/engine/index.ts
index e16240fa87d..b0d8f420193 100644
--- a/packages/@ember/engine/index.ts
+++ b/packages/@ember/engine/index.ts
@@ -14,7 +14,8 @@ import EngineInstance from '@ember/engine/instance';
import { RoutingService } from '@ember/routing/-internals';
import { ComponentLookup } from '@ember/-internals/views';
import { setupEngineRegistry } from '@ember/-internals/glimmer';
-import { RegistryProxyMixin } from '@ember/-internals/runtime';
+import type { FullName, RegisterOptions } from '@ember/owner';
+import type { FactoryClass, InternalFactory } from '@ember/-internals/owner';
function props(obj: object) {
let properties = [];
@@ -50,12 +51,9 @@ export interface Initializer {
@class Engine
@extends Ember.Namespace
- @uses RegistryProxyMixin
@public
*/
-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface Engine extends RegistryProxyMixin {}
-class Engine extends Namespace.extend(RegistryProxyMixin) {
+class Engine extends Namespace {
static initializers: Record> = Object.create(null);
static instanceInitializers: Record> = Object.create(null);
@@ -443,6 +441,79 @@ class Engine extends Namespace.extend(RegistryProxyMixin) {
graph.topsort(cb);
}
+
+ // Registry Proxy
+ // Duplicated with EngineInstance
+
+ declare __registry__: Registry;
+
+ resolveRegistration(fullName: string) {
+ assert('fullName must be a proper full name', this.__registry__.isValidFullName(fullName));
+ return this.__registry__.resolve(fullName);
+ }
+
+ register(
+ fullName: FullName,
+ factory: InternalFactory,
+ options: RegisterOptions & { instantiate: true }
+ ): void;
+ register(fullName: FullName, factory: object, options?: RegisterOptions): void;
+ register(
+ fullName: FullName,
+ factory: InternalFactory,
+ options?: RegisterOptions
+ ): void;
+ register(...args: Parameters) {
+ return this.__registry__.register(...args);
+ }
+
+ /**
+ Unregister a fullName
+ */
+ unregister(fullName: FullName) {
+ this.__registry__.unregister(fullName);
+ }
+
+ /**
+ Given a fullName check if the registry is aware of its factory
+ or singleton instance.
+
+ @private
+ @method hasRegistration
+ @param {String} fullName
+ @param {Object} [options]
+ @param {String} [options.source] the fullname of the request source (used for local lookups)
+ @return {Boolean}
+ */
+ hasRegistration(fullName: FullName): boolean {
+ return this.__registry__.has(fullName);
+ }
+
+ registeredOption(
+ fullName: FullName,
+ optionName: K
+ ): RegisterOptions[K] | undefined {
+ return this.__registry__.getOption(fullName, optionName);
+ }
+
+ registerOptions(fullName: FullName, options: RegisterOptions) {
+ return this.__registry__.options(fullName, options);
+ }
+
+ registeredOptions(fullName: FullName): RegisterOptions | undefined {
+ return this.__registry__.getOptions(fullName);
+ }
+
+ /**
+ Allow registering options for all factories of a type.
+ */
+ registerOptionsForType(type: string, options: RegisterOptions) {
+ return this.__registry__.optionsForType(type, options);
+ }
+
+ registeredOptionsForType(type: string): RegisterOptions | undefined {
+ return this.__registry__.getOptionsForType(type);
+ }
}
/**
diff --git a/packages/@ember/engine/instance.ts b/packages/@ember/engine/instance.ts
index 097b5c6ca6e..d760e62f791 100644
--- a/packages/@ember/engine/instance.ts
+++ b/packages/@ember/engine/instance.ts
@@ -3,13 +3,20 @@
*/
import EmberObject from '@ember/object';
+import { schedule, join } from '@ember/runloop';
import { RSVP } from '@ember/-internals/runtime';
import { assert } from '@ember/debug';
+import type { Container } from '@ember/-internals/container';
import { Registry, privatize as P } from '@ember/-internals/container';
import { guidFor } from '@ember/-internals/utils';
import { ENGINE_PARENT, getEngineParent, setEngineParent } from './parent';
-import { ContainerProxyMixin, RegistryProxyMixin } from '@ember/-internals/runtime';
-import type { InternalOwner } from '@ember/-internals/owner';
+import type {
+ ContainerProxy,
+ FactoryClass,
+ InternalFactory,
+ InternalOwner,
+ RegisterOptions,
+} from '@ember/-internals/owner';
import type Owner from '@ember/-internals/owner';
import { type FullName, isFactory } from '@ember/-internals/owner';
import type Engine from '@ember/engine';
@@ -40,10 +47,9 @@ export interface EngineInstanceOptions {
@public
@class EngineInstance
@extends EmberObject
- @uses RegistryProxyMixin
- @uses ContainerProxyMixin
*/
+// TODO: Update this comment
// Note on types: since `EngineInstance` uses `RegistryProxyMixin` and
// `ContainerProxyMixin`, which respectively implement the same `RegistryMixin`
// and `ContainerMixin` types used to define `InternalOwner`, this is the same
@@ -51,8 +57,9 @@ export interface EngineInstanceOptions {
// clauses for `InternalOwner` and `Owner` is to keep us honest: if this stops
// type checking, we have broken part of our public API contract. Medium-term,
// the goal here is to `EngineInstance` simple be `Owner`.
-interface EngineInstance extends RegistryProxyMixin, ContainerProxyMixin, InternalOwner, Owner {}
-class EngineInstance extends EmberObject.extend(RegistryProxyMixin, ContainerProxyMixin) {
+// eslint-disable-next-line @typescript-eslint/no-empty-object-type
+interface EngineInstance extends Owner {}
+class EngineInstance extends EmberObject implements ContainerProxy, InternalOwner, Owner {
/**
@private
@method setupRegistry
@@ -169,23 +176,6 @@ class EngineInstance extends EmberObject.extend(RegistryProxyMixin, ContainerPro
(this.constructor as typeof EngineInstance).setupRegistry(this.__registry__, options);
}
- /**
- Unregister a factory.
-
- Overrides `RegistryProxy#unregister` in order to clear any cached instances
- of the unregistered factory.
-
- @public
- @method unregister
- @param {String} fullName
- */
- unregister(fullName: FullName) {
- this.__container__.reset(fullName);
-
- // We overwrote this method from RegistryProxyMixin.
- this.__registry__.unregister(fullName);
- }
-
/**
Build a new `EngineInstance` that's a child of this instance.
@@ -256,6 +246,133 @@ class EngineInstance extends EmberObject.extend(RegistryProxyMixin, ContainerPro
this.register(key, singleton, { instantiate: false });
});
}
+
+ // Container Proxy
+
+ /**
+ The container stores state.
+
+ @private
+ @property {Ember.Container} __container__
+ */
+ declare __container__: Container;
+
+ ownerInjection() {
+ return this.__container__.ownerInjection();
+ }
+
+ destroy() {
+ let container = this.__container__;
+
+ if (container) {
+ join(() => {
+ container.destroy();
+ schedule('destroy', container, 'finalizeDestroy');
+ });
+ }
+
+ return super.destroy();
+ }
+
+ // Registry Proxy
+ // Duplicated with Engine
+
+ declare __registry__: Registry;
+
+ resolveRegistration(fullName: string) {
+ assert('fullName must be a proper full name', this.__registry__.isValidFullName(fullName));
+ return this.__registry__.resolve(fullName);
+ }
+
+ /**
+ Registers a factory for later injection.
+
+ @private
+ @method register
+ @param {String} fullName
+ @param {Function} factory
+ @param {Object} options
+ */
+ register(
+ fullName: FullName,
+ factory: InternalFactory,
+ options: RegisterOptions & { instantiate: true }
+ ): void;
+ register(fullName: FullName, factory: object, options?: RegisterOptions): void;
+ register(...args: Parameters) {
+ return this.__registry__.register(...args);
+ }
+
+ /**
+ Unregister a factory.
+
+ Also clears any cached instances of the unregistered factory.
+
+ @public
+ @method unregister
+ @param {String} fullName
+ */
+ unregister(fullName: FullName) {
+ this.__container__.reset(fullName);
+
+ // We overwrote this method from RegistryProxyMixin.
+ this.__registry__.unregister(fullName);
+ }
+
+ /**
+ Given a fullName check if the registry is aware of its factory
+ or singleton instance.
+
+ @private
+ @method hasRegistration
+ @param {String} fullName
+ @param {Object} [options]
+ @param {String} [options.source] the fullname of the request source (used for local lookups)
+ @return {Boolean}
+ */
+ hasRegistration(fullName: FullName): boolean {
+ return this.__registry__.has(fullName);
+ }
+
+ registeredOption(
+ fullName: FullName,
+ optionName: K
+ ): RegisterOptions[K] | undefined {
+ return this.__registry__.getOption(fullName, optionName);
+ }
+
+ registerOptions(fullName: FullName, options: RegisterOptions) {
+ return this.__registry__.options(fullName, options);
+ }
+
+ registeredOptions(fullName: FullName): RegisterOptions | undefined {
+ return this.__registry__.getOptions(fullName);
+ }
+
+ /**
+ Allow registering options for all factories of a type.
+ */
+ registerOptionsForType(type: string, options: RegisterOptions) {
+ return this.__registry__.optionsForType(type, options);
+ }
+
+ registeredOptionsForType(type: string): RegisterOptions | undefined {
+ return this.__registry__.getOptionsForType(type);
+ }
}
+// MEGAHAX: This is really nasty, but if we don't define the functions this way, we need to provide types.
+// If we provide types, for reasons I don't understand, they somehow break the interface.
+// Adding the methods this way allows us to keep the types defined by the interface.
+
+// @ts-expect-error This is a huge hack to avoid type issues.
+EngineInstance.prototype.lookup = function lookup(fullName: FullName, options?: RegisterOptions) {
+ return this.__container__.lookup(fullName, options);
+};
+
+// @ts-expect-error This is a huge hack to avoid type issues
+EngineInstance.prototype.factoryFor = function factoryFor(fullName: FullName) {
+ return this.__container__.factoryFor(fullName);
+};
+
export default EngineInstance;
diff --git a/packages/@ember/engine/type-tests/index.test.ts b/packages/@ember/engine/type-tests/index.test.ts
index 4e63038279a..62cf5f68e44 100644
--- a/packages/@ember/engine/type-tests/index.test.ts
+++ b/packages/@ember/engine/type-tests/index.test.ts
@@ -1,11 +1,12 @@
import type { ResolverClass } from '@ember/-internals/container/lib/registry';
-import type { default as Owner, RegisterOptions, Factory } from '@ember/owner';
+import type { default as Owner, RegisterOptions } from '@ember/owner';
import type Namespace from '@ember/application/namespace';
import type { Initializer } from '@ember/engine';
import Engine from '@ember/engine';
import type EngineInstance from '@ember/engine/instance';
import EmberObject from '@ember/object';
import { expectTypeOf } from 'expect-type';
+import { InternalFactory } from '@ember/-internals/owner';
declare let owner: Owner;
@@ -37,7 +38,8 @@ expectTypeOf(engine.Resolver).toEqualTypeOf();
// RegistryProxy
expectTypeOf(engine.resolveRegistration('foo:bar')).toEqualTypeOf<
- Factory