ember-source 5.11.0-beta.2 → 5.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/build-metadata.json +3 -3
  2. package/dist/ember-template-compiler.js +48 -42
  3. package/dist/ember-testing.js +1 -1
  4. package/dist/ember.debug.js +23102 -23100
  5. package/dist/ember.prod.js +31562 -31563
  6. package/dist/packages/@ember/-internals/container/index.js +1 -1
  7. package/dist/packages/@ember/-internals/deprecations/index.js +2 -1
  8. package/dist/packages/@ember/-internals/glimmer/index.js +2 -1
  9. package/dist/packages/@ember/-internals/meta/lib/meta.js +4 -3
  10. package/dist/packages/@ember/-internals/metal/index.js +9 -8
  11. package/dist/packages/@ember/-internals/routing/index.js +6 -5
  12. package/dist/packages/@ember/-internals/runtime/lib/ext/rsvp.js +3 -2
  13. package/dist/packages/@ember/-internals/runtime/lib/mixins/-proxy.js +6 -5
  14. package/dist/packages/@ember/-internals/runtime/lib/mixins/action_handler.js +4 -3
  15. package/dist/packages/@ember/-internals/runtime/lib/mixins/container_proxy.js +1 -1
  16. package/dist/packages/@ember/-internals/runtime/lib/mixins/registry_proxy.js +2 -1
  17. package/dist/packages/@ember/-internals/runtime/lib/mixins/target_action_support.js +4 -3
  18. package/dist/packages/@ember/-internals/string/index.js +1 -1
  19. package/dist/packages/@ember/-internals/utils/index.js +4 -4
  20. package/dist/packages/@ember/-internals/views/index.js +1 -1
  21. package/dist/packages/@ember/-internals/views/lib/compat/fallback-view-registry.js +1 -1
  22. package/dist/packages/@ember/-internals/views/lib/component_lookup.js +1 -1
  23. package/dist/packages/@ember/-internals/views/lib/mixins/action_support.js +5 -3
  24. package/dist/packages/@ember/-internals/views/lib/mixins/child_views_support.js +3 -3
  25. package/dist/packages/@ember/-internals/views/lib/mixins/class_names_support.js +4 -3
  26. package/dist/packages/@ember/-internals/views/lib/mixins/view_support.js +4 -3
  27. package/dist/packages/@ember/-internals/views/lib/system/event_dispatcher.js +6 -12
  28. package/dist/packages/@ember/-internals/views/lib/system/utils.js +3 -2
  29. package/dist/packages/@ember/-internals/views/lib/views/core_view.js +76 -8
  30. package/dist/packages/@ember/-internals/views/lib/views/states.js +4 -3
  31. package/dist/packages/@ember/application/index.js +16 -7
  32. package/dist/packages/@ember/application/instance.js +13 -9
  33. package/dist/packages/@ember/application/namespace.js +7 -6
  34. package/dist/packages/@ember/array/index.js +617 -11
  35. package/dist/packages/@ember/array/make.js +1 -0
  36. package/dist/packages/@ember/array/mutable.js +1 -1
  37. package/dist/packages/@ember/array/proxy.js +8 -5
  38. package/dist/packages/@ember/component/helper.js +4 -4
  39. package/dist/packages/@ember/component/index.js +4 -4
  40. package/dist/packages/@ember/controller/index.js +6 -6
  41. package/dist/packages/@ember/debug/container-debug-adapter.js +5 -4
  42. package/dist/packages/@ember/debug/data-adapter.js +7 -4
  43. package/dist/packages/@ember/debug/index.js +213 -4
  44. package/dist/packages/@ember/debug/lib/assert.js +47 -0
  45. package/dist/packages/@ember/debug/lib/deprecate.js +194 -4
  46. package/dist/packages/@ember/debug/lib/inspect.js +120 -2
  47. package/dist/packages/@ember/debug/lib/warn.js +94 -3
  48. package/dist/packages/@ember/engine/index.js +440 -17
  49. package/dist/packages/@ember/engine/instance.js +175 -11
  50. package/dist/packages/@ember/engine/parent.js +1 -0
  51. package/dist/packages/@ember/helper/index.js +4 -4
  52. package/dist/packages/@ember/instrumentation/index.js +2 -1
  53. package/dist/packages/@ember/modifier/index.js +13 -5
  54. package/dist/packages/@ember/modifier/on.js +15 -0
  55. package/dist/packages/@ember/object/-internals.js +6 -5
  56. package/dist/packages/@ember/object/compat.js +4 -3
  57. package/dist/packages/@ember/object/computed.js +4 -4
  58. package/dist/packages/@ember/object/core.js +861 -14
  59. package/dist/packages/@ember/object/evented.js +4 -4
  60. package/dist/packages/@ember/object/events.js +3 -3
  61. package/dist/packages/@ember/object/index.js +260 -9
  62. package/dist/packages/@ember/object/internals.js +1 -1
  63. package/dist/packages/@ember/object/lib/computed/computed_macros.js +8 -6
  64. package/dist/packages/@ember/object/lib/computed/reduce_computed_macros.js +8 -4
  65. package/dist/packages/@ember/object/mixin.js +6 -5
  66. package/dist/packages/@ember/object/observable.js +103 -9
  67. package/dist/packages/@ember/object/observers.js +3 -3
  68. package/dist/packages/@ember/object/promise-proxy-mixin.js +5 -5
  69. package/dist/packages/@ember/renderer/index.js +4 -4
  70. package/dist/packages/@ember/routing/-internals.js +3 -1
  71. package/dist/packages/@ember/routing/hash-location.js +2 -2
  72. package/dist/packages/@ember/routing/history-location.js +3 -2
  73. package/dist/packages/@ember/routing/index.js +4 -4
  74. package/dist/packages/@ember/routing/lib/dsl.js +2 -1
  75. package/dist/packages/@ember/routing/lib/generate_controller.js +4 -3
  76. package/dist/packages/@ember/routing/lib/router_state.js +26 -1
  77. package/dist/packages/@ember/routing/lib/routing-service.js +107 -9
  78. package/dist/packages/@ember/routing/lib/utils.js +238 -7
  79. package/dist/packages/@ember/routing/none-location.js +3 -2
  80. package/dist/packages/@ember/routing/route.js +1618 -22
  81. package/dist/packages/@ember/routing/router-service.js +638 -12
  82. package/dist/packages/@ember/routing/router.js +1449 -14
  83. package/dist/packages/@ember/runloop/index.js +760 -6
  84. package/dist/packages/@ember/service/index.js +3 -3
  85. package/dist/packages/@ember/template/index.js +4 -4
  86. package/dist/packages/@ember/utils/index.js +2 -1
  87. package/dist/packages/@ember/utils/lib/compare.js +159 -4
  88. package/dist/packages/@ember/utils/lib/is_empty.js +4 -4
  89. package/dist/packages/@ember/utils/lib/type-of.js +110 -1
  90. package/dist/packages/@glimmer/tracking/index.js +3 -3
  91. package/dist/packages/@glimmer/tracking/primitives/cache.js +3 -3
  92. package/dist/packages/ember/barrel.js +28 -13
  93. package/dist/packages/ember/version.js +1 -1
  94. package/dist/packages/ember-testing/lib/adapters/adapter.js +1 -1
  95. package/dist/packages/ember-testing/lib/adapters/qunit.js +2 -1
  96. package/dist/packages/ember-testing/lib/ext/application.js +2 -1
  97. package/dist/packages/ember-testing/lib/ext/rsvp.js +1 -1
  98. package/dist/packages/ember-testing/lib/helpers/and_then.js +2 -1
  99. package/dist/packages/ember-testing/lib/helpers/current_path.js +8 -6
  100. package/dist/packages/ember-testing/lib/helpers/current_route_name.js +8 -6
  101. package/dist/packages/ember-testing/lib/helpers/current_url.js +6 -5
  102. package/dist/packages/ember-testing/lib/helpers/pause_test.js +2 -1
  103. package/dist/packages/ember-testing/lib/helpers/visit.js +4 -3
  104. package/dist/packages/ember-testing/lib/helpers/wait.js +4 -3
  105. package/dist/packages/ember-testing/lib/initializers.js +15 -8
  106. package/dist/packages/ember-testing/lib/setup_for_testing.js +1 -1
  107. package/dist/packages/ember-testing/lib/test/run.js +1 -1
  108. package/dist/packages/router_js/index.js +2 -1
  109. package/dist/packages/shared-chunks/{alias-By_2yu5c.js → alias-Dri0koi2.js} +5 -3
  110. package/dist/packages/shared-chunks/array-3xbmc_4J.js +119 -0
  111. package/dist/packages/shared-chunks/{cache-gDE3bkXq.js → cache-BESCGvbE.js} +667 -1529
  112. package/dist/packages/shared-chunks/{core_view-Cxne2_wu.js → chunk-3SQBS3Y5-Cj4eryg1.js} +1 -88
  113. package/dist/packages/shared-chunks/{index-BXPoca1S.js → index-Llq6dmgX.js} +40 -4660
  114. package/dist/packages/shared-chunks/{is_proxy-Dmis-70B.js → is_proxy-DjvCKvd5.js} +1 -1
  115. package/dist/packages/shared-chunks/{mandatory-setter-1UQhiJOb.js → mandatory-setter-BiXq-dpN.js} +2 -1
  116. package/dist/packages/shared-chunks/{name-z9D9Yibn.js → name-Dx2bGFVv.js} +1 -1
  117. package/dist/packages/shared-chunks/{namespace_search-CBgHTkDh.js → namespace_search-btMaPM-_.js} +2 -2
  118. package/dist/packages/shared-chunks/{property_set-CW4q-uo4.js → property_set-BapAkp3X.js} +5 -4
  119. package/dist/packages/shared-chunks/{registry-DzfcDwii.js → registry-B8WARvkP.js} +3 -2
  120. package/dist/packages/shared-chunks/{router-B-Q1aYBn.js → router-DrLZsJeE.js} +2 -482
  121. package/dist/packages/shared-chunks/{set_properties-DvalyQdu.js → set_properties-BScfxzvI.js} +2 -2
  122. package/dist/packages/shared-chunks/setup-registry-du4pSGZi.js +48 -0
  123. package/dist/packages/shared-chunks/{to-string-D8i3mjEU.js → to-string-B1BmwUkt.js} +1 -1
  124. package/dist/packages/shared-chunks/unrecognized-url-error-zpz-JEoG.js +484 -0
  125. package/docs/data.json +152 -142
  126. package/package.json +4 -7
  127. package/types/stable/@ember/-internals/metal/lib/array.d.ts +1 -2
  128. package/types/stable/@ember/-internals/metal/lib/object-at.d.ts +4 -0
  129. package/types/stable/@ember/-internals/metal/lib/observer.d.ts +2 -1
  130. package/types/stable/@ember/array/index.d.ts +1 -1
  131. package/types/stable/@ember/array/make.d.ts +3 -0
  132. package/types/stable/@ember/debug/index.d.ts +3 -7
  133. package/types/stable/@ember/debug/lib/assert.d.ts +8 -0
  134. package/types/stable/@ember/engine/index.d.ts +1 -1
  135. package/types/stable/@ember/engine/instance.d.ts +2 -2
  136. package/types/stable/@ember/engine/parent.d.ts +3 -0
  137. package/types/stable/@ember/modifier/index.d.ts +1 -3
  138. package/types/stable/@ember/modifier/on.d.ts +5 -0
  139. package/types/stable/@ember/routing/lib/routing-service.d.ts +1 -1
  140. package/types/stable/@ember/routing/route.d.ts +2 -3
  141. package/types/stable/@ember/routing/router-service.d.ts +1 -1
  142. package/types/stable/@ember/routing/router.d.ts +4 -4
  143. package/types/stable/ember/barrel.d.ts +1 -1
  144. package/types/stable/ember/index.d.ts +1 -1
  145. package/types/stable/index.d.ts +5 -0
  146. package/dist/packages/shared-chunks/index-DTxy4Zgx.js +0 -641
  147. package/dist/packages/shared-chunks/index-PYiGj1jp.js +0 -2071
@@ -1,35 +1,1631 @@
1
- import '../../shared-chunks/registry-DzfcDwii.js';
2
- import '../../shared-chunks/cache-gDE3bkXq.js';
1
+ import { p as privatize } from '../../shared-chunks/registry-B8WARvkP.js';
2
+ import { g as get, I as flushAsyncObservers, b as descriptorForProperty, d as defineProperty, v as addObserver, c as computed } from '../../shared-chunks/cache-BESCGvbE.js';
3
3
  import '../-internals/meta/lib/meta.js';
4
- import '../../shared-chunks/index-DTxy4Zgx.js';
4
+ import { info } from '../debug/index.js';
5
5
  import '../../@glimmer/validator/index.js';
6
- import '../../shared-chunks/mandatory-setter-1UQhiJOb.js';
7
- import '@embroider/macros';
8
- import '../../shared-chunks/is_proxy-Dmis-70B.js';
6
+ import { l as lookupDescriptor } from '../../shared-chunks/mandatory-setter-BiXq-dpN.js';
7
+ import { isDevelopingApp } from '@embroider/macros';
8
+ import { i as isProxy } from '../../shared-chunks/is_proxy-DjvCKvd5.js';
9
9
  import '../../@glimmer/destroyable/index.js';
10
10
  import '../../@glimmer/manager/index.js';
11
- import '../../shared-chunks/property_set-CW4q-uo4.js';
12
- import '../../shared-chunks/set_properties-DvalyQdu.js';
13
- import '../../shared-chunks/env-BJLX2Arx.js';
14
- import '../-internals/owner/index.js';
15
- export { k as default, D as defaultSerialize, B as getFullQueryParams, A as getRenderState, F as hasDefaultSerialize } from '../../shared-chunks/index-BXPoca1S.js';
16
- import './lib/generate_controller.js';
17
- import './lib/cache.js';
18
- import '../../shared-chunks/index-PYiGj1jp.js';
19
- import '../object/evented.js';
11
+ import { s as set } from '../../shared-chunks/property_set-BapAkp3X.js';
12
+ import { g as getProperties, s as setProperties } from '../../shared-chunks/set_properties-BScfxzvI.js';
13
+ import { E as ENV } from '../../shared-chunks/env-BJLX2Arx.js';
14
+ import { getOwner } from '../-internals/owner/index.js';
15
+ import EmberObject from '../object/index.js';
16
+ import Evented from '../object/evented.js';
17
+ import { A } from '../array/index.js';
20
18
  import '../-internals/runtime/lib/mixins/registry_proxy.js';
21
19
  import '../-internals/runtime/lib/mixins/container_proxy.js';
22
20
  import '../-internals/runtime/lib/mixins/comparable.js';
23
- import '../-internals/runtime/lib/mixins/action_handler.js';
21
+ import ActionHandler from '../-internals/runtime/lib/mixins/action_handler.js';
24
22
  import '../-internals/runtime/lib/mixins/-proxy.js';
25
23
  import '../enumerable/mutable.js';
26
24
  import '../-internals/runtime/lib/mixins/target_action_support.js';
27
25
  import '../-internals/runtime/lib/ext/rsvp.js';
28
- import '../controller/index.js';
29
- import '../-internals/deprecations/index.js';
30
- import '../object/compat.js';
26
+ import typeOf from '../utils/lib/type-of.js';
27
+ import Controller from '../controller/index.js';
28
+ import { deprecateUntil, DEPRECATIONS } from '../-internals/deprecations/index.js';
29
+ import EngineInstance from '../engine/instance.js';
30
+ import { dependentKeyCompat } from '../object/compat.js';
31
+ import { once } from '../runloop/index.js';
31
32
  import '../../route-recognizer/index.js';
32
33
  import '../../shared-chunks/rsvp-DaQAFb0W.js';
33
- import '../../shared-chunks/router-B-Q1aYBn.js';
34
- import '../../shared-chunks/core_view-Cxne2_wu.js';
35
- import '../debug/lib/testing.js';
34
+ import { S as STATE_SYMBOL, P as PARAMS_SYMBOL } from '../../shared-chunks/unrecognized-url-error-zpz-JEoG.js';
35
+ import generateController from './lib/generate_controller.js';
36
+ import { prefixRouteNameArg, stashParamNames, calculateCacheKey, normalizeControllerQueryParams } from './lib/utils.js';
37
+ import { a as decorateMethodV2 } from '../../shared-chunks/chunk-3SQBS3Y5-Cj4eryg1.js';
38
+ import { assert } from '../debug/lib/assert.js';
39
+ import { isTesting } from '../debug/lib/testing.js';
40
+
41
+ function isStoreLike(store) {
42
+ return typeof store === 'object' && store !== null && typeof store.find === 'function';
43
+ }
44
+ const RENDER = Symbol('render');
45
+ const RENDER_STATE = Symbol('render-state');
46
+
47
+ /**
48
+ @module @ember/routing/route
49
+ */
50
+
51
+ /**
52
+ The `Route` class is used to define individual routes. Refer to
53
+ the [routing guide](https://guides.emberjs.com/release/routing/) for documentation.
54
+
55
+ @class Route
56
+ @extends EmberObject
57
+ @uses ActionHandler
58
+ @uses Evented
59
+ @since 1.0.0
60
+ @public
61
+ */
62
+
63
+ class Route extends EmberObject.extend(ActionHandler, Evented) {
64
+ static isRouteFactory = true;
65
+
66
+ // These properties will end up appearing in the public interface because we
67
+ // `implements IRoute` from `router.js`, which has them as part of *its*
68
+ // public contract. We mark them as `@internal` so they at least signal to
69
+ // people subclassing `Route` that they should not use them.
70
+ /** @internal */
71
+ context = {};
72
+ /** @internal */
73
+
74
+ /** @internal */
75
+ _bucketCache;
76
+ /** @internal */
77
+ _internalName;
78
+ _names;
79
+ _router;
80
+ constructor(owner) {
81
+ super(owner);
82
+ if (owner) {
83
+ let router = owner.lookup('router:main');
84
+ let bucketCache = owner.lookup(privatize`-bucket-cache:main`);
85
+ this._router = router;
86
+ this._bucketCache = bucketCache;
87
+ this._topLevelViewTemplate = owner.lookup('template:-outlet');
88
+ this._environment = owner.lookup('-environment:main');
89
+ }
90
+ }
91
+
92
+ /**
93
+ A hook you can implement to convert the route's model into parameters
94
+ for the URL.
95
+ ```app/router.js
96
+ // ...
97
+ Router.map(function() {
98
+ this.route('post', { path: '/posts/:post_id' });
99
+ });
100
+ ```
101
+ ```app/routes/post.js
102
+ import Route from '@ember/routing/route';
103
+ export default class PostRoute extends Route {
104
+ model({ post_id }) {
105
+ // the server returns `{ id: 12 }`
106
+ return fetch(`/posts/${post_id}`;
107
+ }
108
+ serialize(model) {
109
+ // this will make the URL `/posts/12`
110
+ return { post_id: model.id };
111
+ }
112
+ }
113
+ ```
114
+ The default `serialize` method will insert the model's `id` into the
115
+ route's dynamic segment (in this case, `:post_id`) if the segment contains '_id'.
116
+ If the route has multiple dynamic segments or does not contain '_id', `serialize`
117
+ will return `getProperties(model, params)`
118
+ This method is called when `transitionTo` is called with a context
119
+ in order to populate the URL.
120
+ @method serialize
121
+ @param {Object} model the routes model
122
+ @param {Array} params an Array of parameter names for the current
123
+ route (in the example, `['post_id']`.
124
+ @return {Object} the serialized parameters
125
+ @since 1.0.0
126
+ @public
127
+ */
128
+ serialize(model, params) {
129
+ if (params.length < 1 || !model) {
130
+ return;
131
+ }
132
+ let object = {};
133
+ if (params.length === 1) {
134
+ let [name] = params;
135
+ (isDevelopingApp() && !(name) && assert('has name', name));
136
+ if (typeof model === 'object' && name in model) {
137
+ object[name] = get(model, name);
138
+ } else if (/_id$/.test(name)) {
139
+ object[name] = get(model, 'id');
140
+ } else if (isProxy(model)) {
141
+ object[name] = get(model, name);
142
+ }
143
+ } else {
144
+ object = getProperties(model, params);
145
+ }
146
+ return object;
147
+ }
148
+
149
+ /**
150
+ Configuration hash for this route's queryParams. The possible
151
+ configuration options and their defaults are as follows
152
+ (assuming a query param whose controller property is `page`):
153
+ ```javascript
154
+ queryParams = {
155
+ page: {
156
+ // By default, controller query param properties don't
157
+ // cause a full transition when they are changed, but
158
+ // rather only cause the URL to update. Setting
159
+ // `refreshModel` to true will cause an "in-place"
160
+ // transition to occur, whereby the model hooks for
161
+ // this route (and any child routes) will re-fire, allowing
162
+ // you to reload models (e.g., from the server) using the
163
+ // updated query param values.
164
+ refreshModel: false,
165
+ // By default, changes to controller query param properties
166
+ // cause the URL to update via `pushState`, which means an
167
+ // item will be added to the browser's history, allowing
168
+ // you to use the back button to restore the app to the
169
+ // previous state before the query param property was changed.
170
+ // Setting `replace` to true will use `replaceState` (or its
171
+ // hash location equivalent), which causes no browser history
172
+ // item to be added. This options name and default value are
173
+ // the same as the `link-to` helper's `replace` option.
174
+ replace: false,
175
+ // By default, the query param URL key is the same name as
176
+ // the controller property name. Use `as` to specify a
177
+ // different URL key.
178
+ as: 'page'
179
+ }
180
+ };
181
+ ```
182
+ @property queryParams
183
+ @for Route
184
+ @type Object
185
+ @since 1.6.0
186
+ @public
187
+ */
188
+ // Set in reopen so it can be overriden with extend
189
+
190
+ /**
191
+ The name of the template to use by default when rendering this route's
192
+ template.
193
+ ```app/routes/posts/list.js
194
+ import Route from '@ember/routing/route';
195
+ export default class PostsListRoute extends Route {
196
+ templateName = 'posts/list';
197
+ }
198
+ ```
199
+ ```app/routes/posts/index.js
200
+ import PostsListRoute from '../posts/list';
201
+ export default class PostsIndexRoute extends PostsListRoute {};
202
+ ```
203
+ ```app/routes/posts/archived.js
204
+ import PostsListRoute from '../posts/list';
205
+ export default class PostsArchivedRoute extends PostsListRoute {};
206
+ ```
207
+ @property templateName
208
+ @type String
209
+ @default null
210
+ @since 1.4.0
211
+ @public
212
+ */
213
+ // Set in reopen so it can be overriden with extend
214
+
215
+ /**
216
+ The name of the controller to associate with this route.
217
+ By default, Ember will lookup a route's controller that matches the name
218
+ of the route (i.e. `posts.new`). However,
219
+ if you would like to define a specific controller to use, you can do so
220
+ using this property.
221
+ This is useful in many ways, as the controller specified will be:
222
+ * passed to the `setupController` method.
223
+ * used as the controller for the template being rendered by the route.
224
+ * returned from a call to `controllerFor` for the route.
225
+ @property controllerName
226
+ @type String
227
+ @default null
228
+ @since 1.4.0
229
+ @public
230
+ */
231
+ // Set in reopen so it can be overriden with extend
232
+
233
+ /**
234
+ The controller associated with this route.
235
+ Example
236
+ ```app/routes/form.js
237
+ import Route from '@ember/routing/route';
238
+ import { action } from '@ember/object';
239
+ export default class FormRoute extends Route {
240
+ @action
241
+ willTransition(transition) {
242
+ if (this.controller.get('userHasEnteredData') &&
243
+ !confirm('Are you sure you want to abandon progress?')) {
244
+ transition.abort();
245
+ } else {
246
+ // Bubble the `willTransition` action so that
247
+ // parent routes can decide whether or not to abort.
248
+ return true;
249
+ }
250
+ }
251
+ }
252
+ ```
253
+ @property controller
254
+ @type Controller
255
+ @since 1.6.0
256
+ @public
257
+ */
258
+
259
+ /**
260
+ The name of the route, dot-delimited.
261
+ For example, a route found at `app/routes/posts/post.js` will have
262
+ a `routeName` of `posts.post`.
263
+ @property routeName
264
+ @for Route
265
+ @type String
266
+ @since 1.0.0
267
+ @public
268
+ */
269
+
270
+ /**
271
+ The name of the route, dot-delimited, including the engine prefix
272
+ if applicable.
273
+ For example, a route found at `addon/routes/posts/post.js` within an
274
+ engine named `admin` will have a `fullRouteName` of `admin.posts.post`.
275
+ @property fullRouteName
276
+ @for Route
277
+ @type String
278
+ @since 2.10.0
279
+ @public
280
+ */
281
+
282
+ /**
283
+ Sets the name for this route, including a fully resolved name for routes
284
+ inside engines.
285
+ @private
286
+ @method _setRouteName
287
+ @param {String} name
288
+ */
289
+ _setRouteName(name) {
290
+ this.routeName = name;
291
+ let owner = getOwner(this);
292
+ (isDevelopingApp() && !(owner instanceof EngineInstance) && assert('Expected route to have EngineInstance as owner', owner instanceof EngineInstance));
293
+ this.fullRouteName = getEngineRouteName(owner, name);
294
+ }
295
+
296
+ /**
297
+ @private
298
+ @method _stashNames
299
+ */
300
+ _stashNames(routeInfo, dynamicParent) {
301
+ if (this._names) {
302
+ return;
303
+ }
304
+ let names = this._names = routeInfo['_names'];
305
+ if (!names.length) {
306
+ routeInfo = dynamicParent;
307
+ names = routeInfo && routeInfo['_names'] || [];
308
+ }
309
+
310
+ // SAFETY: Since `_qp` is protected we can't infer the type
311
+ let qps = get(this, '_qp').qps;
312
+ let namePaths = new Array(names.length);
313
+ for (let a = 0; a < names.length; ++a) {
314
+ namePaths[a] = `${routeInfo.name}.${names[a]}`;
315
+ }
316
+ for (let qp of qps) {
317
+ if (qp.scope === 'model') {
318
+ qp.parts = namePaths;
319
+ }
320
+ }
321
+ }
322
+
323
+ /**
324
+ @private
325
+ @property _activeQPChanged
326
+ */
327
+ _activeQPChanged(qp, value) {
328
+ this._router._activeQPChanged(qp.scopedPropertyName, value);
329
+ }
330
+
331
+ /**
332
+ @private
333
+ @method _updatingQPChanged
334
+ */
335
+ _updatingQPChanged(qp) {
336
+ this._router._updatingQPChanged(qp.urlKey);
337
+ }
338
+
339
+ /**
340
+ Returns a hash containing the parameters of an ancestor route.
341
+ You may notice that `this.paramsFor` sometimes works when referring to a
342
+ child route, but this behavior should not be relied upon as only ancestor
343
+ routes are certain to be loaded in time.
344
+ Example
345
+ ```app/router.js
346
+ // ...
347
+ Router.map(function() {
348
+ this.route('member', { path: ':name' }, function() {
349
+ this.route('interest', { path: ':interest' });
350
+ });
351
+ });
352
+ ```
353
+ ```app/routes/member.js
354
+ import Route from '@ember/routing/route';
355
+ export default class MemberRoute extends Route {
356
+ queryParams = {
357
+ memberQp: { refreshModel: true }
358
+ }
359
+ }
360
+ ```
361
+ ```app/routes/member/interest.js
362
+ import Route from '@ember/routing/route';
363
+ export default class MemberInterestRoute extends Route {
364
+ queryParams = {
365
+ interestQp: { refreshModel: true }
366
+ }
367
+ model() {
368
+ return this.paramsFor('member');
369
+ }
370
+ }
371
+ ```
372
+ If we visit `/turing/maths?memberQp=member&interestQp=interest` the model for
373
+ the `member.interest` route is a hash with:
374
+ * `name`: `turing`
375
+ * `memberQp`: `member`
376
+ @method paramsFor
377
+ @param {String} name
378
+ @return {Object} hash containing the parameters of the route `name`
379
+ @since 1.4.0
380
+ @public
381
+ */
382
+ paramsFor(name) {
383
+ let owner = getOwner(this);
384
+ (isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
385
+ let route = owner.lookup(`route:${name}`);
386
+ if (route === undefined) {
387
+ return {};
388
+ }
389
+ let transition = this._router._routerMicrolib.activeTransition;
390
+ let state = transition ? transition[STATE_SYMBOL] : this._router._routerMicrolib.state;
391
+ let fullName = route.fullRouteName;
392
+ let params = {
393
+ ...state.params[fullName]
394
+ };
395
+ let queryParams = getQueryParamsFor(route, state);
396
+ return Object.entries(queryParams).reduce((params, [key, value]) => {
397
+ (isDevelopingApp() && !(!params[key]) && assert(`The route '${this.routeName}' has both a dynamic segment and query param with name '${key}'. Please rename one to avoid collisions.`, !params[key]));
398
+ params[key] = value;
399
+ return params;
400
+ }, params);
401
+ }
402
+
403
+ /**
404
+ Serializes the query parameter key
405
+ @method serializeQueryParamKey
406
+ @param {String} controllerPropertyName
407
+ @private
408
+ */
409
+ serializeQueryParamKey(controllerPropertyName) {
410
+ return controllerPropertyName;
411
+ }
412
+
413
+ /**
414
+ Serializes value of the query parameter based on defaultValueType
415
+ @method serializeQueryParam
416
+ @param {Object} value
417
+ @param {String} urlKey
418
+ @param {String} defaultValueType
419
+ @private
420
+ */
421
+ serializeQueryParam(value, _urlKey, defaultValueType) {
422
+ // urlKey isn't used here, but anyone overriding
423
+ // can use it to provide serialization specific
424
+ // to a certain query param.
425
+ return this._router._serializeQueryParam(value, defaultValueType);
426
+ }
427
+
428
+ /**
429
+ Deserializes value of the query parameter based on defaultValueType
430
+ @method deserializeQueryParam
431
+ @param {Object} value
432
+ @param {String} urlKey
433
+ @param {String} defaultValueType
434
+ @private
435
+ */
436
+ deserializeQueryParam(value, _urlKey, defaultValueType) {
437
+ // urlKey isn't used here, but anyone overriding
438
+ // can use it to provide deserialization specific
439
+ // to a certain query param.
440
+ return this._router._deserializeQueryParam(value, defaultValueType);
441
+ }
442
+
443
+ /**
444
+ @private
445
+ @property _optionsForQueryParam
446
+ */
447
+ _optionsForQueryParam(qp) {
448
+ const queryParams = get(this, 'queryParams');
449
+ return get(queryParams, qp.urlKey) || get(queryParams, qp.prop) || queryParams[qp.urlKey] || queryParams[qp.prop] || {};
450
+ }
451
+
452
+ /**
453
+ A hook you can use to reset controller values either when the model
454
+ changes or the route is exiting.
455
+ ```app/routes/articles.js
456
+ import Route from '@ember/routing/route';
457
+ export default class ArticlesRoute extends Route {
458
+ resetController(controller, isExiting, transition) {
459
+ if (isExiting && transition.targetName !== 'error') {
460
+ controller.set('page', 1);
461
+ }
462
+ }
463
+ }
464
+ ```
465
+ @method resetController
466
+ @param {Controller} controller instance
467
+ @param {Boolean} isExiting
468
+ @param {Object} transition
469
+ @since 1.7.0
470
+ @public
471
+ */
472
+ resetController(_controller, _isExiting, _transition) {
473
+ // We document that subclasses do not have to return *anything* and in fact
474
+ // do not even have to call super, so whiel we *do* return `this`, we need
475
+ // to be explicit in the types that our return type is *effectively* `void`.
476
+ return this;
477
+ }
478
+
479
+ /**
480
+ @private
481
+ @method exit
482
+ */
483
+ exit(transition) {
484
+ this.deactivate(transition);
485
+ this.trigger('deactivate', transition);
486
+ this.teardownViews();
487
+ }
488
+
489
+ /**
490
+ @private
491
+ @method _internalReset
492
+ @since 3.6.0
493
+ */
494
+ _internalReset(isExiting, transition) {
495
+ let controller = this.controller;
496
+ // SAFETY: Since `_qp` is protected we can't infer the type
497
+ controller['_qpDelegate'] = get(this, '_qp').states.inactive;
498
+ this.resetController(controller, isExiting, transition);
499
+ }
500
+
501
+ /**
502
+ @private
503
+ @method enter
504
+ */
505
+ enter(transition) {
506
+ this[RENDER_STATE] = undefined;
507
+ this.activate(transition);
508
+ this.trigger('activate', transition);
509
+ }
510
+
511
+ /**
512
+ This event is triggered when the router enters the route. It is
513
+ not executed when the model for the route changes.
514
+ ```app/routes/application.js
515
+ import { on } from '@ember/object/evented';
516
+ import Route from '@ember/routing/route';
517
+ export default Route.extend({
518
+ collectAnalytics: on('activate', function(){
519
+ collectAnalytics();
520
+ })
521
+ });
522
+ ```
523
+ @event activate
524
+ @since 1.9.0
525
+ @public
526
+ */
527
+
528
+ /**
529
+ This event is triggered when the router completely exits this
530
+ route. It is not executed when the model for the route changes.
531
+ ```app/routes/index.js
532
+ import { on } from '@ember/object/evented';
533
+ import Route from '@ember/routing/route';
534
+ export default Route.extend({
535
+ trackPageLeaveAnalytics: on('deactivate', function(){
536
+ trackPageLeaveAnalytics();
537
+ })
538
+ });
539
+ ```
540
+ @event deactivate
541
+ @since 1.9.0
542
+ @public
543
+ */
544
+
545
+ /**
546
+ This hook is executed when the router completely exits this route. It is
547
+ not executed when the model for the route changes.
548
+ @method deactivate
549
+ @param {Transition} transition
550
+ @since 1.0.0
551
+ @public
552
+ */
553
+ deactivate(_transition) {}
554
+
555
+ /**
556
+ This hook is executed when the router enters the route. It is not executed
557
+ when the model for the route changes.
558
+ @method activate
559
+ @param {Transition} transition
560
+ @since 1.0.0
561
+ @public
562
+ */
563
+ activate(_transition) {}
564
+
565
+ /**
566
+ Perform a synchronous transition into another route without attempting
567
+ to resolve promises, update the URL, or abort any currently active
568
+ asynchronous transitions (i.e. regular transitions caused by
569
+ `transitionTo` or URL changes).
570
+ This method is handy for performing intermediate transitions on the
571
+ way to a final destination route, and is called internally by the
572
+ default implementations of the `error` and `loading` handlers.
573
+ @method intermediateTransitionTo
574
+ @param {String} name the name of the route
575
+ @param {...Object} models the model(s) to be used while transitioning
576
+ to the route.
577
+ @since 1.2.0
578
+ @public
579
+ */
580
+ intermediateTransitionTo(...args) {
581
+ let [name, ...preparedArgs] = prefixRouteNameArg(this, args);
582
+ this._router.intermediateTransitionTo(name, ...preparedArgs);
583
+ }
584
+
585
+ /**
586
+ Refresh the model on this route and any child routes, firing the
587
+ `beforeModel`, `model`, and `afterModel` hooks in a similar fashion
588
+ to how routes are entered when transitioning in from other route.
589
+ The current route params (e.g. `article_id`) will be passed in
590
+ to the respective model hooks, and if a different model is returned,
591
+ `setupController` and associated route hooks will re-fire as well.
592
+ An example usage of this method is re-querying the server for the
593
+ latest information using the same parameters as when the route
594
+ was first entered.
595
+ Note that this will cause `model` hooks to fire even on routes
596
+ that were provided a model object when the route was initially
597
+ entered.
598
+ @method refresh
599
+ @return {Transition} the transition object associated with this
600
+ attempted transition
601
+ @since 1.4.0
602
+ @public
603
+ */
604
+ refresh() {
605
+ return this._router._routerMicrolib.refresh(this);
606
+ }
607
+
608
+ /**
609
+ This hook is the entry point for router.js
610
+ @private
611
+ @method setup
612
+ */
613
+ setup(context, transition) {
614
+ let controllerName = this.controllerName || this.routeName;
615
+ let definedController = this.controllerFor(controllerName, true);
616
+ let controller = definedController ?? this.generateController(controllerName);
617
+
618
+ // SAFETY: Since `_qp` is protected we can't infer the type
619
+ let queryParams = get(this, '_qp');
620
+
621
+ // Assign the route's controller so that it can more easily be
622
+ // referenced in action handlers. Side effects. Side effects everywhere.
623
+ if (!this.controller) {
624
+ let propNames = queryParams.propertyNames;
625
+ addQueryParamsObservers(controller, propNames);
626
+ this.controller = controller;
627
+ }
628
+ let states = queryParams.states;
629
+ controller._qpDelegate = states.allowOverrides;
630
+ if (transition) {
631
+ // Update the model dep values used to calculate cache keys.
632
+ stashParamNames(this._router, transition[STATE_SYMBOL].routeInfos);
633
+ let cache = this._bucketCache;
634
+ let params = transition[PARAMS_SYMBOL];
635
+ let allParams = queryParams.propertyNames;
636
+ allParams.forEach(prop => {
637
+ let aQp = queryParams.map[prop];
638
+ (isDevelopingApp() && !(aQp) && assert('expected aQp', aQp));
639
+ aQp.values = params;
640
+ let cacheKey = calculateCacheKey(aQp.route.fullRouteName, aQp.parts, aQp.values);
641
+ let value = cache.lookup(cacheKey, prop, aQp.undecoratedDefaultValue);
642
+ set(controller, prop, value);
643
+ });
644
+ let qpValues = getQueryParamsFor(this, transition[STATE_SYMBOL]);
645
+ setProperties(controller, qpValues);
646
+ }
647
+ this.setupController(controller, context, transition);
648
+ if (this._environment.options.shouldRender) {
649
+ this[RENDER]();
650
+ }
651
+
652
+ // Setup can cause changes to QPs which need to be propogated immediately in
653
+ // some situations. Eventually, we should work on making these async somehow.
654
+ flushAsyncObservers(false);
655
+ }
656
+
657
+ /*
658
+ Called when a query parameter for this route changes, regardless of whether the route
659
+ is currently part of the active route hierarchy. This will update the query parameter's
660
+ value in the cache so if this route becomes active, the cache value has been updated.
661
+ */
662
+ _qpChanged(prop, value, qp) {
663
+ if (!qp) {
664
+ return;
665
+ }
666
+
667
+ // Update model-dep cache
668
+ let cache = this._bucketCache;
669
+ let cacheKey = calculateCacheKey(qp.route.fullRouteName, qp.parts, qp.values);
670
+ cache.stash(cacheKey, prop, value);
671
+ }
672
+
673
+ /**
674
+ This hook is the first of the route entry validation hooks
675
+ called when an attempt is made to transition into a route
676
+ or one of its children. It is called before `model` and
677
+ `afterModel`, and is appropriate for cases when:
678
+ 1) A decision can be made to redirect elsewhere without
679
+ needing to resolve the model first.
680
+ 2) Any async operations need to occur first before the
681
+ model is attempted to be resolved.
682
+ This hook is provided the current `transition` attempt
683
+ as a parameter, which can be used to `.abort()` the transition,
684
+ save it for a later `.retry()`, or retrieve values set
685
+ on it from a previous hook. You can also just call
686
+ `router.transitionTo` to another route to implicitly
687
+ abort the `transition`.
688
+ You can return a promise from this hook to pause the
689
+ transition until the promise resolves (or rejects). This could
690
+ be useful, for instance, for retrieving async code from
691
+ the server that is required to enter a route.
692
+ @method beforeModel
693
+ @param {Transition} transition
694
+ @return {any | Promise<any>} if the value returned from this hook is
695
+ a promise, the transition will pause until the transition
696
+ resolves. Otherwise, non-promise return values are not
697
+ utilized in any way.
698
+ @since 1.0.0
699
+ @public
700
+ */
701
+
702
+ beforeModel(_transition) {}
703
+
704
+ /**
705
+ This hook is called after this route's model has resolved.
706
+ It follows identical async/promise semantics to `beforeModel`
707
+ but is provided the route's resolved model in addition to
708
+ the `transition`, and is therefore suited to performing
709
+ logic that can only take place after the model has already
710
+ resolved.
711
+ ```app/routes/posts.js
712
+ import Route from '@ember/routing/route';
713
+ import { service } from '@ember/service';
714
+ export default class PostsRoute extends Route {
715
+ @service router;
716
+ afterModel(posts, transition) {
717
+ if (posts.get('length') === 1) {
718
+ this.router.transitionTo('post.show', posts.get('firstObject'));
719
+ }
720
+ }
721
+ }
722
+ ```
723
+ Refer to documentation for `beforeModel` for a description
724
+ of transition-pausing semantics when a promise is returned
725
+ from this hook.
726
+ @method afterModel
727
+ @param {Object} resolvedModel the value returned from `model`,
728
+ or its resolved value if it was a promise
729
+ @param {Transition} transition
730
+ @return {any | Promise<any>} if the value returned from this hook is
731
+ a promise, the transition will pause until the transition
732
+ resolves. Otherwise, non-promise return values are not
733
+ utilized in any way.
734
+ @since 1.0.0
735
+ @public
736
+ */
737
+
738
+ afterModel(_resolvedModel, _transition) {}
739
+
740
+ /**
741
+ A hook you can implement to optionally redirect to another route.
742
+ Calling `this.router.transitionTo` from inside of the `redirect` hook will
743
+ abort the current transition (into the route that has implemented `redirect`).
744
+ `redirect` and `afterModel` behave very similarly and are
745
+ called almost at the same time, but they have an important
746
+ distinction when calling `this.router.transitionTo` to a child route
747
+ of the current route. From `afterModel`, this new transition
748
+ invalidates the current transition, causing `beforeModel`,
749
+ `model`, and `afterModel` hooks to be called again. But the
750
+ same transition started from `redirect` does _not_ invalidate
751
+ the current transition. In other words, by the time the `redirect`
752
+ hook has been called, both the resolved model and the attempted
753
+ entry into this route are considered fully validated.
754
+ @method redirect
755
+ @param {Object} model the model for this route
756
+ @param {Transition} transition the transition object associated with the current transition
757
+ @since 1.0.0
758
+ @public
759
+ */
760
+ redirect(_model, _transition) {}
761
+
762
+ /**
763
+ Called when the context is changed by router.js.
764
+ @private
765
+ @method contextDidChange
766
+ */
767
+ contextDidChange() {
768
+ this.currentModel = this.context;
769
+ }
770
+
771
+ /**
772
+ A hook you can implement to convert the URL into the model for
773
+ this route.
774
+ ```app/router.js
775
+ // ...
776
+ Router.map(function() {
777
+ this.route('post', { path: '/posts/:post_id' });
778
+ });
779
+ export default Router;
780
+ ```
781
+ Note that for routes with dynamic segments, this hook is not always
782
+ executed. If the route is entered through a transition (e.g. when
783
+ using the `link-to` Handlebars helper or the `transitionTo` method
784
+ of routes), and a model context is already provided this hook
785
+ is not called.
786
+ A model context does not include a primitive string or number,
787
+ which does cause the model hook to be called.
788
+ Routes without dynamic segments will always execute the model hook.
789
+ ```javascript
790
+ // no dynamic segment, model hook always called
791
+ this.router.transitionTo('posts');
792
+ // model passed in, so model hook not called
793
+ thePost = store.findRecord('post', 1);
794
+ this.router.transitionTo('post', thePost);
795
+ // integer passed in, model hook is called
796
+ this.router.transitionTo('post', 1);
797
+ // model id passed in, model hook is called
798
+ // useful for forcing the hook to execute
799
+ thePost = store.findRecord('post', 1);
800
+ this.router.transitionTo('post', thePost.id);
801
+ ```
802
+ This hook follows the asynchronous/promise semantics
803
+ described in the documentation for `beforeModel`. In particular,
804
+ if a promise returned from `model` fails, the error will be
805
+ handled by the `error` hook on `Route`.
806
+ Note that the legacy behavior of automatically defining a model
807
+ hook when a dynamic segment ending in `_id` is present is
808
+ [deprecated](https://deprecations.emberjs.com/v5.x#toc_deprecate-implicit-route-model).
809
+ You should explicitly define a model hook whenever any segments are
810
+ present.
811
+ Example
812
+ ```app/routes/post.js
813
+ import Route from '@ember/routing/route';
814
+ import { service } from '@ember/service';
815
+ export default class PostRoute extends Route {
816
+ @service store;
817
+ model(params) {
818
+ return this.store.findRecord('post', params.post_id);
819
+ }
820
+ }
821
+ ```
822
+ @method model
823
+ @param {Object} params the parameters extracted from the URL
824
+ @param {Transition} transition
825
+ @return {any | Promise<any>} the model for this route. If
826
+ a promise is returned, the transition will pause until
827
+ the promise resolves, and the resolved value of the promise
828
+ will be used as the model for this route.
829
+ @since 1.0.0
830
+ @public
831
+ */
832
+ model(params, transition) {
833
+ let name, sawParams, value;
834
+ // SAFETY: Since `_qp` is protected we can't infer the type
835
+ let queryParams = get(this, '_qp').map;
836
+ for (let prop in params) {
837
+ if (prop === 'queryParams' || queryParams && prop in queryParams) {
838
+ continue;
839
+ }
840
+ let match = prop.match(/^(.*)_id$/);
841
+ if (match !== null) {
842
+ name = match[1];
843
+ value = params[prop];
844
+ }
845
+ sawParams = true;
846
+ }
847
+ if (!name) {
848
+ if (sawParams) {
849
+ // SAFETY: This should be equivalent
850
+ return Object.assign({}, params);
851
+ } else {
852
+ if (transition.resolveIndex < 1) {
853
+ return;
854
+ }
855
+ // SAFETY: This should be correct, but TS is unable to infer this.
856
+ return transition[STATE_SYMBOL].routeInfos[transition.resolveIndex - 1].context;
857
+ }
858
+ }
859
+ return this.findModel(name, value);
860
+ }
861
+
862
+ /**
863
+ @private
864
+ @method deserialize
865
+ @param {Object} params the parameters extracted from the URL
866
+ @param {Transition} transition
867
+ @return {any | Promise<any>} the model for this route.
868
+ Router.js hook.
869
+ */
870
+ deserialize(_params, transition) {
871
+ return this.model(this._paramsFor(this.routeName, _params), transition);
872
+ }
873
+
874
+ /**
875
+ @method findModel
876
+ @param {String} type the model type
877
+ @param {Object} value the value passed to find
878
+ @private
879
+ */
880
+ findModel(type, value) {
881
+ if (ENV._NO_IMPLICIT_ROUTE_MODEL) {
882
+ return;
883
+ }
884
+ deprecateUntil(`The implicit model loading behavior for routes is deprecated. ` + `Please define an explicit model hook for ${this.fullRouteName}.`, DEPRECATIONS.DEPRECATE_IMPLICIT_ROUTE_MODEL);
885
+ const store = 'store' in this ? this.store : get(this, '_store');
886
+ (isDevelopingApp() && !(isStoreLike(store)) && assert('Expected route to have a store with a find method', isStoreLike(store))); // SAFETY: We don't actually know it will return this, but this code path is also deprecated.
887
+ return store.find(type, value);
888
+ }
889
+
890
+ /**
891
+ A hook you can use to setup the controller for the current route.
892
+ This method is called with the controller for the current route and the
893
+ model supplied by the `model` hook.
894
+ By default, the `setupController` hook sets the `model` property of
895
+ the controller to the specified `model` when it is not `undefined`.
896
+ If you implement the `setupController` hook in your Route, it will
897
+ prevent this default behavior. If you want to preserve that behavior
898
+ when implementing your `setupController` function, make sure to call
899
+ `super`:
900
+ ```app/routes/photos.js
901
+ import Route from '@ember/routing/route';
902
+ import { service } from '@ember/service';
903
+ export default class PhotosRoute extends Route {
904
+ @service store;
905
+ model() {
906
+ return this.store.findAll('photo');
907
+ }
908
+ setupController(controller, model) {
909
+ super.setupController(controller, model);
910
+ this.controllerFor('application').set('showingPhotos', true);
911
+ }
912
+ }
913
+ ```
914
+ The provided controller will be one resolved based on the name
915
+ of this route.
916
+ If no explicit controller is defined, Ember will automatically create one.
917
+ As an example, consider the router:
918
+ ```app/router.js
919
+ // ...
920
+ Router.map(function() {
921
+ this.route('post', { path: '/posts/:post_id' });
922
+ });
923
+ export default Router;
924
+ ```
925
+ If you have defined a file for the post controller,
926
+ the framework will use it.
927
+ If it is not defined, a basic `Controller` instance would be used.
928
+ @example Behavior of a basic Controller
929
+ ```app/routes/post.js
930
+ import Route from '@ember/routing/route';
931
+ export default class PostRoute extends Route {
932
+ setupController(controller, model) {
933
+ controller.set('model', model);
934
+ }
935
+ });
936
+ ```
937
+ @method setupController
938
+ @param {Controller} controller instance
939
+ @param {Object} model
940
+ @param {Transition} [transition]
941
+ @since 1.0.0
942
+ @public
943
+ */
944
+ setupController(controller, context, _transition) {
945
+ if (controller && context !== undefined) {
946
+ set(controller, 'model', context);
947
+ }
948
+ }
949
+
950
+ /**
951
+ Returns the controller of the current route, or a parent (or any ancestor)
952
+ route in a route hierarchy.
953
+ The controller instance must already have been created, either through entering the
954
+ associated route or using `generateController`.
955
+ ```app/routes/post.js
956
+ import Route from '@ember/routing/route';
957
+ export default class PostRoute extends Route {
958
+ setupController(controller, post) {
959
+ super.setupController(controller, post);
960
+ this.controllerFor('posts').set('currentPost', post);
961
+ }
962
+ }
963
+ ```
964
+ @method controllerFor
965
+ @param {String} name the name of the route or controller
966
+ @return {Controller | undefined}
967
+ @since 1.0.0
968
+ @public
969
+ */
970
+
971
+ controllerFor(name, _skipAssert = false) {
972
+ let owner = getOwner(this);
973
+ (isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
974
+ let route = owner.lookup(`route:${name}`);
975
+ if (route && route.controllerName) {
976
+ name = route.controllerName;
977
+ }
978
+ let controller = owner.lookup(`controller:${name}`);
979
+
980
+ // NOTE: We're specifically checking that skipAssert is true, because according
981
+ // to the old API the second parameter was model. We do not want people who
982
+ // passed a model to skip the assertion.
983
+ (isDevelopingApp() && !(controller !== undefined || _skipAssert === true) && assert(`The controller named '${name}' could not be found. Make sure that this route exists and has already been entered at least once. If you are accessing a controller not associated with a route, make sure the controller class is explicitly defined.`, controller !== undefined || _skipAssert === true));
984
+ (isDevelopingApp() && !(controller === undefined || controller instanceof Controller) && assert(`Expected controller:${name} to be an instance of Controller`, controller === undefined || controller instanceof Controller));
985
+ return controller;
986
+ }
987
+
988
+ /**
989
+ Generates a controller for a route.
990
+ Example
991
+ ```app/routes/post.js
992
+ import Route from '@ember/routing/route';
993
+ export default class Post extends Route {
994
+ setupController(controller, post) {
995
+ super.setupController(controller, post);
996
+ this.generateController('posts');
997
+ }
998
+ }
999
+ ```
1000
+ @method generateController
1001
+ @param {String} name the name of the controller
1002
+ @private
1003
+ */
1004
+ generateController(name) {
1005
+ let owner = getOwner(this);
1006
+ (isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
1007
+ return generateController(owner, name);
1008
+ }
1009
+
1010
+ /**
1011
+ Returns the resolved model of a parent (or any ancestor) route
1012
+ in a route hierarchy. During a transition, all routes
1013
+ must resolve a model object, and if a route
1014
+ needs access to a parent route's model in order to
1015
+ resolve a model (or just reuse the model from a parent),
1016
+ it can call `this.modelFor(theNameOfParentRoute)` to
1017
+ retrieve it. If the ancestor route's model was a promise,
1018
+ its resolved result is returned.
1019
+ Example
1020
+ ```app/router.js
1021
+ // ...
1022
+ Router.map(function() {
1023
+ this.route('post', { path: '/posts/:post_id' }, function() {
1024
+ this.route('comments');
1025
+ });
1026
+ });
1027
+ export default Router;
1028
+ ```
1029
+ ```app/routes/post/comments.js
1030
+ import Route from '@ember/routing/route';
1031
+ export default class PostCommentsRoute extends Route {
1032
+ model() {
1033
+ let post = this.modelFor('post');
1034
+ return post.comments;
1035
+ }
1036
+ }
1037
+ ```
1038
+ @method modelFor
1039
+ @param {String} name the name of the route
1040
+ @return {Object} the model object
1041
+ @since 1.0.0
1042
+ @public
1043
+ */
1044
+ modelFor(_name) {
1045
+ let name;
1046
+ let owner = getOwner(this);
1047
+ (isDevelopingApp() && !(owner instanceof EngineInstance) && assert('Expected router owner to be an EngineInstance', owner instanceof EngineInstance));
1048
+ let transition = this._router && this._router._routerMicrolib ? this._router._routerMicrolib.activeTransition : undefined;
1049
+
1050
+ // Only change the route name when there is an active transition.
1051
+ // Otherwise, use the passed in route name.
1052
+ if (owner.routable && transition !== undefined) {
1053
+ name = getEngineRouteName(owner, _name);
1054
+ } else {
1055
+ name = _name;
1056
+ }
1057
+ let route = owner.lookup(`route:${name}`);
1058
+ // If we are mid-transition, we want to try and look up
1059
+ // resolved parent contexts on the current transitionEvent.
1060
+ if (transition !== undefined && transition !== null) {
1061
+ let modelLookupName = route && route.routeName || name;
1062
+ if (Object.prototype.hasOwnProperty.call(transition.resolvedModels, modelLookupName)) {
1063
+ return transition.resolvedModels[modelLookupName];
1064
+ }
1065
+ }
1066
+ return route?.currentModel;
1067
+ }
1068
+ [RENDER_STATE] = undefined;
1069
+
1070
+ /**
1071
+ `this[RENDER]` is used to set up the rendering option for the outlet state.
1072
+ @method this[RENDER]
1073
+ @private
1074
+ */
1075
+ [RENDER]() {
1076
+ this[RENDER_STATE] = buildRenderState(this);
1077
+ once(this._router, '_setOutlets');
1078
+ }
1079
+ willDestroy() {
1080
+ this.teardownViews();
1081
+ }
1082
+
1083
+ /**
1084
+ @private
1085
+ @method teardownViews
1086
+ */
1087
+ teardownViews() {
1088
+ if (this[RENDER_STATE]) {
1089
+ this[RENDER_STATE] = undefined;
1090
+ once(this._router, '_setOutlets');
1091
+ }
1092
+ }
1093
+
1094
+ /**
1095
+ Allows you to produce custom metadata for the route.
1096
+ The return value of this method will be attached to
1097
+ its corresponding RouteInfoWithAttributes object.
1098
+ Example
1099
+ ```app/routes/posts/index.js
1100
+ import Route from '@ember/routing/route';
1101
+ export default class PostsIndexRoute extends Route {
1102
+ buildRouteInfoMetadata() {
1103
+ return { title: 'Posts Page' }
1104
+ }
1105
+ }
1106
+ ```
1107
+ ```app/routes/application.js
1108
+ import Route from '@ember/routing/route';
1109
+ import { service } from '@ember/service';
1110
+ export default class ApplicationRoute extends Route {
1111
+ @service router
1112
+ constructor() {
1113
+ super(...arguments);
1114
+ this.router.on('routeDidChange', transition => {
1115
+ document.title = transition.to.metadata.title;
1116
+ // would update document's title to "Posts Page"
1117
+ });
1118
+ }
1119
+ }
1120
+ ```
1121
+ @method buildRouteInfoMetadata
1122
+ @return any
1123
+ @since 3.10.0
1124
+ @public
1125
+ */
1126
+
1127
+ buildRouteInfoMetadata() {}
1128
+ _paramsFor(routeName, params) {
1129
+ let transition = this._router._routerMicrolib.activeTransition;
1130
+ if (transition !== undefined) {
1131
+ return this.paramsFor(routeName);
1132
+ }
1133
+ return params;
1134
+ }
1135
+
1136
+ /** @deprecated Manually define your own store, such as with `@service store` */
1137
+ get _store() {
1138
+ const owner = getOwner(this);
1139
+ (isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
1140
+ let routeName = this.routeName;
1141
+ return {
1142
+ find(name, value) {
1143
+ let modelClass = owner.factoryFor(`model:${name}`);
1144
+ (isDevelopingApp() && !(Boolean(modelClass)) && assert(`You used the dynamic segment \`${name}_id\` in your route ` + `\`${routeName}\` for which Ember requires you provide a ` + `data-loading implementation. Commonly, that is done by ` + `adding a model hook implementation on the route ` + `(\`model({${name}_id}) {\`) or by injecting an implemention of ` + `a data store: \`@service store;\`.`, Boolean(modelClass)));
1145
+ if (!modelClass) {
1146
+ return;
1147
+ }
1148
+ modelClass = modelClass.class;
1149
+ (isDevelopingApp() && !(typeof modelClass.find === 'function') && assert(`You used the dynamic segment \`${name}_id\` in your route ` + `\`${routeName}\` for which Ember requires you provide a ` + `data-loading implementation. Commonly, that is done by ` + `adding a model hook implementation on the route ` + `(\`model({${name}_id}) {\`) or by injecting an implemention of ` + `a data store: \`@service store;\`.\n\n` + `Rarely, applications may attempt to use a legacy behavior where ` + `the model class (in this case \`${name}\`) is resolved and the ` + `\`find\` method on that class is invoked to load data. In this ` + `application, a model of \`${name}\` was found but it did not ` + `provide a \`find\` method. You should not add a \`find\` ` + `method to your model. Instead, please implement an appropriate ` + `\`model\` hook on the \`${routeName}\` route.`, typeof modelClass.find === 'function'));
1150
+ return modelClass.find(value);
1151
+ }
1152
+ };
1153
+ }
1154
+
1155
+ /**
1156
+ @private
1157
+ @property _qp
1158
+ */
1159
+ static {
1160
+ decorateMethodV2(this.prototype, "_store", [computed]);
1161
+ }
1162
+ get _qp() {
1163
+ let combinedQueryParameterConfiguration = {};
1164
+ let controllerName = this.controllerName || this.routeName;
1165
+ let owner = getOwner(this);
1166
+ (isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
1167
+ let controller = owner.lookup(`controller:${controllerName}`);
1168
+ let queryParameterConfiguraton = get(this, 'queryParams');
1169
+ let hasRouterDefinedQueryParams = Object.keys(queryParameterConfiguraton).length > 0;
1170
+ if (controller) {
1171
+ (isDevelopingApp() && !(controller instanceof Controller) && assert('Expected an instance of controller', controller instanceof Controller)); // the developer has authored a controller class in their application for
1172
+ // this route find its query params and normalize their object shape them
1173
+ // merge in the query params for the route. As a mergedProperty,
1174
+ // Route#queryParams is always at least `{}`
1175
+ let controllerDefinedQueryParameterConfiguration = get(controller, 'queryParams') || [];
1176
+ let normalizedControllerQueryParameterConfiguration = normalizeControllerQueryParams(controllerDefinedQueryParameterConfiguration);
1177
+ combinedQueryParameterConfiguration = mergeEachQueryParams(normalizedControllerQueryParameterConfiguration, queryParameterConfiguraton);
1178
+ } else if (hasRouterDefinedQueryParams) {
1179
+ // the developer has not defined a controller but *has* supplied route query params.
1180
+ // Generate a class for them so we can later insert default values
1181
+ controller = generateController(owner, controllerName);
1182
+ combinedQueryParameterConfiguration = queryParameterConfiguraton;
1183
+ }
1184
+ let qps = [];
1185
+ let map = {};
1186
+ let propertyNames = [];
1187
+ for (let propName in combinedQueryParameterConfiguration) {
1188
+ if (!Object.prototype.hasOwnProperty.call(combinedQueryParameterConfiguration, propName)) {
1189
+ continue;
1190
+ }
1191
+
1192
+ // to support the dubious feature of using unknownProperty
1193
+ // on queryParams configuration
1194
+ if (propName === 'unknownProperty' || propName === '_super') {
1195
+ // possible todo: issue deprecation warning?
1196
+ continue;
1197
+ }
1198
+ let desc = combinedQueryParameterConfiguration[propName];
1199
+ (isDevelopingApp() && !(desc) && assert(`[BUG] missing query parameter configuration for ${propName}`, desc));
1200
+ let scope = desc.scope || 'model';
1201
+ let parts = undefined;
1202
+ if (scope === 'controller') {
1203
+ parts = [];
1204
+ }
1205
+ let urlKey = desc.as || this.serializeQueryParamKey(propName);
1206
+ let defaultValue = get(controller, propName);
1207
+ defaultValue = copyDefaultValue(defaultValue);
1208
+ let type = desc.type || typeOf(defaultValue);
1209
+ let defaultValueSerialized = this.serializeQueryParam(defaultValue, urlKey, type);
1210
+ let scopedPropertyName = `${controllerName}:${propName}`;
1211
+ let qp = {
1212
+ undecoratedDefaultValue: get(controller, propName),
1213
+ defaultValue,
1214
+ serializedDefaultValue: defaultValueSerialized,
1215
+ serializedValue: defaultValueSerialized,
1216
+ type,
1217
+ urlKey,
1218
+ prop: propName,
1219
+ scopedPropertyName,
1220
+ controllerName,
1221
+ route: this,
1222
+ parts,
1223
+ // provided later when stashNames is called if 'model' scope
1224
+ values: null,
1225
+ // provided later when setup is called. no idea why.
1226
+ scope
1227
+ };
1228
+ map[propName] = map[urlKey] = map[scopedPropertyName] = qp;
1229
+ qps.push(qp);
1230
+ propertyNames.push(propName);
1231
+ }
1232
+ return {
1233
+ qps,
1234
+ map,
1235
+ propertyNames,
1236
+ states: {
1237
+ /*
1238
+ Called when a query parameter changes in the URL, this route cares
1239
+ about that query parameter, but the route is not currently
1240
+ in the active route hierarchy.
1241
+ */
1242
+ inactive: (prop, value) => {
1243
+ let qp = map[prop];
1244
+ (isDevelopingApp() && !(qp) && assert('expected inactive callback to only be called for registered qps', qp));
1245
+ this._qpChanged(prop, value, qp);
1246
+ },
1247
+ /*
1248
+ Called when a query parameter changes in the URL, this route cares
1249
+ about that query parameter, and the route is currently
1250
+ in the active route hierarchy.
1251
+ */
1252
+ active: (prop, value) => {
1253
+ let qp = map[prop];
1254
+ (isDevelopingApp() && !(qp) && assert('expected active callback to only be called for registered qps', qp));
1255
+ this._qpChanged(prop, value, qp);
1256
+ return this._activeQPChanged(qp, value);
1257
+ },
1258
+ /*
1259
+ Called when a value of a query parameter this route handles changes in a controller
1260
+ and the route is currently in the active route hierarchy.
1261
+ */
1262
+ allowOverrides: (prop, value) => {
1263
+ let qp = map[prop];
1264
+ (isDevelopingApp() && !(qp) && assert('expected allowOverrides callback to only be called for registered qps', qp));
1265
+ this._qpChanged(prop, value, qp);
1266
+ return this._updatingQPChanged(qp);
1267
+ }
1268
+ }
1269
+ };
1270
+ }
1271
+
1272
+ // Set in reopen
1273
+ static {
1274
+ decorateMethodV2(this.prototype, "_qp", [computed]);
1275
+ }
1276
+ /**
1277
+ Sends an action to the router, which will delegate it to the currently
1278
+ active route hierarchy per the bubbling rules explained under `actions`.
1279
+ Example
1280
+ ```app/router.js
1281
+ // ...
1282
+ Router.map(function() {
1283
+ this.route('index');
1284
+ });
1285
+ export default Router;
1286
+ ```
1287
+ ```app/routes/application.js
1288
+ import Route from '@ember/routing/route';
1289
+ import { action } from '@ember/object';
1290
+ export default class ApplicationRoute extends Route {
1291
+ @action
1292
+ track(arg) {
1293
+ console.log(arg, 'was clicked');
1294
+ }
1295
+ }
1296
+ ```
1297
+ ```app/routes/index.js
1298
+ import Route from '@ember/routing/route';
1299
+ import { action } from '@ember/object';
1300
+ export default class IndexRoute extends Route {
1301
+ @action
1302
+ trackIfDebug(arg) {
1303
+ if (debug) {
1304
+ this.send('track', arg);
1305
+ }
1306
+ }
1307
+ }
1308
+ ```
1309
+ @method send
1310
+ @param {String} name the name of the action to trigger
1311
+ @param {...*} args
1312
+ @since 1.0.0
1313
+ @public
1314
+ */
1315
+ // Set with reopen to override parent behavior
1316
+ }
1317
+ function getRenderState(route) {
1318
+ return route[RENDER_STATE];
1319
+ }
1320
+ function buildRenderState(route) {
1321
+ let owner = getOwner(route);
1322
+ (isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
1323
+ let name = route.routeName;
1324
+ let controller = owner.lookup(`controller:${route.controllerName || name}`);
1325
+ (isDevelopingApp() && !(controller instanceof Controller) && assert('Expected an instance of controller', controller instanceof Controller));
1326
+ let model = route.currentModel;
1327
+ let template = owner.lookup(`template:${route.templateName || name}`);
1328
+ let render = {
1329
+ owner,
1330
+ into: undefined,
1331
+ outlet: 'main',
1332
+ name,
1333
+ controller,
1334
+ model,
1335
+ template: template?.(owner) ?? route._topLevelViewTemplate(owner)
1336
+ };
1337
+ if (isDevelopingApp()) {
1338
+ let LOG_VIEW_LOOKUPS = get(route._router, 'namespace.LOG_VIEW_LOOKUPS');
1339
+ if (LOG_VIEW_LOOKUPS && !template) {
1340
+ info(`Could not find "${name}" template. Nothing will be rendered`, {
1341
+ fullName: `template:${name}`
1342
+ });
1343
+ }
1344
+ }
1345
+ return render;
1346
+ }
1347
+ function getFullQueryParams(router, state) {
1348
+ if (state.fullQueryParams) {
1349
+ return state.fullQueryParams;
1350
+ }
1351
+ let haveAllRouteInfosResolved = state.routeInfos.every(routeInfo => routeInfo.route);
1352
+ let fullQueryParamsState = {
1353
+ ...state.queryParams
1354
+ };
1355
+ router._deserializeQueryParams(state.routeInfos, fullQueryParamsState);
1356
+
1357
+ // only cache query params state if all routeinfos have resolved; it's possible
1358
+ // for lazy routes to not have resolved when `getFullQueryParams` is called, so
1359
+ // we wait until all routes have resolved prior to caching query params state
1360
+ if (haveAllRouteInfosResolved) {
1361
+ state.fullQueryParams = fullQueryParamsState;
1362
+ }
1363
+ return fullQueryParamsState;
1364
+ }
1365
+ function getQueryParamsFor(route, state) {
1366
+ state.queryParamsFor = state.queryParamsFor || {};
1367
+ let name = route.fullRouteName;
1368
+ let existing = state.queryParamsFor[name];
1369
+ if (existing) {
1370
+ return existing;
1371
+ }
1372
+ let fullQueryParams = getFullQueryParams(route._router, state);
1373
+ let params = state.queryParamsFor[name] = {};
1374
+
1375
+ // Copy over all the query params for this route/controller into params hash.
1376
+ // SAFETY: Since `_qp` is protected we can't infer the type
1377
+ let qps = get(route, '_qp').qps;
1378
+ for (let qp of qps) {
1379
+ // Put deserialized qp on params hash.
1380
+ let qpValueWasPassedIn = (qp.prop in fullQueryParams);
1381
+ params[qp.prop] = qpValueWasPassedIn ? fullQueryParams[qp.prop] : copyDefaultValue(qp.defaultValue);
1382
+ }
1383
+ return params;
1384
+ }
1385
+
1386
+ // FIXME: This should probably actually return a `NativeArray` if the passed in value is an Array.
1387
+ function copyDefaultValue(value) {
1388
+ if (Array.isArray(value)) {
1389
+ // SAFETY: We lost the type data about the array if we don't cast.
1390
+ return A(value.slice());
1391
+ }
1392
+ return value;
1393
+ }
1394
+
1395
+ /*
1396
+ Merges all query parameters from a controller with those from
1397
+ a route, returning a new object and avoiding any mutations to
1398
+ the existing objects.
1399
+ */
1400
+ function mergeEachQueryParams(controllerQP, routeQP) {
1401
+ let qps = {};
1402
+ let keysAlreadyMergedOrSkippable = {
1403
+ defaultValue: true,
1404
+ type: true,
1405
+ scope: true,
1406
+ as: true
1407
+ };
1408
+
1409
+ // first loop over all controller qps, merging them with any matching route qps
1410
+ // into a new empty object to avoid mutating.
1411
+ for (let cqpName in controllerQP) {
1412
+ if (!Object.prototype.hasOwnProperty.call(controllerQP, cqpName)) {
1413
+ continue;
1414
+ }
1415
+ qps[cqpName] = {
1416
+ ...controllerQP[cqpName],
1417
+ ...routeQP[cqpName]
1418
+ };
1419
+
1420
+ // allows us to skip this QP when we check route QPs.
1421
+ keysAlreadyMergedOrSkippable[cqpName] = true;
1422
+ }
1423
+
1424
+ // loop over all route qps, skipping those that were merged in the first pass
1425
+ // because they also appear in controller qps
1426
+ for (let rqpName in routeQP) {
1427
+ if (!Object.prototype.hasOwnProperty.call(routeQP, rqpName) || keysAlreadyMergedOrSkippable[rqpName]) {
1428
+ continue;
1429
+ }
1430
+ qps[rqpName] = {
1431
+ ...routeQP[rqpName],
1432
+ ...controllerQP[rqpName]
1433
+ };
1434
+ }
1435
+ return qps;
1436
+ }
1437
+ function addQueryParamsObservers(controller, propNames) {
1438
+ propNames.forEach(prop => {
1439
+ if (descriptorForProperty(controller, prop) === undefined) {
1440
+ let desc = lookupDescriptor(controller, prop);
1441
+ if (desc !== null && (typeof desc.get === 'function' || typeof desc.set === 'function')) {
1442
+ defineProperty(controller, prop, dependentKeyCompat({
1443
+ get: desc.get,
1444
+ set: desc.set
1445
+ }));
1446
+ }
1447
+ }
1448
+ addObserver(controller, `${prop}.[]`, controller, controller._qpChanged, false);
1449
+ });
1450
+ }
1451
+ function getEngineRouteName(engine, routeName) {
1452
+ if (engine.routable) {
1453
+ let prefix = engine.mountPoint;
1454
+ if (routeName === 'application') {
1455
+ return prefix;
1456
+ } else {
1457
+ return `${prefix}.${routeName}`;
1458
+ }
1459
+ }
1460
+ return routeName;
1461
+ }
1462
+ const defaultSerialize = Route.prototype.serialize;
1463
+ function hasDefaultSerialize(route) {
1464
+ return route.serialize === defaultSerialize;
1465
+ }
1466
+
1467
+ // Set these here so they can be overridden with extend
1468
+ Route.reopen({
1469
+ mergedProperties: ['queryParams'],
1470
+ queryParams: {},
1471
+ templateName: null,
1472
+ controllerName: null,
1473
+ send(...args) {
1474
+ (isDevelopingApp() && !(!this.isDestroying && !this.isDestroyed) && assert(`Attempted to call .send() with the action '${args[0]}' on the destroyed route '${this.routeName}'.`, !this.isDestroying && !this.isDestroyed));
1475
+ if (this._router && this._router._routerMicrolib || !isTesting()) {
1476
+ this._router.send(...args);
1477
+ } else {
1478
+ let name = args.shift();
1479
+ let action = this.actions[name];
1480
+ if (action) {
1481
+ return action.apply(this, args);
1482
+ }
1483
+ }
1484
+ },
1485
+ /**
1486
+ The controller associated with this route.
1487
+ Example
1488
+ ```app/routes/form.js
1489
+ import Route from '@ember/routing/route';
1490
+ import { action } from '@ember/object';
1491
+ export default class FormRoute extends Route {
1492
+ @action
1493
+ willTransition(transition) {
1494
+ if (this.controller.get('userHasEnteredData') &&
1495
+ !confirm('Are you sure you want to abandon progress?')) {
1496
+ transition.abort();
1497
+ } else {
1498
+ // Bubble the `willTransition` action so that
1499
+ // parent routes can decide whether or not to abort.
1500
+ return true;
1501
+ }
1502
+ }
1503
+ }
1504
+ ```
1505
+ @property controller
1506
+ @type Controller
1507
+ @since 1.6.0
1508
+ @public
1509
+ */
1510
+
1511
+ actions: {
1512
+ /**
1513
+ This action is called when one or more query params have changed. Bubbles.
1514
+ @method queryParamsDidChange
1515
+ @param changed {Object} Keys are names of query params that have changed.
1516
+ @param totalPresent {Object} Keys are names of query params that are currently set.
1517
+ @param removed {Object} Keys are names of query params that have been removed.
1518
+ @returns {boolean}
1519
+ @private
1520
+ */
1521
+ queryParamsDidChange(changed, _totalPresent, removed) {
1522
+ // SAFETY: Since `_qp` is protected we can't infer the type
1523
+ let qpMap = get(this, '_qp').map;
1524
+ let totalChanged = Object.keys(changed).concat(Object.keys(removed));
1525
+ for (let change of totalChanged) {
1526
+ let qp = qpMap[change];
1527
+ if (qp) {
1528
+ let options = this._optionsForQueryParam(qp);
1529
+ (isDevelopingApp() && !(options && typeof options === 'object') && assert('options exists', options && typeof options === 'object'));
1530
+ if (get(options, 'refreshModel') && this._router.currentState) {
1531
+ this.refresh();
1532
+ break;
1533
+ }
1534
+ }
1535
+ }
1536
+ return true;
1537
+ },
1538
+ finalizeQueryParamChange(params, finalParams, transition) {
1539
+ if (this.fullRouteName !== 'application') {
1540
+ return true;
1541
+ }
1542
+
1543
+ // Transition object is absent for intermediate transitions.
1544
+ if (!transition) {
1545
+ return;
1546
+ }
1547
+ let routeInfos = transition[STATE_SYMBOL].routeInfos;
1548
+ let router = this._router;
1549
+ let qpMeta = router._queryParamsFor(routeInfos);
1550
+ let changes = router._qpUpdates;
1551
+ let qpUpdated = false;
1552
+ let replaceUrl;
1553
+ stashParamNames(router, routeInfos);
1554
+ for (let qp of qpMeta.qps) {
1555
+ let route = qp.route;
1556
+ let controller = route.controller;
1557
+ let presentKey = qp.urlKey in params && qp.urlKey;
1558
+
1559
+ // Do a reverse lookup to see if the changed query
1560
+ // param URL key corresponds to a QP property on
1561
+ // this controller.
1562
+ let value;
1563
+ let svalue;
1564
+ if (changes.has(qp.urlKey)) {
1565
+ // Value updated in/before setupController
1566
+ value = get(controller, qp.prop);
1567
+ svalue = route.serializeQueryParam(value, qp.urlKey, qp.type);
1568
+ } else {
1569
+ if (presentKey) {
1570
+ svalue = params[presentKey];
1571
+ if (svalue !== undefined) {
1572
+ value = route.deserializeQueryParam(svalue, qp.urlKey, qp.type);
1573
+ }
1574
+ } else {
1575
+ // No QP provided; use default value.
1576
+ svalue = qp.serializedDefaultValue;
1577
+ value = copyDefaultValue(qp.defaultValue);
1578
+ }
1579
+ }
1580
+
1581
+ // SAFETY: Since `_qp` is protected we can't infer the type
1582
+ controller._qpDelegate = get(route, '_qp').states.inactive;
1583
+ let thisQueryParamChanged = svalue !== qp.serializedValue;
1584
+ if (thisQueryParamChanged) {
1585
+ if (transition.queryParamsOnly && replaceUrl !== false) {
1586
+ let options = route._optionsForQueryParam(qp);
1587
+ let replaceConfigValue = get(options, 'replace');
1588
+ if (replaceConfigValue) {
1589
+ replaceUrl = true;
1590
+ } else if (replaceConfigValue === false) {
1591
+ // Explicit pushState wins over any other replaceStates.
1592
+ replaceUrl = false;
1593
+ }
1594
+ }
1595
+ set(controller, qp.prop, value);
1596
+ qpUpdated = true;
1597
+ }
1598
+
1599
+ // Stash current serialized value of controller.
1600
+ qp.serializedValue = svalue;
1601
+ let thisQueryParamHasDefaultValue = qp.serializedDefaultValue === svalue;
1602
+ if (!thisQueryParamHasDefaultValue) {
1603
+ finalParams.push({
1604
+ value: svalue,
1605
+ visible: true,
1606
+ key: presentKey || qp.urlKey
1607
+ });
1608
+ }
1609
+ }
1610
+
1611
+ // Some QPs have been updated, and those changes need to be propogated
1612
+ // immediately. Eventually, we should work on making this async somehow.
1613
+ if (qpUpdated === true) {
1614
+ flushAsyncObservers(false);
1615
+ }
1616
+ if (replaceUrl) {
1617
+ transition.method('replace');
1618
+ }
1619
+ qpMeta.qps.forEach(qp => {
1620
+ // SAFETY: Since `_qp` is protected we can't infer the type
1621
+ let routeQpMeta = get(qp.route, '_qp');
1622
+ let finalizedController = qp.route.controller;
1623
+ finalizedController['_qpDelegate'] = get(routeQpMeta, 'states.active');
1624
+ });
1625
+ router._qpUpdates.clear();
1626
+ return;
1627
+ }
1628
+ }
1629
+ });
1630
+
1631
+ export { Route as default, defaultSerialize, getFullQueryParams, getRenderState, hasDefaultSerialize };