ember-source 5.11.0 → 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.
- package/build-metadata.json +3 -3
- package/dist/ember-template-compiler.js +48 -42
- package/dist/ember-testing.js +1 -1
- package/dist/ember.debug.js +23102 -23100
- package/dist/ember.prod.js +31562 -31563
- package/dist/packages/@ember/-internals/container/index.js +1 -1
- package/dist/packages/@ember/-internals/deprecations/index.js +2 -1
- package/dist/packages/@ember/-internals/glimmer/index.js +2 -1
- package/dist/packages/@ember/-internals/meta/lib/meta.js +4 -3
- package/dist/packages/@ember/-internals/metal/index.js +9 -8
- package/dist/packages/@ember/-internals/routing/index.js +6 -5
- package/dist/packages/@ember/-internals/runtime/lib/ext/rsvp.js +3 -2
- package/dist/packages/@ember/-internals/runtime/lib/mixins/-proxy.js +6 -5
- package/dist/packages/@ember/-internals/runtime/lib/mixins/action_handler.js +4 -3
- package/dist/packages/@ember/-internals/runtime/lib/mixins/container_proxy.js +1 -1
- package/dist/packages/@ember/-internals/runtime/lib/mixins/registry_proxy.js +2 -1
- package/dist/packages/@ember/-internals/runtime/lib/mixins/target_action_support.js +4 -3
- package/dist/packages/@ember/-internals/string/index.js +1 -1
- package/dist/packages/@ember/-internals/utils/index.js +4 -4
- package/dist/packages/@ember/-internals/views/index.js +1 -1
- package/dist/packages/@ember/-internals/views/lib/compat/fallback-view-registry.js +1 -1
- package/dist/packages/@ember/-internals/views/lib/component_lookup.js +1 -1
- package/dist/packages/@ember/-internals/views/lib/mixins/action_support.js +5 -3
- package/dist/packages/@ember/-internals/views/lib/mixins/child_views_support.js +3 -3
- package/dist/packages/@ember/-internals/views/lib/mixins/class_names_support.js +4 -3
- package/dist/packages/@ember/-internals/views/lib/mixins/view_support.js +4 -3
- package/dist/packages/@ember/-internals/views/lib/system/event_dispatcher.js +6 -12
- package/dist/packages/@ember/-internals/views/lib/system/utils.js +3 -2
- package/dist/packages/@ember/-internals/views/lib/views/core_view.js +76 -8
- package/dist/packages/@ember/-internals/views/lib/views/states.js +4 -3
- package/dist/packages/@ember/application/index.js +16 -7
- package/dist/packages/@ember/application/instance.js +13 -9
- package/dist/packages/@ember/application/namespace.js +7 -6
- package/dist/packages/@ember/array/index.js +617 -11
- package/dist/packages/@ember/array/make.js +1 -0
- package/dist/packages/@ember/array/mutable.js +1 -1
- package/dist/packages/@ember/array/proxy.js +8 -5
- package/dist/packages/@ember/component/helper.js +4 -4
- package/dist/packages/@ember/component/index.js +4 -4
- package/dist/packages/@ember/controller/index.js +6 -6
- package/dist/packages/@ember/debug/container-debug-adapter.js +5 -4
- package/dist/packages/@ember/debug/data-adapter.js +7 -4
- package/dist/packages/@ember/debug/index.js +213 -4
- package/dist/packages/@ember/debug/lib/assert.js +47 -0
- package/dist/packages/@ember/debug/lib/deprecate.js +194 -4
- package/dist/packages/@ember/debug/lib/inspect.js +120 -2
- package/dist/packages/@ember/debug/lib/warn.js +94 -3
- package/dist/packages/@ember/engine/index.js +440 -17
- package/dist/packages/@ember/engine/instance.js +175 -11
- package/dist/packages/@ember/engine/parent.js +1 -0
- package/dist/packages/@ember/helper/index.js +4 -4
- package/dist/packages/@ember/instrumentation/index.js +2 -1
- package/dist/packages/@ember/modifier/index.js +13 -5
- package/dist/packages/@ember/modifier/on.js +15 -0
- package/dist/packages/@ember/object/-internals.js +6 -5
- package/dist/packages/@ember/object/compat.js +4 -3
- package/dist/packages/@ember/object/computed.js +4 -4
- package/dist/packages/@ember/object/core.js +861 -14
- package/dist/packages/@ember/object/evented.js +4 -4
- package/dist/packages/@ember/object/events.js +3 -3
- package/dist/packages/@ember/object/index.js +260 -9
- package/dist/packages/@ember/object/internals.js +1 -1
- package/dist/packages/@ember/object/lib/computed/computed_macros.js +8 -6
- package/dist/packages/@ember/object/lib/computed/reduce_computed_macros.js +8 -4
- package/dist/packages/@ember/object/mixin.js +6 -5
- package/dist/packages/@ember/object/observable.js +103 -9
- package/dist/packages/@ember/object/observers.js +3 -3
- package/dist/packages/@ember/object/promise-proxy-mixin.js +5 -5
- package/dist/packages/@ember/renderer/index.js +4 -4
- package/dist/packages/@ember/routing/-internals.js +3 -1
- package/dist/packages/@ember/routing/hash-location.js +2 -2
- package/dist/packages/@ember/routing/history-location.js +3 -2
- package/dist/packages/@ember/routing/index.js +4 -4
- package/dist/packages/@ember/routing/lib/dsl.js +2 -1
- package/dist/packages/@ember/routing/lib/generate_controller.js +4 -3
- package/dist/packages/@ember/routing/lib/router_state.js +26 -1
- package/dist/packages/@ember/routing/lib/routing-service.js +107 -9
- package/dist/packages/@ember/routing/lib/utils.js +238 -7
- package/dist/packages/@ember/routing/none-location.js +3 -2
- package/dist/packages/@ember/routing/route.js +1618 -22
- package/dist/packages/@ember/routing/router-service.js +638 -12
- package/dist/packages/@ember/routing/router.js +1449 -14
- package/dist/packages/@ember/runloop/index.js +760 -6
- package/dist/packages/@ember/service/index.js +3 -3
- package/dist/packages/@ember/template/index.js +4 -4
- package/dist/packages/@ember/utils/index.js +2 -1
- package/dist/packages/@ember/utils/lib/compare.js +159 -4
- package/dist/packages/@ember/utils/lib/is_empty.js +4 -4
- package/dist/packages/@ember/utils/lib/type-of.js +110 -1
- package/dist/packages/@glimmer/tracking/index.js +3 -3
- package/dist/packages/@glimmer/tracking/primitives/cache.js +3 -3
- package/dist/packages/ember/barrel.js +28 -13
- package/dist/packages/ember/version.js +1 -1
- package/dist/packages/ember-testing/lib/adapters/adapter.js +1 -1
- package/dist/packages/ember-testing/lib/adapters/qunit.js +2 -1
- package/dist/packages/ember-testing/lib/ext/application.js +2 -1
- package/dist/packages/ember-testing/lib/ext/rsvp.js +1 -1
- package/dist/packages/ember-testing/lib/helpers/and_then.js +2 -1
- package/dist/packages/ember-testing/lib/helpers/current_path.js +8 -6
- package/dist/packages/ember-testing/lib/helpers/current_route_name.js +8 -6
- package/dist/packages/ember-testing/lib/helpers/current_url.js +6 -5
- package/dist/packages/ember-testing/lib/helpers/pause_test.js +2 -1
- package/dist/packages/ember-testing/lib/helpers/visit.js +4 -3
- package/dist/packages/ember-testing/lib/helpers/wait.js +4 -3
- package/dist/packages/ember-testing/lib/initializers.js +15 -8
- package/dist/packages/ember-testing/lib/setup_for_testing.js +1 -1
- package/dist/packages/ember-testing/lib/test/run.js +1 -1
- package/dist/packages/router_js/index.js +2 -1
- package/dist/packages/shared-chunks/{alias-By_2yu5c.js → alias-Dri0koi2.js} +5 -3
- package/dist/packages/shared-chunks/array-3xbmc_4J.js +119 -0
- package/dist/packages/shared-chunks/{cache-gDE3bkXq.js → cache-BESCGvbE.js} +667 -1529
- package/dist/packages/shared-chunks/{core_view-Cxne2_wu.js → chunk-3SQBS3Y5-Cj4eryg1.js} +1 -88
- package/dist/packages/shared-chunks/{index-BXPoca1S.js → index-Llq6dmgX.js} +40 -4660
- package/dist/packages/shared-chunks/{is_proxy-Dmis-70B.js → is_proxy-DjvCKvd5.js} +1 -1
- package/dist/packages/shared-chunks/{mandatory-setter-1UQhiJOb.js → mandatory-setter-BiXq-dpN.js} +2 -1
- package/dist/packages/shared-chunks/{name-z9D9Yibn.js → name-Dx2bGFVv.js} +1 -1
- package/dist/packages/shared-chunks/{namespace_search-CBgHTkDh.js → namespace_search-btMaPM-_.js} +2 -2
- package/dist/packages/shared-chunks/{property_set-CW4q-uo4.js → property_set-BapAkp3X.js} +5 -4
- package/dist/packages/shared-chunks/{registry-DzfcDwii.js → registry-B8WARvkP.js} +3 -2
- package/dist/packages/shared-chunks/{router-B-Q1aYBn.js → router-DrLZsJeE.js} +2 -482
- package/dist/packages/shared-chunks/{set_properties-DvalyQdu.js → set_properties-BScfxzvI.js} +2 -2
- package/dist/packages/shared-chunks/setup-registry-du4pSGZi.js +48 -0
- package/dist/packages/shared-chunks/{to-string-D8i3mjEU.js → to-string-B1BmwUkt.js} +1 -1
- package/dist/packages/shared-chunks/unrecognized-url-error-zpz-JEoG.js +484 -0
- package/docs/data.json +152 -142
- package/package.json +2 -2
- package/types/stable/@ember/-internals/metal/lib/array.d.ts +1 -2
- package/types/stable/@ember/-internals/metal/lib/object-at.d.ts +4 -0
- package/types/stable/@ember/-internals/metal/lib/observer.d.ts +2 -1
- package/types/stable/@ember/array/index.d.ts +1 -1
- package/types/stable/@ember/array/make.d.ts +3 -0
- package/types/stable/@ember/debug/index.d.ts +3 -7
- package/types/stable/@ember/debug/lib/assert.d.ts +8 -0
- package/types/stable/@ember/engine/index.d.ts +1 -1
- package/types/stable/@ember/engine/instance.d.ts +2 -2
- package/types/stable/@ember/engine/parent.d.ts +3 -0
- package/types/stable/@ember/modifier/index.d.ts +1 -3
- package/types/stable/@ember/modifier/on.d.ts +5 -0
- package/types/stable/@ember/routing/lib/routing-service.d.ts +1 -1
- package/types/stable/@ember/routing/route.d.ts +2 -3
- package/types/stable/@ember/routing/router-service.d.ts +1 -1
- package/types/stable/@ember/routing/router.d.ts +4 -4
- package/types/stable/ember/barrel.d.ts +1 -1
- package/types/stable/ember/index.d.ts +1 -1
- package/types/stable/index.d.ts +5 -0
- package/dist/packages/shared-chunks/index-DTxy4Zgx.js +0 -641
- package/dist/packages/shared-chunks/index-PYiGj1jp.js +0 -2071
|
@@ -1,70 +1,65 @@
|
|
|
1
1
|
import { templateFactory, programCompilationContext } from '../@glimmer/opcode-compiler/index.js';
|
|
2
|
-
import { g as getFactoryFor, p as privatize
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { g as getFactoryFor, p as privatize } from './registry-B8WARvkP.js';
|
|
3
|
+
import { warn, debugFreeze, deprecate } from '../@ember/debug/index.js';
|
|
4
|
+
import { reifyPositional, normalizeProperty, EMPTY_ARGS, createCapturedArgs, EMPTY_POSITIONAL, curry, hash, array, concat, fn, get as get$1, templateOnlyComponent, TEMPLATE_ONLY_COMPONENT_MANAGER, on as on$1, runtimeContext, DOMTreeConstruction, DOMChanges, clientBuilder, inTransaction, renderMain } from '../@glimmer/runtime/index.js';
|
|
5
|
+
import { join, _backburner, schedule, _getCurrentRunLoop } from '../@ember/runloop/index.js';
|
|
6
6
|
import { valueForRef, isConstRef, createConstRef, isUpdatableRef, updateRef, createPrimitiveRef, childRefFor, createComputeRef, childRefFromParts, isInvokableRef, createUnboundRef, createInvokableRef, createReadOnlyRef, createDebugAliasRef, UNDEFINED_REFERENCE } from '../@glimmer/reference/index.js';
|
|
7
7
|
import { untrack, consumeTag, tagFor, createCache, getValue, valueForTag, beginUntrackFrame, endUntrackFrame, beginTrackFrame, endTrackFrame, validateTag, createTag, dirtyTag as DIRTY_TAG$1, CONSTANT_TAG, isTracking, debug, createUpdatableTag, CURRENT_TAG } from '../@glimmer/validator/index.js';
|
|
8
8
|
import { isDevelopingApp } from '@embroider/macros';
|
|
9
|
-
import {
|
|
9
|
+
import { setInternalComponentManager, setComponentTemplate, setInternalHelperManager, setHelperManager, getInternalHelperManager, helperCapabilities, capabilityFlagsFrom, setInternalModifierManager, getInternalComponentManager, getComponentTemplate } from '../@glimmer/manager/index.js';
|
|
10
10
|
import { h as hasDOM } from './index-BGP1rw3B.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { action as action$1 } from '../@ember/object/index.js';
|
|
12
|
+
import { on } from '../@ember/modifier/on.js';
|
|
13
13
|
import '../@ember/-internals/meta/lib/meta.js';
|
|
14
|
-
import { g as guidFor, a as getDebugName, e as isObject, u as uuid
|
|
14
|
+
import { g as guidFor, a as getDebugName, e as isObject, u as uuid } from './mandatory-setter-BiXq-dpN.js';
|
|
15
15
|
import { registerDestructor, associateDestroyableChild, destroy } from '../@glimmer/destroyable/index.js';
|
|
16
|
+
import { S as tracked, g as get, Q as PROPERTY_DID_CHANGE, t as tagForObject, o as objectAt, a as tagForProperty, V as _getProp } from './cache-BESCGvbE.js';
|
|
16
17
|
import { E as ENV } from './env-BJLX2Arx.js';
|
|
18
|
+
import { setOwner, getOwner, isFactory } from '../@ember/-internals/owner/index.js';
|
|
19
|
+
import { assert } from '../@ember/debug/lib/assert.js';
|
|
20
|
+
import { d as decorateFieldV2, i as initializeDeferredDecorator, a as decorateMethodV2 } from './chunk-3SQBS3Y5-Cj4eryg1.js';
|
|
21
|
+
import { isSimpleClick, getViewElement, clearElementView, clearViewElement, addChildView, setViewElement, setElementView, constructStyleDeprecationMessage, getViewId } from '../@ember/-internals/views/lib/system/utils.js';
|
|
17
22
|
import ActionManager from '../@ember/-internals/views/lib/system/action_manager.js';
|
|
18
|
-
import
|
|
19
|
-
import
|
|
23
|
+
import '../@ember/-internals/views/lib/component_lookup.js';
|
|
24
|
+
import CoreView from '../@ember/-internals/views/lib/views/core_view.js';
|
|
20
25
|
import ClassNamesSupport from '../@ember/-internals/views/lib/mixins/class_names_support.js';
|
|
21
26
|
import ChildViewsSupport from '../@ember/-internals/views/lib/mixins/child_views_support.js';
|
|
22
27
|
import ViewStateSupport from '../@ember/-internals/views/lib/mixins/view_state_support.js';
|
|
23
28
|
import ViewMixin from '../@ember/-internals/views/lib/mixins/view_support.js';
|
|
24
29
|
import ActionSupport from '../@ember/-internals/views/lib/mixins/action_support.js';
|
|
25
|
-
import { getEngineParent
|
|
26
|
-
import {
|
|
27
|
-
import
|
|
28
|
-
import
|
|
29
|
-
import
|
|
30
|
-
import
|
|
31
|
-
import RegistryProxyMixin from '../@ember/-internals/runtime/lib/mixins/registry_proxy.js';
|
|
32
|
-
import ContainerProxyMixin from '../@ember/-internals/runtime/lib/mixins/container_proxy.js';
|
|
30
|
+
import { getEngineParent } from '../@ember/engine/lib/engine-parent.js';
|
|
31
|
+
import { flaggedInstrument, _instrumentStart } from '../@ember/instrumentation/index.js';
|
|
32
|
+
import { service } from '../@ember/service/index.js';
|
|
33
|
+
import inspect from '../@ember/debug/lib/inspect.js';
|
|
34
|
+
import '../@ember/-internals/runtime/lib/mixins/registry_proxy.js';
|
|
35
|
+
import '../@ember/-internals/runtime/lib/mixins/container_proxy.js';
|
|
33
36
|
import '../@ember/-internals/runtime/lib/mixins/comparable.js';
|
|
34
|
-
import
|
|
37
|
+
import '../@ember/-internals/runtime/lib/mixins/action_handler.js';
|
|
35
38
|
import { contentFor } from '../@ember/-internals/runtime/lib/mixins/-proxy.js';
|
|
36
39
|
import '../@ember/enumerable/mutable.js';
|
|
37
40
|
import TargetActionSupport from '../@ember/-internals/runtime/lib/mixins/target_action_support.js';
|
|
38
41
|
import '../@ember/-internals/runtime/lib/ext/rsvp.js';
|
|
39
|
-
import { setOwner, getOwner, isFactory } from '../@ember/-internals/owner/index.js';
|
|
40
|
-
import { a as RSVP, R as RSVP$1 } from './rsvp-DaQAFb0W.js';
|
|
41
|
-
import { _ as _setProp, s as set } from './property_set-CW4q-uo4.js';
|
|
42
|
-
import '../route-recognizer/index.js';
|
|
43
|
-
import { S as STATE_SYMBOL, P as PARAMS_SYMBOL, R as Router, l as logAbort } from './router-B-Q1aYBn.js';
|
|
44
|
-
import { readOnly } from '../@ember/object/lib/computed/computed_macros.js';
|
|
45
|
-
import Service, { service } from '../@ember/service/index.js';
|
|
46
|
-
import { getOwner as getOwner$1 } from '../@ember/owner/index.js';
|
|
47
|
-
import BucketCache from '../@ember/routing/lib/cache.js';
|
|
48
|
-
import DSLImpl from '../@ember/routing/lib/dsl.js';
|
|
49
|
-
import Evented from '../@ember/object/evented.js';
|
|
50
|
-
import { i as isProxy } from './is_proxy-Dmis-70B.js';
|
|
51
|
-
import { g as getProperties, s as setProperties } from './set_properties-DvalyQdu.js';
|
|
52
|
-
import generateController, { generateControllerFactory } from '../@ember/routing/lib/generate_controller.js';
|
|
53
|
-
import { deprecateUntil, DEPRECATIONS } from '../@ember/-internals/deprecations/index.js';
|
|
54
|
-
import { dependentKeyCompat } from '../@ember/object/compat.js';
|
|
55
|
-
import { isTesting } from '../@ember/debug/lib/testing.js';
|
|
56
42
|
import EventDispatcher from '../@ember/-internals/views/lib/system/event_dispatcher.js';
|
|
57
|
-
import { e as enumerableSymbol } from './to-string-
|
|
58
|
-
import { flaggedInstrument, _instrumentStart } from '../@ember/instrumentation/index.js';
|
|
43
|
+
import { e as enumerableSymbol } from './to-string-B1BmwUkt.js';
|
|
59
44
|
import { unwrapTemplate, EMPTY_ARRAY as EMPTY_ARRAY$1, dict } from '../@glimmer/util/index.js';
|
|
60
45
|
import { dasherize } from '../@ember/-internals/string/index.js';
|
|
61
46
|
import { MUTABLE_CELL } from '../@ember/-internals/views/lib/compat/attrs.js';
|
|
47
|
+
import { deprecateUntil, DEPRECATIONS } from '../@ember/-internals/deprecations/index.js';
|
|
62
48
|
import { FrameworkObject } from '../@ember/object/-internals.js';
|
|
63
49
|
import { CurriedType as CurriedTypes } from '../@glimmer/vm/index.js';
|
|
64
50
|
import { RuntimeOpImpl, artifacts } from '../@glimmer/program/index.js';
|
|
65
|
-
import {
|
|
51
|
+
import { a as RSVP } from './rsvp-DaQAFb0W.js';
|
|
52
|
+
import EngineInstance from '../@ember/engine/instance.js';
|
|
53
|
+
import { NodeDOMTreeConstruction } from '../@glimmer/node/index.js';
|
|
54
|
+
import { _ as _setProp, s as set } from './property_set-BapAkp3X.js';
|
|
66
55
|
import setGlobalContext from '../@glimmer/global-context/index.js';
|
|
67
56
|
import { isEmberArray } from '../@ember/array/-internals.js';
|
|
57
|
+
import { i as isProxy } from './is_proxy-DjvCKvd5.js';
|
|
58
|
+
import { isArray } from '../@ember/array/index.js';
|
|
59
|
+
import '../route-recognizer/index.js';
|
|
60
|
+
import './unrecognized-url-error-zpz-JEoG.js';
|
|
61
|
+
import '../@ember/routing/lib/routing-service.js';
|
|
62
|
+
import { generateControllerFactory } from '../@ember/routing/lib/generate_controller.js';
|
|
68
63
|
|
|
69
64
|
const RootTemplate = templateFactory(
|
|
70
65
|
/*
|
|
@@ -77,22 +72,6 @@ const RootTemplate = templateFactory(
|
|
|
77
72
|
"isStrictMode": true
|
|
78
73
|
});
|
|
79
74
|
|
|
80
|
-
// In normal TypeScript, this modifier is essentially an opaque token that just
|
|
81
|
-
// needs to be importable. Declaring it with a unique interface like this,
|
|
82
|
-
// however, gives tools like Glint (that *do* have a richer notion of what it
|
|
83
|
-
// is) a place to install more detailed type information.
|
|
84
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
85
|
-
|
|
86
|
-
// SAFETY: at the time of writing, the cast here is from `{}` to `OnModifier`,
|
|
87
|
-
// which makes it strictly safer to use outside this module because it is not
|
|
88
|
-
// usable as "any non-null item", which is what `{}` means, without loss of any
|
|
89
|
-
// information from the type itself.
|
|
90
|
-
const on = on$1;
|
|
91
|
-
|
|
92
|
-
// NOTE: this uses assignment to *require* that the `glimmerSetModifierManager`
|
|
93
|
-
// is legally assignable to this type, i.e. that variance is properly upheld.
|
|
94
|
-
const setModifierManager = setModifierManager$1;
|
|
95
|
-
|
|
96
75
|
const InputTemplate = templateFactory(
|
|
97
76
|
/*
|
|
98
77
|
<input
|
|
@@ -1059,11 +1038,11 @@ class _LinkTo extends InternalComponent {
|
|
|
1059
1038
|
}
|
|
1060
1039
|
get isEngine() {
|
|
1061
1040
|
let owner = this.owner;
|
|
1062
|
-
return
|
|
1041
|
+
return getEngineParent(owner) !== undefined;
|
|
1063
1042
|
}
|
|
1064
1043
|
get engineMountPoint() {
|
|
1065
1044
|
let owner = this.owner;
|
|
1066
|
-
return owner
|
|
1045
|
+
return owner.mountPoint;
|
|
1067
1046
|
}
|
|
1068
1047
|
classFor(state) {
|
|
1069
1048
|
let className = this.named(`${state}Class`);
|
|
@@ -1800,7 +1779,6 @@ function invokeRef(value) {
|
|
|
1800
1779
|
function processComponentArgs(namedArgs) {
|
|
1801
1780
|
let attrs = Object.create(null);
|
|
1802
1781
|
let props = Object.create(null);
|
|
1803
|
-
props[ARGS] = namedArgs;
|
|
1804
1782
|
for (let name in namedArgs) {
|
|
1805
1783
|
let ref = namedArgs[name];
|
|
1806
1784
|
(isDevelopingApp() && !(ref) && assert('expected ref', ref));
|
|
@@ -1980,6 +1958,7 @@ class CurlyComponentManager {
|
|
|
1980
1958
|
let capturedArgs = args.named.capture();
|
|
1981
1959
|
beginTrackFrame();
|
|
1982
1960
|
let props = processComponentArgs(capturedArgs);
|
|
1961
|
+
props[ARGS] = capturedArgs;
|
|
1983
1962
|
let argsTag = endTrackFrame();
|
|
1984
1963
|
|
|
1985
1964
|
// Alias `id` argument to `elementId` property on the component instance.
|
|
@@ -3652,7 +3631,7 @@ class OutletComponentManager {
|
|
|
3652
3631
|
let parentOwner = parentState && parentState.render && parentState.render.owner;
|
|
3653
3632
|
let currentOwner = valueForRef(currentStateRef).render.owner;
|
|
3654
3633
|
if (parentOwner && parentOwner !== currentOwner) {
|
|
3655
|
-
(isDevelopingApp() && !(currentOwner instanceof
|
|
3634
|
+
(isDevelopingApp() && !(currentOwner instanceof EngineInstance) && assert('Expected currentOwner to be an EngineInstance', currentOwner instanceof EngineInstance));
|
|
3656
3635
|
let mountPoint = currentOwner.mountPoint;
|
|
3657
3636
|
state.engine = currentOwner;
|
|
3658
3637
|
if (mountPoint) {
|
|
@@ -4595,7 +4574,7 @@ class MountManager {
|
|
|
4595
4574
|
// mount is a runtime helper, this shouldn't use dynamic layout
|
|
4596
4575
|
// we should resolve the engine app template in the helper
|
|
4597
4576
|
// it also should use the owner that looked up the mount helper.
|
|
4598
|
-
(isDevelopingApp() && !(owner instanceof
|
|
4577
|
+
(isDevelopingApp() && !(owner instanceof EngineInstance) && assert('Expected owner to be an EngineInstance', owner instanceof EngineInstance));
|
|
4599
4578
|
let engine = owner.buildChildEngineInstance(name);
|
|
4600
4579
|
engine.boot();
|
|
4601
4580
|
let applicationFactory = engine.factoryFor(`controller:application`);
|
|
@@ -5515,4603 +5494,4 @@ const OutletTemplate = templateFactory(
|
|
|
5515
5494
|
"isStrictMode": true
|
|
5516
5495
|
});
|
|
5517
5496
|
|
|
5518
|
-
|
|
5519
|
-
// because we are using injections we can't use instantiate false
|
|
5520
|
-
// we need to use bind() to copy the function so factory for
|
|
5521
|
-
// association won't leak
|
|
5522
|
-
registry.register('service:-dom-builder', {
|
|
5523
|
-
// Additionally, we *must* constrain this to require `props` on create, else
|
|
5524
|
-
// we *know* it cannot have an owner.
|
|
5525
|
-
create(props) {
|
|
5526
|
-
let owner = getOwner(props);
|
|
5527
|
-
(isDevelopingApp() && !(owner) && assert('DomBuilderService is unexpectedly missing an owner', owner));
|
|
5528
|
-
let env = owner.lookup('-environment:main');
|
|
5529
|
-
switch (env._renderMode) {
|
|
5530
|
-
case 'serialize':
|
|
5531
|
-
return serializeBuilder.bind(null);
|
|
5532
|
-
case 'rehydrate':
|
|
5533
|
-
return rehydrationBuilder.bind(null);
|
|
5534
|
-
default:
|
|
5535
|
-
return clientBuilder.bind(null);
|
|
5536
|
-
}
|
|
5537
|
-
}
|
|
5538
|
-
});
|
|
5539
|
-
registry.register(privatize`template:-root`, RootTemplate);
|
|
5540
|
-
registry.register('renderer:-dom', Renderer);
|
|
5541
|
-
}
|
|
5542
|
-
function setupEngineRegistry(registry) {
|
|
5543
|
-
registry.optionsForType('template', {
|
|
5544
|
-
instantiate: false
|
|
5545
|
-
});
|
|
5546
|
-
registry.register('view:-outlet', OutletView);
|
|
5547
|
-
registry.register('template:-outlet', OutletTemplate);
|
|
5548
|
-
registry.optionsForType('helper', {
|
|
5549
|
-
instantiate: false
|
|
5550
|
-
});
|
|
5551
|
-
registry.register('component:input', Input);
|
|
5552
|
-
registry.register('component:link-to', LinkTo);
|
|
5553
|
-
registry.register('component:textarea', Textarea);
|
|
5554
|
-
}
|
|
5555
|
-
|
|
5556
|
-
function props(obj) {
|
|
5557
|
-
let properties = [];
|
|
5558
|
-
for (let key in obj) {
|
|
5559
|
-
properties.push(key);
|
|
5560
|
-
}
|
|
5561
|
-
return properties;
|
|
5562
|
-
}
|
|
5563
|
-
|
|
5564
|
-
/**
|
|
5565
|
-
@module @ember/engine
|
|
5566
|
-
*/
|
|
5567
|
-
|
|
5568
|
-
/**
|
|
5569
|
-
The `Engine` class contains core functionality for both applications and
|
|
5570
|
-
engines.
|
|
5571
|
-
|
|
5572
|
-
Each engine manages a registry that's used for dependency injection and
|
|
5573
|
-
exposed through `RegistryProxy`.
|
|
5574
|
-
|
|
5575
|
-
Engines also manage initializers and instance initializers.
|
|
5576
|
-
|
|
5577
|
-
Engines can spawn `EngineInstance` instances via `buildInstance()`.
|
|
5578
|
-
|
|
5579
|
-
@class Engine
|
|
5580
|
-
@extends Ember.Namespace
|
|
5581
|
-
@uses RegistryProxyMixin
|
|
5582
|
-
@public
|
|
5583
|
-
*/
|
|
5584
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
5585
|
-
|
|
5586
|
-
class Engine extends Namespace.extend(RegistryProxyMixin) {
|
|
5587
|
-
static initializers = Object.create(null);
|
|
5588
|
-
static instanceInitializers = Object.create(null);
|
|
5589
|
-
|
|
5590
|
-
/**
|
|
5591
|
-
The goal of initializers should be to register dependencies and injections.
|
|
5592
|
-
This phase runs once. Because these initializers may load code, they are
|
|
5593
|
-
allowed to defer application readiness and advance it. If you need to access
|
|
5594
|
-
the container or store you should use an InstanceInitializer that will be run
|
|
5595
|
-
after all initializers and therefore after all code is loaded and the app is
|
|
5596
|
-
ready.
|
|
5597
|
-
Initializer receives an object which has the following attributes:
|
|
5598
|
-
`name`, `before`, `after`, `initialize`. The only required attribute is
|
|
5599
|
-
`initialize`, all others are optional.
|
|
5600
|
-
* `name` allows you to specify under which name the initializer is registered.
|
|
5601
|
-
This must be a unique name, as trying to register two initializers with the
|
|
5602
|
-
same name will result in an error.
|
|
5603
|
-
```app/initializer/named-initializer.js
|
|
5604
|
-
import { debug } from '@ember/debug';
|
|
5605
|
-
export function initialize() {
|
|
5606
|
-
debug('Running namedInitializer!');
|
|
5607
|
-
}
|
|
5608
|
-
export default {
|
|
5609
|
-
name: 'named-initializer',
|
|
5610
|
-
initialize
|
|
5611
|
-
};
|
|
5612
|
-
```
|
|
5613
|
-
* `before` and `after` are used to ensure that this initializer is ran prior
|
|
5614
|
-
or after the one identified by the value. This value can be a single string
|
|
5615
|
-
or an array of strings, referencing the `name` of other initializers.
|
|
5616
|
-
An example of ordering initializers, we create an initializer named `first`:
|
|
5617
|
-
```app/initializer/first.js
|
|
5618
|
-
import { debug } from '@ember/debug';
|
|
5619
|
-
export function initialize() {
|
|
5620
|
-
debug('First initializer!');
|
|
5621
|
-
}
|
|
5622
|
-
export default {
|
|
5623
|
-
name: 'first',
|
|
5624
|
-
initialize
|
|
5625
|
-
};
|
|
5626
|
-
```
|
|
5627
|
-
```bash
|
|
5628
|
-
// DEBUG: First initializer!
|
|
5629
|
-
```
|
|
5630
|
-
We add another initializer named `second`, specifying that it should run
|
|
5631
|
-
after the initializer named `first`:
|
|
5632
|
-
```app/initializer/second.js
|
|
5633
|
-
import { debug } from '@ember/debug';
|
|
5634
|
-
export function initialize() {
|
|
5635
|
-
debug('Second initializer!');
|
|
5636
|
-
}
|
|
5637
|
-
export default {
|
|
5638
|
-
name: 'second',
|
|
5639
|
-
after: 'first',
|
|
5640
|
-
initialize
|
|
5641
|
-
};
|
|
5642
|
-
```
|
|
5643
|
-
```
|
|
5644
|
-
// DEBUG: First initializer!
|
|
5645
|
-
// DEBUG: Second initializer!
|
|
5646
|
-
```
|
|
5647
|
-
Afterwards we add a further initializer named `pre`, this time specifying
|
|
5648
|
-
that it should run before the initializer named `first`:
|
|
5649
|
-
```app/initializer/pre.js
|
|
5650
|
-
import { debug } from '@ember/debug';
|
|
5651
|
-
export function initialize() {
|
|
5652
|
-
debug('Pre initializer!');
|
|
5653
|
-
}
|
|
5654
|
-
export default {
|
|
5655
|
-
name: 'pre',
|
|
5656
|
-
before: 'first',
|
|
5657
|
-
initialize
|
|
5658
|
-
};
|
|
5659
|
-
```
|
|
5660
|
-
```bash
|
|
5661
|
-
// DEBUG: Pre initializer!
|
|
5662
|
-
// DEBUG: First initializer!
|
|
5663
|
-
// DEBUG: Second initializer!
|
|
5664
|
-
```
|
|
5665
|
-
Finally we add an initializer named `post`, specifying it should run after
|
|
5666
|
-
both the `first` and the `second` initializers:
|
|
5667
|
-
```app/initializer/post.js
|
|
5668
|
-
import { debug } from '@ember/debug';
|
|
5669
|
-
export function initialize() {
|
|
5670
|
-
debug('Post initializer!');
|
|
5671
|
-
}
|
|
5672
|
-
export default {
|
|
5673
|
-
name: 'post',
|
|
5674
|
-
after: ['first', 'second'],
|
|
5675
|
-
initialize
|
|
5676
|
-
};
|
|
5677
|
-
```
|
|
5678
|
-
```bash
|
|
5679
|
-
// DEBUG: Pre initializer!
|
|
5680
|
-
// DEBUG: First initializer!
|
|
5681
|
-
// DEBUG: Second initializer!
|
|
5682
|
-
// DEBUG: Post initializer!
|
|
5683
|
-
```
|
|
5684
|
-
* `initialize` is a callback function that receives one argument,
|
|
5685
|
-
`application`, on which you can operate.
|
|
5686
|
-
Example of using `application` to register an adapter:
|
|
5687
|
-
```app/initializer/api-adapter.js
|
|
5688
|
-
import ApiAdapter from '../utils/api-adapter';
|
|
5689
|
-
export function initialize(application) {
|
|
5690
|
-
application.register('api-adapter:main', ApiAdapter);
|
|
5691
|
-
}
|
|
5692
|
-
export default {
|
|
5693
|
-
name: 'post',
|
|
5694
|
-
after: ['first', 'second'],
|
|
5695
|
-
initialize
|
|
5696
|
-
};
|
|
5697
|
-
```
|
|
5698
|
-
@method initializer
|
|
5699
|
-
@param initializer {Object}
|
|
5700
|
-
@public
|
|
5701
|
-
*/
|
|
5702
|
-
|
|
5703
|
-
static initializer = buildInitializerMethod('initializers', 'initializer');
|
|
5704
|
-
|
|
5705
|
-
/**
|
|
5706
|
-
Instance initializers run after all initializers have run. Because
|
|
5707
|
-
instance initializers run after the app is fully set up. We have access
|
|
5708
|
-
to the store, container, and other items. However, these initializers run
|
|
5709
|
-
after code has loaded and are not allowed to defer readiness.
|
|
5710
|
-
Instance initializer receives an object which has the following attributes:
|
|
5711
|
-
`name`, `before`, `after`, `initialize`. The only required attribute is
|
|
5712
|
-
`initialize`, all others are optional.
|
|
5713
|
-
* `name` allows you to specify under which name the instanceInitializer is
|
|
5714
|
-
registered. This must be a unique name, as trying to register two
|
|
5715
|
-
instanceInitializer with the same name will result in an error.
|
|
5716
|
-
```app/initializer/named-instance-initializer.js
|
|
5717
|
-
import { debug } from '@ember/debug';
|
|
5718
|
-
export function initialize() {
|
|
5719
|
-
debug('Running named-instance-initializer!');
|
|
5720
|
-
}
|
|
5721
|
-
export default {
|
|
5722
|
-
name: 'named-instance-initializer',
|
|
5723
|
-
initialize
|
|
5724
|
-
};
|
|
5725
|
-
```
|
|
5726
|
-
* `before` and `after` are used to ensure that this initializer is ran prior
|
|
5727
|
-
or after the one identified by the value. This value can be a single string
|
|
5728
|
-
or an array of strings, referencing the `name` of other initializers.
|
|
5729
|
-
* See Application.initializer for discussion on the usage of before
|
|
5730
|
-
and after.
|
|
5731
|
-
Example instanceInitializer to preload data into the store.
|
|
5732
|
-
```app/initializer/preload-data.js
|
|
5733
|
-
export function initialize(application) {
|
|
5734
|
-
var userConfig, userConfigEncoded, store;
|
|
5735
|
-
// We have a HTML escaped JSON representation of the user's basic
|
|
5736
|
-
// configuration generated server side and stored in the DOM of the main
|
|
5737
|
-
// index.html file. This allows the app to have access to a set of data
|
|
5738
|
-
// without making any additional remote calls. Good for basic data that is
|
|
5739
|
-
// needed for immediate rendering of the page. Keep in mind, this data,
|
|
5740
|
-
// like all local models and data can be manipulated by the user, so it
|
|
5741
|
-
// should not be relied upon for security or authorization.
|
|
5742
|
-
// Grab the encoded data from the meta tag
|
|
5743
|
-
userConfigEncoded = document.querySelector('head meta[name=app-user-config]').attr('content');
|
|
5744
|
-
// Unescape the text, then parse the resulting JSON into a real object
|
|
5745
|
-
userConfig = JSON.parse(unescape(userConfigEncoded));
|
|
5746
|
-
// Lookup the store
|
|
5747
|
-
store = application.lookup('service:store');
|
|
5748
|
-
// Push the encoded JSON into the store
|
|
5749
|
-
store.pushPayload(userConfig);
|
|
5750
|
-
}
|
|
5751
|
-
export default {
|
|
5752
|
-
name: 'named-instance-initializer',
|
|
5753
|
-
initialize
|
|
5754
|
-
};
|
|
5755
|
-
```
|
|
5756
|
-
@method instanceInitializer
|
|
5757
|
-
@param instanceInitializer
|
|
5758
|
-
@public
|
|
5759
|
-
*/
|
|
5760
|
-
static instanceInitializer = buildInitializerMethod('instanceInitializers', 'instance initializer');
|
|
5761
|
-
|
|
5762
|
-
/**
|
|
5763
|
-
This creates a registry with the default Ember naming conventions.
|
|
5764
|
-
It also configures the registry:
|
|
5765
|
-
* registered views are created every time they are looked up (they are
|
|
5766
|
-
not singletons)
|
|
5767
|
-
* registered templates are not factories; the registered value is
|
|
5768
|
-
returned directly.
|
|
5769
|
-
* the router receives the application as its `namespace` property
|
|
5770
|
-
* all controllers receive the router as their `target` and `controllers`
|
|
5771
|
-
properties
|
|
5772
|
-
* all controllers receive the application as their `namespace` property
|
|
5773
|
-
* the application view receives the application controller as its
|
|
5774
|
-
`controller` property
|
|
5775
|
-
* the application view receives the application template as its
|
|
5776
|
-
`defaultTemplate` property
|
|
5777
|
-
@method buildRegistry
|
|
5778
|
-
@static
|
|
5779
|
-
@param {Application} namespace the application for which to
|
|
5780
|
-
build the registry
|
|
5781
|
-
@return {Ember.Registry} the built registry
|
|
5782
|
-
@private
|
|
5783
|
-
*/
|
|
5784
|
-
static buildRegistry(namespace) {
|
|
5785
|
-
let registry = new Registry({
|
|
5786
|
-
resolver: resolverFor(namespace)
|
|
5787
|
-
});
|
|
5788
|
-
registry.set = set;
|
|
5789
|
-
registry.register('application:main', namespace, {
|
|
5790
|
-
instantiate: false
|
|
5791
|
-
});
|
|
5792
|
-
commonSetupRegistry(registry);
|
|
5793
|
-
setupEngineRegistry(registry);
|
|
5794
|
-
return registry;
|
|
5795
|
-
}
|
|
5796
|
-
|
|
5797
|
-
/**
|
|
5798
|
-
Set this to provide an alternate class to `DefaultResolver`
|
|
5799
|
-
@property resolver
|
|
5800
|
-
@public
|
|
5801
|
-
*/
|
|
5802
|
-
|
|
5803
|
-
init(properties) {
|
|
5804
|
-
super.init(properties);
|
|
5805
|
-
this.buildRegistry();
|
|
5806
|
-
}
|
|
5807
|
-
|
|
5808
|
-
/**
|
|
5809
|
-
A private flag indicating whether an engine's initializers have run yet.
|
|
5810
|
-
@private
|
|
5811
|
-
@property _initializersRan
|
|
5812
|
-
*/
|
|
5813
|
-
_initializersRan = false;
|
|
5814
|
-
|
|
5815
|
-
/**
|
|
5816
|
-
Ensure that initializers are run once, and only once, per engine.
|
|
5817
|
-
@private
|
|
5818
|
-
@method ensureInitializers
|
|
5819
|
-
*/
|
|
5820
|
-
ensureInitializers() {
|
|
5821
|
-
if (!this._initializersRan) {
|
|
5822
|
-
this.runInitializers();
|
|
5823
|
-
this._initializersRan = true;
|
|
5824
|
-
}
|
|
5825
|
-
}
|
|
5826
|
-
|
|
5827
|
-
/**
|
|
5828
|
-
Create an EngineInstance for this engine.
|
|
5829
|
-
@public
|
|
5830
|
-
@method buildInstance
|
|
5831
|
-
@return {EngineInstance} the engine instance
|
|
5832
|
-
*/
|
|
5833
|
-
buildInstance(options = {}) {
|
|
5834
|
-
this.ensureInitializers();
|
|
5835
|
-
return EmberEngineInstance.create({
|
|
5836
|
-
...options,
|
|
5837
|
-
base: this
|
|
5838
|
-
});
|
|
5839
|
-
}
|
|
5840
|
-
|
|
5841
|
-
/**
|
|
5842
|
-
Build and configure the registry for the current engine.
|
|
5843
|
-
@private
|
|
5844
|
-
@method buildRegistry
|
|
5845
|
-
@return {Ember.Registry} the configured registry
|
|
5846
|
-
*/
|
|
5847
|
-
buildRegistry() {
|
|
5848
|
-
let registry = this.__registry__ = this.constructor.buildRegistry(this);
|
|
5849
|
-
return registry;
|
|
5850
|
-
}
|
|
5851
|
-
|
|
5852
|
-
/**
|
|
5853
|
-
@private
|
|
5854
|
-
@method initializer
|
|
5855
|
-
*/
|
|
5856
|
-
initializer(initializer) {
|
|
5857
|
-
this.constructor.initializer(initializer);
|
|
5858
|
-
}
|
|
5859
|
-
|
|
5860
|
-
/**
|
|
5861
|
-
@private
|
|
5862
|
-
@method instanceInitializer
|
|
5863
|
-
*/
|
|
5864
|
-
instanceInitializer(initializer) {
|
|
5865
|
-
this.constructor.instanceInitializer(initializer);
|
|
5866
|
-
}
|
|
5867
|
-
|
|
5868
|
-
/**
|
|
5869
|
-
@private
|
|
5870
|
-
@method runInitializers
|
|
5871
|
-
*/
|
|
5872
|
-
runInitializers() {
|
|
5873
|
-
this._runInitializer('initializers', (name, initializer) => {
|
|
5874
|
-
(isDevelopingApp() && !(initializer) && assert(`No application initializer named '${name}'`, initializer));
|
|
5875
|
-
initializer.initialize(this);
|
|
5876
|
-
});
|
|
5877
|
-
}
|
|
5878
|
-
|
|
5879
|
-
/**
|
|
5880
|
-
@private
|
|
5881
|
-
@since 1.12.0
|
|
5882
|
-
@method runInstanceInitializers
|
|
5883
|
-
*/
|
|
5884
|
-
runInstanceInitializers(instance) {
|
|
5885
|
-
this._runInitializer('instanceInitializers', (name, initializer) => {
|
|
5886
|
-
(isDevelopingApp() && !(initializer) && assert(`No instance initializer named '${name}'`, initializer));
|
|
5887
|
-
initializer.initialize(instance);
|
|
5888
|
-
});
|
|
5889
|
-
}
|
|
5890
|
-
_runInitializer(bucketName, cb) {
|
|
5891
|
-
let initializersByName = get(this.constructor, bucketName);
|
|
5892
|
-
let initializers = props(initializersByName);
|
|
5893
|
-
let graph = new DAG();
|
|
5894
|
-
let initializer;
|
|
5895
|
-
for (let name of initializers) {
|
|
5896
|
-
initializer = initializersByName[name];
|
|
5897
|
-
(isDevelopingApp() && !(initializer) && assert(`missing ${bucketName}: ${name}`, initializer));
|
|
5898
|
-
graph.add(initializer.name, initializer, initializer.before, initializer.after);
|
|
5899
|
-
}
|
|
5900
|
-
graph.topsort(cb);
|
|
5901
|
-
}
|
|
5902
|
-
}
|
|
5903
|
-
|
|
5904
|
-
/**
|
|
5905
|
-
This function defines the default lookup rules for container lookups:
|
|
5906
|
-
|
|
5907
|
-
* templates are looked up on `Ember.TEMPLATES`
|
|
5908
|
-
* other names are looked up on the application after classifying the name.
|
|
5909
|
-
For example, `controller:post` looks up `App.PostController` by default.
|
|
5910
|
-
* if the default lookup fails, look for registered classes on the container
|
|
5911
|
-
|
|
5912
|
-
This allows the application to register default injections in the container
|
|
5913
|
-
that could be overridden by the normal naming convention.
|
|
5914
|
-
|
|
5915
|
-
@private
|
|
5916
|
-
@method resolverFor
|
|
5917
|
-
@param {Ember.Enginer} namespace the namespace to look for classes
|
|
5918
|
-
@return {*} the resolved value for a given lookup
|
|
5919
|
-
*/
|
|
5920
|
-
function resolverFor(namespace) {
|
|
5921
|
-
let ResolverClass = namespace.Resolver;
|
|
5922
|
-
let props = {
|
|
5923
|
-
namespace
|
|
5924
|
-
};
|
|
5925
|
-
return ResolverClass.create(props);
|
|
5926
|
-
}
|
|
5927
|
-
|
|
5928
|
-
/** @internal */
|
|
5929
|
-
function buildInitializerMethod(bucketName, humanName) {
|
|
5930
|
-
return function (initializer) {
|
|
5931
|
-
// If this is the first initializer being added to a subclass, we are going to reopen the class
|
|
5932
|
-
// to make sure we have a new `initializers` object, which extends from the parent class' using
|
|
5933
|
-
// prototypal inheritance. Without this, attempting to add initializers to the subclass would
|
|
5934
|
-
// pollute the parent class as well as other subclasses.
|
|
5935
|
-
// SAFETY: The superclass may be an Engine, we don't call unless we confirmed it was ok.
|
|
5936
|
-
let superclass = this.superclass;
|
|
5937
|
-
if (superclass[bucketName] !== undefined && superclass[bucketName] === this[bucketName]) {
|
|
5938
|
-
let attrs = {
|
|
5939
|
-
[bucketName]: Object.create(this[bucketName])
|
|
5940
|
-
};
|
|
5941
|
-
this.reopenClass(attrs);
|
|
5942
|
-
}
|
|
5943
|
-
(isDevelopingApp() && !(!this[bucketName][initializer.name]) && assert(`The ${humanName} '${initializer.name}' has already been registered`, !this[bucketName][initializer.name]));
|
|
5944
|
-
(isDevelopingApp() && !(canInvoke(initializer, 'initialize')) && assert(`An ${humanName} cannot be registered without an initialize function`, canInvoke(initializer, 'initialize')));
|
|
5945
|
-
(isDevelopingApp() && !(initializer.name !== undefined) && assert(`An ${humanName} cannot be registered without a name property`, initializer.name !== undefined));
|
|
5946
|
-
let initializers = this[bucketName];
|
|
5947
|
-
initializers[initializer.name] = initializer;
|
|
5948
|
-
};
|
|
5949
|
-
}
|
|
5950
|
-
function commonSetupRegistry(registry) {
|
|
5951
|
-
registry.optionsForType('component', {
|
|
5952
|
-
singleton: false
|
|
5953
|
-
});
|
|
5954
|
-
registry.optionsForType('view', {
|
|
5955
|
-
singleton: false
|
|
5956
|
-
});
|
|
5957
|
-
registry.register('controller:basic', Controller, {
|
|
5958
|
-
instantiate: false
|
|
5959
|
-
});
|
|
5960
|
-
|
|
5961
|
-
// Register the routing service...
|
|
5962
|
-
registry.register('service:-routing', RoutingService);
|
|
5963
|
-
|
|
5964
|
-
// DEBUGGING
|
|
5965
|
-
registry.register('resolver-for-debugging:main', registry.resolver, {
|
|
5966
|
-
instantiate: false
|
|
5967
|
-
});
|
|
5968
|
-
registry.register('container-debug-adapter:main', ContainerDebugAdapter);
|
|
5969
|
-
registry.register('component-lookup:main', ComponentLookup);
|
|
5970
|
-
}
|
|
5971
|
-
|
|
5972
|
-
/**
|
|
5973
|
-
@module @ember/engine
|
|
5974
|
-
*/
|
|
5975
|
-
|
|
5976
|
-
class EngineInstance extends EmberObject.extend(RegistryProxyMixin, ContainerProxyMixin) {
|
|
5977
|
-
/**
|
|
5978
|
-
@private
|
|
5979
|
-
@method setupRegistry
|
|
5980
|
-
@param {Registry} registry
|
|
5981
|
-
@param {BootOptions} options
|
|
5982
|
-
*/
|
|
5983
|
-
// This is effectively an "abstract" method: it defines the contract a
|
|
5984
|
-
// subclass (e.g. `ApplicationInstance`) must follow to implement this
|
|
5985
|
-
// behavior, but an `EngineInstance` has no behavior of its own here.
|
|
5986
|
-
static setupRegistry(_registry, _options) {}
|
|
5987
|
-
|
|
5988
|
-
/**
|
|
5989
|
-
The base `Engine` for which this is an instance.
|
|
5990
|
-
@property {Engine} engine
|
|
5991
|
-
@private
|
|
5992
|
-
*/
|
|
5993
|
-
|
|
5994
|
-
[ENGINE_PARENT];
|
|
5995
|
-
_booted = false;
|
|
5996
|
-
init(properties) {
|
|
5997
|
-
super.init(properties);
|
|
5998
|
-
|
|
5999
|
-
// Ensure the guid gets setup for this instance
|
|
6000
|
-
guidFor(this);
|
|
6001
|
-
this.base ??= this.application;
|
|
6002
|
-
|
|
6003
|
-
// Create a per-instance registry that will use the application's registry
|
|
6004
|
-
// as a fallback for resolving registrations.
|
|
6005
|
-
let registry = this.__registry__ = new Registry({
|
|
6006
|
-
fallback: this.base.__registry__
|
|
6007
|
-
});
|
|
6008
|
-
|
|
6009
|
-
// Create a per-instance container from the instance's registry
|
|
6010
|
-
this.__container__ = registry.container({
|
|
6011
|
-
owner: this
|
|
6012
|
-
});
|
|
6013
|
-
this._booted = false;
|
|
6014
|
-
}
|
|
6015
|
-
_bootPromise = null;
|
|
6016
|
-
|
|
6017
|
-
/**
|
|
6018
|
-
Initialize the `EngineInstance` and return a promise that resolves
|
|
6019
|
-
with the instance itself when the boot process is complete.
|
|
6020
|
-
The primary task here is to run any registered instance initializers.
|
|
6021
|
-
See the documentation on `BootOptions` for the options it takes.
|
|
6022
|
-
@public
|
|
6023
|
-
@method boot
|
|
6024
|
-
@param options {Object}
|
|
6025
|
-
@return {Promise<EngineInstance,Error>}
|
|
6026
|
-
*/
|
|
6027
|
-
boot(options) {
|
|
6028
|
-
if (this._bootPromise) {
|
|
6029
|
-
return this._bootPromise;
|
|
6030
|
-
}
|
|
6031
|
-
this._bootPromise = new RSVP$1.Promise(resolve => {
|
|
6032
|
-
resolve(this._bootSync(options));
|
|
6033
|
-
});
|
|
6034
|
-
return this._bootPromise;
|
|
6035
|
-
}
|
|
6036
|
-
|
|
6037
|
-
/**
|
|
6038
|
-
Unfortunately, a lot of existing code assumes booting an instance is
|
|
6039
|
-
synchronous – specifically, a lot of tests assume the last call to
|
|
6040
|
-
`app.advanceReadiness()` or `app.reset()` will result in a new instance
|
|
6041
|
-
being fully-booted when the current runloop completes.
|
|
6042
|
-
We would like new code (like the `visit` API) to stop making this
|
|
6043
|
-
assumption, so we created the asynchronous version above that returns a
|
|
6044
|
-
promise. But until we have migrated all the code, we would have to expose
|
|
6045
|
-
this method for use *internally* in places where we need to boot an instance
|
|
6046
|
-
synchronously.
|
|
6047
|
-
@private
|
|
6048
|
-
*/
|
|
6049
|
-
_bootSync(options) {
|
|
6050
|
-
if (this._booted) {
|
|
6051
|
-
return this;
|
|
6052
|
-
}
|
|
6053
|
-
(isDevelopingApp() && !(getEngineParent(this)) && assert("An engine instance's parent must be set via `setEngineParent(engine, parent)` prior to calling `engine.boot()`.", getEngineParent(this)));
|
|
6054
|
-
this.cloneParentDependencies();
|
|
6055
|
-
this.setupRegistry(options);
|
|
6056
|
-
this.base.runInstanceInitializers(this);
|
|
6057
|
-
this._booted = true;
|
|
6058
|
-
return this;
|
|
6059
|
-
}
|
|
6060
|
-
setupRegistry(options = this.__container__.lookup('-environment:main')) {
|
|
6061
|
-
this.constructor.setupRegistry(this.__registry__, options);
|
|
6062
|
-
}
|
|
6063
|
-
|
|
6064
|
-
/**
|
|
6065
|
-
Unregister a factory.
|
|
6066
|
-
Overrides `RegistryProxy#unregister` in order to clear any cached instances
|
|
6067
|
-
of the unregistered factory.
|
|
6068
|
-
@public
|
|
6069
|
-
@method unregister
|
|
6070
|
-
@param {String} fullName
|
|
6071
|
-
*/
|
|
6072
|
-
unregister(fullName) {
|
|
6073
|
-
this.__container__.reset(fullName);
|
|
6074
|
-
|
|
6075
|
-
// We overwrote this method from RegistryProxyMixin.
|
|
6076
|
-
this.__registry__.unregister(fullName);
|
|
6077
|
-
}
|
|
6078
|
-
|
|
6079
|
-
/**
|
|
6080
|
-
Build a new `EngineInstance` that's a child of this instance.
|
|
6081
|
-
Engines must be registered by name with their parent engine
|
|
6082
|
-
(or application).
|
|
6083
|
-
@private
|
|
6084
|
-
@method buildChildEngineInstance
|
|
6085
|
-
@param name {String} the registered name of the engine.
|
|
6086
|
-
@param options {Object} options provided to the engine instance.
|
|
6087
|
-
@return {EngineInstance,Error}
|
|
6088
|
-
*/
|
|
6089
|
-
buildChildEngineInstance(name, options = {}) {
|
|
6090
|
-
let ChildEngine = this.lookup(`engine:${name}`);
|
|
6091
|
-
if (!ChildEngine) {
|
|
6092
|
-
throw new Error(`You attempted to mount the engine '${name}', but it is not registered with its parent.`);
|
|
6093
|
-
}
|
|
6094
|
-
(isDevelopingApp() && !(ChildEngine instanceof Engine) && assert('expected an Engine', ChildEngine instanceof Engine));
|
|
6095
|
-
let engineInstance = ChildEngine.buildInstance(options);
|
|
6096
|
-
setEngineParent(engineInstance, this);
|
|
6097
|
-
return engineInstance;
|
|
6098
|
-
}
|
|
6099
|
-
|
|
6100
|
-
/**
|
|
6101
|
-
Clone dependencies shared between an engine instance and its parent.
|
|
6102
|
-
@private
|
|
6103
|
-
@method cloneParentDependencies
|
|
6104
|
-
*/
|
|
6105
|
-
cloneParentDependencies() {
|
|
6106
|
-
const parent = getEngineParent(this);
|
|
6107
|
-
(isDevelopingApp() && !(parent) && assert('expected parent', parent));
|
|
6108
|
-
let registrations = ['route:basic', 'service:-routing'];
|
|
6109
|
-
registrations.forEach(key => {
|
|
6110
|
-
let registration = parent.resolveRegistration(key);
|
|
6111
|
-
(isDevelopingApp() && !(isFactory(registration)) && assert('expected registration to be a factory', isFactory(registration)));
|
|
6112
|
-
this.register(key, registration);
|
|
6113
|
-
});
|
|
6114
|
-
let env = parent.lookup('-environment:main');
|
|
6115
|
-
this.register('-environment:main', env, {
|
|
6116
|
-
instantiate: false
|
|
6117
|
-
});
|
|
6118
|
-
|
|
6119
|
-
// The type annotation forces TS to (a) validate that these match and (b)
|
|
6120
|
-
// *notice* that they match, e.g. below on the `singletons.push()`.
|
|
6121
|
-
let singletons = ['router:main', privatize`-bucket-cache:main`, '-view-registry:main', `renderer:-dom`, 'service:-document'];
|
|
6122
|
-
if (env['isInteractive']) {
|
|
6123
|
-
singletons.push('event_dispatcher:main');
|
|
6124
|
-
}
|
|
6125
|
-
singletons.forEach(key => {
|
|
6126
|
-
// SAFETY: We already expect this to be a singleton
|
|
6127
|
-
let singleton = parent.lookup(key);
|
|
6128
|
-
this.register(key, singleton, {
|
|
6129
|
-
instantiate: false
|
|
6130
|
-
});
|
|
6131
|
-
});
|
|
6132
|
-
}
|
|
6133
|
-
}
|
|
6134
|
-
const EmberEngineInstance = EngineInstance;
|
|
6135
|
-
|
|
6136
|
-
const ALL_PERIODS_REGEX = /\./g;
|
|
6137
|
-
function extractRouteArgs(args) {
|
|
6138
|
-
// SAFETY: This should just be the same thing
|
|
6139
|
-
args = args.slice();
|
|
6140
|
-
let possibleOptions = args[args.length - 1];
|
|
6141
|
-
let queryParams;
|
|
6142
|
-
if (isRouteOptions(possibleOptions)) {
|
|
6143
|
-
args.pop(); // Remove options
|
|
6144
|
-
queryParams = possibleOptions.queryParams;
|
|
6145
|
-
} else {
|
|
6146
|
-
queryParams = {};
|
|
6147
|
-
}
|
|
6148
|
-
let routeName;
|
|
6149
|
-
if (typeof args[0] === 'string') {
|
|
6150
|
-
routeName = args.shift();
|
|
6151
|
-
// We just checked this!
|
|
6152
|
-
(isDevelopingApp() && !(typeof routeName === 'string') && assert('routeName is a string', typeof routeName === 'string'));
|
|
6153
|
-
}
|
|
6154
|
-
|
|
6155
|
-
// SAFTEY: We removed the name and options if they existed, only models left.
|
|
6156
|
-
let models = args;
|
|
6157
|
-
return {
|
|
6158
|
-
routeName,
|
|
6159
|
-
models,
|
|
6160
|
-
queryParams
|
|
6161
|
-
};
|
|
6162
|
-
}
|
|
6163
|
-
function getActiveTargetName(router) {
|
|
6164
|
-
let routeInfos = router.activeTransition ? router.activeTransition[STATE_SYMBOL].routeInfos : router.state.routeInfos;
|
|
6165
|
-
let lastRouteInfo = routeInfos[routeInfos.length - 1];
|
|
6166
|
-
(isDevelopingApp() && !(lastRouteInfo) && assert('has last route info', lastRouteInfo));
|
|
6167
|
-
return lastRouteInfo.name;
|
|
6168
|
-
}
|
|
6169
|
-
function stashParamNames(router, routeInfos) {
|
|
6170
|
-
if (routeInfos['_namesStashed']) {
|
|
6171
|
-
return;
|
|
6172
|
-
}
|
|
6173
|
-
|
|
6174
|
-
// This helper exists because router.js/route-recognizer.js awkwardly
|
|
6175
|
-
// keeps separate a routeInfo's list of parameter names depending
|
|
6176
|
-
// on whether a URL transition or named transition is happening.
|
|
6177
|
-
// Hopefully we can remove this in the future.
|
|
6178
|
-
let routeInfo = routeInfos[routeInfos.length - 1];
|
|
6179
|
-
(isDevelopingApp() && !(routeInfo) && assert('has route info', routeInfo));
|
|
6180
|
-
let targetRouteName = routeInfo.name;
|
|
6181
|
-
let recogHandlers = router._routerMicrolib.recognizer.handlersFor(targetRouteName);
|
|
6182
|
-
let dynamicParent;
|
|
6183
|
-
for (let i = 0; i < routeInfos.length; ++i) {
|
|
6184
|
-
let routeInfo = routeInfos[i];
|
|
6185
|
-
(isDevelopingApp() && !(routeInfo) && assert('has route info', routeInfo));
|
|
6186
|
-
let names = recogHandlers[i].names;
|
|
6187
|
-
if (names.length) {
|
|
6188
|
-
dynamicParent = routeInfo;
|
|
6189
|
-
}
|
|
6190
|
-
routeInfo['_names'] = names;
|
|
6191
|
-
let route = routeInfo.route;
|
|
6192
|
-
route._stashNames(routeInfo, dynamicParent);
|
|
6193
|
-
}
|
|
6194
|
-
routeInfos['_namesStashed'] = true;
|
|
6195
|
-
}
|
|
6196
|
-
function _calculateCacheValuePrefix(prefix, part) {
|
|
6197
|
-
// calculates the dot separated sections from prefix that are also
|
|
6198
|
-
// at the start of part - which gives us the route name
|
|
6199
|
-
|
|
6200
|
-
// given : prefix = site.article.comments, part = site.article.id
|
|
6201
|
-
// - returns: site.article (use get(values[site.article], 'id') to get the dynamic part - used below)
|
|
6202
|
-
|
|
6203
|
-
// given : prefix = site.article, part = site.article.id
|
|
6204
|
-
// - returns: site.article. (use get(values[site.article], 'id') to get the dynamic part - used below)
|
|
6205
|
-
|
|
6206
|
-
let prefixParts = prefix.split('.');
|
|
6207
|
-
let currPrefix = '';
|
|
6208
|
-
for (let i = 0; i < prefixParts.length; i++) {
|
|
6209
|
-
let currPart = prefixParts.slice(0, i + 1).join('.');
|
|
6210
|
-
if (part.indexOf(currPart) !== 0) {
|
|
6211
|
-
break;
|
|
6212
|
-
}
|
|
6213
|
-
currPrefix = currPart;
|
|
6214
|
-
}
|
|
6215
|
-
return currPrefix;
|
|
6216
|
-
}
|
|
6217
|
-
|
|
6218
|
-
/*
|
|
6219
|
-
Stolen from Controller
|
|
6220
|
-
*/
|
|
6221
|
-
function calculateCacheKey(prefix, parts = [], values) {
|
|
6222
|
-
let suffixes = '';
|
|
6223
|
-
for (let part of parts) {
|
|
6224
|
-
let cacheValuePrefix = _calculateCacheValuePrefix(prefix, part);
|
|
6225
|
-
let value;
|
|
6226
|
-
if (values) {
|
|
6227
|
-
if (cacheValuePrefix && cacheValuePrefix in values) {
|
|
6228
|
-
let partRemovedPrefix = part.indexOf(cacheValuePrefix) === 0 ? part.substring(cacheValuePrefix.length + 1) : part;
|
|
6229
|
-
value = get(values[cacheValuePrefix], partRemovedPrefix);
|
|
6230
|
-
} else {
|
|
6231
|
-
value = get(values, part);
|
|
6232
|
-
}
|
|
6233
|
-
}
|
|
6234
|
-
suffixes += `::${part}:${value}`;
|
|
6235
|
-
}
|
|
6236
|
-
return prefix + suffixes.replace(ALL_PERIODS_REGEX, '-');
|
|
6237
|
-
}
|
|
6238
|
-
|
|
6239
|
-
/*
|
|
6240
|
-
Controller-defined query parameters can come in three shapes:
|
|
6241
|
-
|
|
6242
|
-
Array
|
|
6243
|
-
queryParams: ['foo', 'bar']
|
|
6244
|
-
Array of simple objects where value is an alias
|
|
6245
|
-
queryParams: [
|
|
6246
|
-
{
|
|
6247
|
-
'foo': 'rename_foo_to_this'
|
|
6248
|
-
},
|
|
6249
|
-
{
|
|
6250
|
-
'bar': 'call_bar_this_instead'
|
|
6251
|
-
}
|
|
6252
|
-
]
|
|
6253
|
-
Array of fully defined objects
|
|
6254
|
-
queryParams: [
|
|
6255
|
-
{
|
|
6256
|
-
'foo': {
|
|
6257
|
-
as: 'rename_foo_to_this'
|
|
6258
|
-
},
|
|
6259
|
-
}
|
|
6260
|
-
{
|
|
6261
|
-
'bar': {
|
|
6262
|
-
as: 'call_bar_this_instead',
|
|
6263
|
-
scope: 'controller'
|
|
6264
|
-
}
|
|
6265
|
-
}
|
|
6266
|
-
]
|
|
6267
|
-
|
|
6268
|
-
This helper normalizes all three possible styles into the
|
|
6269
|
-
'Array of fully defined objects' style.
|
|
6270
|
-
*/
|
|
6271
|
-
function normalizeControllerQueryParams(queryParams) {
|
|
6272
|
-
let qpMap = {};
|
|
6273
|
-
for (let queryParam of queryParams) {
|
|
6274
|
-
accumulateQueryParamDescriptors(queryParam, qpMap);
|
|
6275
|
-
}
|
|
6276
|
-
return qpMap;
|
|
6277
|
-
}
|
|
6278
|
-
function accumulateQueryParamDescriptors(_desc, accum) {
|
|
6279
|
-
let desc = typeof _desc === 'string' ? {
|
|
6280
|
-
[_desc]: {
|
|
6281
|
-
as: null
|
|
6282
|
-
}
|
|
6283
|
-
} : _desc;
|
|
6284
|
-
for (let key in desc) {
|
|
6285
|
-
if (!Object.prototype.hasOwnProperty.call(desc, key)) {
|
|
6286
|
-
return;
|
|
6287
|
-
}
|
|
6288
|
-
let _singleDesc = desc[key];
|
|
6289
|
-
let singleDesc = typeof _singleDesc === 'string' ? {
|
|
6290
|
-
as: _singleDesc
|
|
6291
|
-
} : _singleDesc;
|
|
6292
|
-
let partialVal = accum[key] || {
|
|
6293
|
-
as: null,
|
|
6294
|
-
scope: 'model'
|
|
6295
|
-
};
|
|
6296
|
-
let val = {
|
|
6297
|
-
...partialVal,
|
|
6298
|
-
...singleDesc
|
|
6299
|
-
};
|
|
6300
|
-
accum[key] = val;
|
|
6301
|
-
}
|
|
6302
|
-
}
|
|
6303
|
-
|
|
6304
|
-
/*
|
|
6305
|
-
Check if a routeName resembles a url instead
|
|
6306
|
-
|
|
6307
|
-
@private
|
|
6308
|
-
*/
|
|
6309
|
-
function resemblesURL(str) {
|
|
6310
|
-
return typeof str === 'string' && (str === '' || str[0] === '/');
|
|
6311
|
-
}
|
|
6312
|
-
|
|
6313
|
-
/*
|
|
6314
|
-
Returns an arguments array where the route name arg is prefixed based on the mount point
|
|
6315
|
-
|
|
6316
|
-
@private
|
|
6317
|
-
*/
|
|
6318
|
-
function prefixRouteNameArg(route, args) {
|
|
6319
|
-
let routeName;
|
|
6320
|
-
let owner = getOwner(route);
|
|
6321
|
-
(isDevelopingApp() && !(owner instanceof EmberEngineInstance) && assert('Expected route to have EngineInstance as owner', owner instanceof EmberEngineInstance));
|
|
6322
|
-
let prefix = owner.mountPoint;
|
|
6323
|
-
|
|
6324
|
-
// only alter the routeName if it's actually referencing a route.
|
|
6325
|
-
if (owner.routable && typeof args[0] === 'string') {
|
|
6326
|
-
routeName = args[0];
|
|
6327
|
-
if (resemblesURL(routeName)) {
|
|
6328
|
-
throw new Error('Programmatic transitions by URL cannot be used within an Engine. Please use the route name instead.');
|
|
6329
|
-
} else {
|
|
6330
|
-
routeName = `${prefix}.${routeName}`;
|
|
6331
|
-
args[0] = routeName;
|
|
6332
|
-
}
|
|
6333
|
-
}
|
|
6334
|
-
return args;
|
|
6335
|
-
}
|
|
6336
|
-
function shallowEqual(a, b) {
|
|
6337
|
-
let aCount = 0;
|
|
6338
|
-
let bCount = 0;
|
|
6339
|
-
for (let kA in a) {
|
|
6340
|
-
if (Object.prototype.hasOwnProperty.call(a, kA)) {
|
|
6341
|
-
if (a[kA] !== b[kA]) {
|
|
6342
|
-
return false;
|
|
6343
|
-
}
|
|
6344
|
-
aCount++;
|
|
6345
|
-
}
|
|
6346
|
-
}
|
|
6347
|
-
for (let kB in b) {
|
|
6348
|
-
if (Object.prototype.hasOwnProperty.call(b, kB)) {
|
|
6349
|
-
bCount++;
|
|
6350
|
-
}
|
|
6351
|
-
}
|
|
6352
|
-
return aCount === bCount;
|
|
6353
|
-
}
|
|
6354
|
-
function isRouteOptions(value) {
|
|
6355
|
-
if (value && typeof value === 'object') {
|
|
6356
|
-
let qps = value.queryParams;
|
|
6357
|
-
if (qps && typeof qps === 'object') {
|
|
6358
|
-
return Object.keys(qps).every(k => typeof k === 'string');
|
|
6359
|
-
}
|
|
6360
|
-
}
|
|
6361
|
-
return false;
|
|
6362
|
-
}
|
|
6363
|
-
|
|
6364
|
-
class RouterState {
|
|
6365
|
-
router;
|
|
6366
|
-
emberRouter;
|
|
6367
|
-
routerJsState;
|
|
6368
|
-
constructor(emberRouter, router, routerJsState) {
|
|
6369
|
-
this.emberRouter = emberRouter;
|
|
6370
|
-
this.router = router;
|
|
6371
|
-
this.routerJsState = routerJsState;
|
|
6372
|
-
}
|
|
6373
|
-
isActiveIntent(routeName, models, queryParams) {
|
|
6374
|
-
let state = this.routerJsState;
|
|
6375
|
-
if (!this.router.isActiveIntent(routeName, models, undefined, state)) {
|
|
6376
|
-
return false;
|
|
6377
|
-
}
|
|
6378
|
-
if (queryParams !== undefined && Object.keys(queryParams).length > 0) {
|
|
6379
|
-
let visibleQueryParams = Object.assign({}, queryParams);
|
|
6380
|
-
this.emberRouter._prepareQueryParams(routeName, models, visibleQueryParams);
|
|
6381
|
-
return shallowEqual(visibleQueryParams, state.queryParams);
|
|
6382
|
-
}
|
|
6383
|
-
return true;
|
|
6384
|
-
}
|
|
6385
|
-
}
|
|
6386
|
-
|
|
6387
|
-
function isStoreLike(store) {
|
|
6388
|
-
return typeof store === 'object' && store !== null && typeof store.find === 'function';
|
|
6389
|
-
}
|
|
6390
|
-
const RENDER = Symbol('render');
|
|
6391
|
-
const RENDER_STATE = Symbol('render-state');
|
|
6392
|
-
|
|
6393
|
-
/**
|
|
6394
|
-
@module @ember/routing/route
|
|
6395
|
-
*/
|
|
6396
|
-
|
|
6397
|
-
/**
|
|
6398
|
-
The `Route` class is used to define individual routes. Refer to
|
|
6399
|
-
the [routing guide](https://guides.emberjs.com/release/routing/) for documentation.
|
|
6400
|
-
|
|
6401
|
-
@class Route
|
|
6402
|
-
@extends EmberObject
|
|
6403
|
-
@uses ActionHandler
|
|
6404
|
-
@uses Evented
|
|
6405
|
-
@since 1.0.0
|
|
6406
|
-
@public
|
|
6407
|
-
*/
|
|
6408
|
-
|
|
6409
|
-
class Route extends EmberObject.extend(ActionHandler, Evented) {
|
|
6410
|
-
static isRouteFactory = true;
|
|
6411
|
-
|
|
6412
|
-
// These properties will end up appearing in the public interface because we
|
|
6413
|
-
// `implements IRoute` from `router.js`, which has them as part of *its*
|
|
6414
|
-
// public contract. We mark them as `@internal` so they at least signal to
|
|
6415
|
-
// people subclassing `Route` that they should not use them.
|
|
6416
|
-
/** @internal */
|
|
6417
|
-
context = {};
|
|
6418
|
-
/** @internal */
|
|
6419
|
-
|
|
6420
|
-
/** @internal */
|
|
6421
|
-
_bucketCache;
|
|
6422
|
-
/** @internal */
|
|
6423
|
-
_internalName;
|
|
6424
|
-
_names;
|
|
6425
|
-
_router;
|
|
6426
|
-
constructor(owner) {
|
|
6427
|
-
super(owner);
|
|
6428
|
-
if (owner) {
|
|
6429
|
-
let router = owner.lookup('router:main');
|
|
6430
|
-
let bucketCache = owner.lookup(privatize`-bucket-cache:main`);
|
|
6431
|
-
(isDevelopingApp() && !(router instanceof EmberRouter$1 && bucketCache instanceof BucketCache) && assert('ROUTER BUG: Expected route injections to be defined on the route. This is an internal bug, please open an issue on Github if you see this message!', router instanceof EmberRouter$1 && bucketCache instanceof BucketCache));
|
|
6432
|
-
this._router = router;
|
|
6433
|
-
this._bucketCache = bucketCache;
|
|
6434
|
-
this._topLevelViewTemplate = owner.lookup('template:-outlet');
|
|
6435
|
-
this._environment = owner.lookup('-environment:main');
|
|
6436
|
-
}
|
|
6437
|
-
}
|
|
6438
|
-
|
|
6439
|
-
/**
|
|
6440
|
-
A hook you can implement to convert the route's model into parameters
|
|
6441
|
-
for the URL.
|
|
6442
|
-
```app/router.js
|
|
6443
|
-
// ...
|
|
6444
|
-
Router.map(function() {
|
|
6445
|
-
this.route('post', { path: '/posts/:post_id' });
|
|
6446
|
-
});
|
|
6447
|
-
```
|
|
6448
|
-
```app/routes/post.js
|
|
6449
|
-
import Route from '@ember/routing/route';
|
|
6450
|
-
export default class PostRoute extends Route {
|
|
6451
|
-
model({ post_id }) {
|
|
6452
|
-
// the server returns `{ id: 12 }`
|
|
6453
|
-
return fetch(`/posts/${post_id}`;
|
|
6454
|
-
}
|
|
6455
|
-
serialize(model) {
|
|
6456
|
-
// this will make the URL `/posts/12`
|
|
6457
|
-
return { post_id: model.id };
|
|
6458
|
-
}
|
|
6459
|
-
}
|
|
6460
|
-
```
|
|
6461
|
-
The default `serialize` method will insert the model's `id` into the
|
|
6462
|
-
route's dynamic segment (in this case, `:post_id`) if the segment contains '_id'.
|
|
6463
|
-
If the route has multiple dynamic segments or does not contain '_id', `serialize`
|
|
6464
|
-
will return `getProperties(model, params)`
|
|
6465
|
-
This method is called when `transitionTo` is called with a context
|
|
6466
|
-
in order to populate the URL.
|
|
6467
|
-
@method serialize
|
|
6468
|
-
@param {Object} model the routes model
|
|
6469
|
-
@param {Array} params an Array of parameter names for the current
|
|
6470
|
-
route (in the example, `['post_id']`.
|
|
6471
|
-
@return {Object} the serialized parameters
|
|
6472
|
-
@since 1.0.0
|
|
6473
|
-
@public
|
|
6474
|
-
*/
|
|
6475
|
-
serialize(model, params) {
|
|
6476
|
-
if (params.length < 1 || !model) {
|
|
6477
|
-
return;
|
|
6478
|
-
}
|
|
6479
|
-
let object = {};
|
|
6480
|
-
if (params.length === 1) {
|
|
6481
|
-
let [name] = params;
|
|
6482
|
-
(isDevelopingApp() && !(name) && assert('has name', name));
|
|
6483
|
-
if (typeof model === 'object' && name in model) {
|
|
6484
|
-
object[name] = get(model, name);
|
|
6485
|
-
} else if (/_id$/.test(name)) {
|
|
6486
|
-
object[name] = get(model, 'id');
|
|
6487
|
-
} else if (isProxy(model)) {
|
|
6488
|
-
object[name] = get(model, name);
|
|
6489
|
-
}
|
|
6490
|
-
} else {
|
|
6491
|
-
object = getProperties(model, params);
|
|
6492
|
-
}
|
|
6493
|
-
return object;
|
|
6494
|
-
}
|
|
6495
|
-
|
|
6496
|
-
/**
|
|
6497
|
-
Configuration hash for this route's queryParams. The possible
|
|
6498
|
-
configuration options and their defaults are as follows
|
|
6499
|
-
(assuming a query param whose controller property is `page`):
|
|
6500
|
-
```javascript
|
|
6501
|
-
queryParams = {
|
|
6502
|
-
page: {
|
|
6503
|
-
// By default, controller query param properties don't
|
|
6504
|
-
// cause a full transition when they are changed, but
|
|
6505
|
-
// rather only cause the URL to update. Setting
|
|
6506
|
-
// `refreshModel` to true will cause an "in-place"
|
|
6507
|
-
// transition to occur, whereby the model hooks for
|
|
6508
|
-
// this route (and any child routes) will re-fire, allowing
|
|
6509
|
-
// you to reload models (e.g., from the server) using the
|
|
6510
|
-
// updated query param values.
|
|
6511
|
-
refreshModel: false,
|
|
6512
|
-
// By default, changes to controller query param properties
|
|
6513
|
-
// cause the URL to update via `pushState`, which means an
|
|
6514
|
-
// item will be added to the browser's history, allowing
|
|
6515
|
-
// you to use the back button to restore the app to the
|
|
6516
|
-
// previous state before the query param property was changed.
|
|
6517
|
-
// Setting `replace` to true will use `replaceState` (or its
|
|
6518
|
-
// hash location equivalent), which causes no browser history
|
|
6519
|
-
// item to be added. This options name and default value are
|
|
6520
|
-
// the same as the `link-to` helper's `replace` option.
|
|
6521
|
-
replace: false,
|
|
6522
|
-
// By default, the query param URL key is the same name as
|
|
6523
|
-
// the controller property name. Use `as` to specify a
|
|
6524
|
-
// different URL key.
|
|
6525
|
-
as: 'page'
|
|
6526
|
-
}
|
|
6527
|
-
};
|
|
6528
|
-
```
|
|
6529
|
-
@property queryParams
|
|
6530
|
-
@for Route
|
|
6531
|
-
@type Object
|
|
6532
|
-
@since 1.6.0
|
|
6533
|
-
@public
|
|
6534
|
-
*/
|
|
6535
|
-
// Set in reopen so it can be overriden with extend
|
|
6536
|
-
|
|
6537
|
-
/**
|
|
6538
|
-
The name of the template to use by default when rendering this route's
|
|
6539
|
-
template.
|
|
6540
|
-
```app/routes/posts/list.js
|
|
6541
|
-
import Route from '@ember/routing/route';
|
|
6542
|
-
export default class PostsListRoute extends Route {
|
|
6543
|
-
templateName = 'posts/list';
|
|
6544
|
-
}
|
|
6545
|
-
```
|
|
6546
|
-
```app/routes/posts/index.js
|
|
6547
|
-
import PostsListRoute from '../posts/list';
|
|
6548
|
-
export default class PostsIndexRoute extends PostsListRoute {};
|
|
6549
|
-
```
|
|
6550
|
-
```app/routes/posts/archived.js
|
|
6551
|
-
import PostsListRoute from '../posts/list';
|
|
6552
|
-
export default class PostsArchivedRoute extends PostsListRoute {};
|
|
6553
|
-
```
|
|
6554
|
-
@property templateName
|
|
6555
|
-
@type String
|
|
6556
|
-
@default null
|
|
6557
|
-
@since 1.4.0
|
|
6558
|
-
@public
|
|
6559
|
-
*/
|
|
6560
|
-
// Set in reopen so it can be overriden with extend
|
|
6561
|
-
|
|
6562
|
-
/**
|
|
6563
|
-
The name of the controller to associate with this route.
|
|
6564
|
-
By default, Ember will lookup a route's controller that matches the name
|
|
6565
|
-
of the route (i.e. `posts.new`). However,
|
|
6566
|
-
if you would like to define a specific controller to use, you can do so
|
|
6567
|
-
using this property.
|
|
6568
|
-
This is useful in many ways, as the controller specified will be:
|
|
6569
|
-
* passed to the `setupController` method.
|
|
6570
|
-
* used as the controller for the template being rendered by the route.
|
|
6571
|
-
* returned from a call to `controllerFor` for the route.
|
|
6572
|
-
@property controllerName
|
|
6573
|
-
@type String
|
|
6574
|
-
@default null
|
|
6575
|
-
@since 1.4.0
|
|
6576
|
-
@public
|
|
6577
|
-
*/
|
|
6578
|
-
// Set in reopen so it can be overriden with extend
|
|
6579
|
-
|
|
6580
|
-
/**
|
|
6581
|
-
The controller associated with this route.
|
|
6582
|
-
Example
|
|
6583
|
-
```app/routes/form.js
|
|
6584
|
-
import Route from '@ember/routing/route';
|
|
6585
|
-
import { action } from '@ember/object';
|
|
6586
|
-
export default class FormRoute extends Route {
|
|
6587
|
-
@action
|
|
6588
|
-
willTransition(transition) {
|
|
6589
|
-
if (this.controller.get('userHasEnteredData') &&
|
|
6590
|
-
!confirm('Are you sure you want to abandon progress?')) {
|
|
6591
|
-
transition.abort();
|
|
6592
|
-
} else {
|
|
6593
|
-
// Bubble the `willTransition` action so that
|
|
6594
|
-
// parent routes can decide whether or not to abort.
|
|
6595
|
-
return true;
|
|
6596
|
-
}
|
|
6597
|
-
}
|
|
6598
|
-
}
|
|
6599
|
-
```
|
|
6600
|
-
@property controller
|
|
6601
|
-
@type Controller
|
|
6602
|
-
@since 1.6.0
|
|
6603
|
-
@public
|
|
6604
|
-
*/
|
|
6605
|
-
|
|
6606
|
-
/**
|
|
6607
|
-
The name of the route, dot-delimited.
|
|
6608
|
-
For example, a route found at `app/routes/posts/post.js` will have
|
|
6609
|
-
a `routeName` of `posts.post`.
|
|
6610
|
-
@property routeName
|
|
6611
|
-
@for Route
|
|
6612
|
-
@type String
|
|
6613
|
-
@since 1.0.0
|
|
6614
|
-
@public
|
|
6615
|
-
*/
|
|
6616
|
-
|
|
6617
|
-
/**
|
|
6618
|
-
The name of the route, dot-delimited, including the engine prefix
|
|
6619
|
-
if applicable.
|
|
6620
|
-
For example, a route found at `addon/routes/posts/post.js` within an
|
|
6621
|
-
engine named `admin` will have a `fullRouteName` of `admin.posts.post`.
|
|
6622
|
-
@property fullRouteName
|
|
6623
|
-
@for Route
|
|
6624
|
-
@type String
|
|
6625
|
-
@since 2.10.0
|
|
6626
|
-
@public
|
|
6627
|
-
*/
|
|
6628
|
-
|
|
6629
|
-
/**
|
|
6630
|
-
Sets the name for this route, including a fully resolved name for routes
|
|
6631
|
-
inside engines.
|
|
6632
|
-
@private
|
|
6633
|
-
@method _setRouteName
|
|
6634
|
-
@param {String} name
|
|
6635
|
-
*/
|
|
6636
|
-
_setRouteName(name) {
|
|
6637
|
-
this.routeName = name;
|
|
6638
|
-
let owner = getOwner(this);
|
|
6639
|
-
(isDevelopingApp() && !(owner instanceof EmberEngineInstance) && assert('Expected route to have EngineInstance as owner', owner instanceof EmberEngineInstance));
|
|
6640
|
-
this.fullRouteName = getEngineRouteName(owner, name);
|
|
6641
|
-
}
|
|
6642
|
-
|
|
6643
|
-
/**
|
|
6644
|
-
@private
|
|
6645
|
-
@method _stashNames
|
|
6646
|
-
*/
|
|
6647
|
-
_stashNames(routeInfo, dynamicParent) {
|
|
6648
|
-
if (this._names) {
|
|
6649
|
-
return;
|
|
6650
|
-
}
|
|
6651
|
-
let names = this._names = routeInfo['_names'];
|
|
6652
|
-
if (!names.length) {
|
|
6653
|
-
routeInfo = dynamicParent;
|
|
6654
|
-
names = routeInfo && routeInfo['_names'] || [];
|
|
6655
|
-
}
|
|
6656
|
-
|
|
6657
|
-
// SAFETY: Since `_qp` is protected we can't infer the type
|
|
6658
|
-
let qps = get(this, '_qp').qps;
|
|
6659
|
-
let namePaths = new Array(names.length);
|
|
6660
|
-
for (let a = 0; a < names.length; ++a) {
|
|
6661
|
-
namePaths[a] = `${routeInfo.name}.${names[a]}`;
|
|
6662
|
-
}
|
|
6663
|
-
for (let qp of qps) {
|
|
6664
|
-
if (qp.scope === 'model') {
|
|
6665
|
-
qp.parts = namePaths;
|
|
6666
|
-
}
|
|
6667
|
-
}
|
|
6668
|
-
}
|
|
6669
|
-
|
|
6670
|
-
/**
|
|
6671
|
-
@private
|
|
6672
|
-
@property _activeQPChanged
|
|
6673
|
-
*/
|
|
6674
|
-
_activeQPChanged(qp, value) {
|
|
6675
|
-
this._router._activeQPChanged(qp.scopedPropertyName, value);
|
|
6676
|
-
}
|
|
6677
|
-
|
|
6678
|
-
/**
|
|
6679
|
-
@private
|
|
6680
|
-
@method _updatingQPChanged
|
|
6681
|
-
*/
|
|
6682
|
-
_updatingQPChanged(qp) {
|
|
6683
|
-
this._router._updatingQPChanged(qp.urlKey);
|
|
6684
|
-
}
|
|
6685
|
-
|
|
6686
|
-
/**
|
|
6687
|
-
Returns a hash containing the parameters of an ancestor route.
|
|
6688
|
-
You may notice that `this.paramsFor` sometimes works when referring to a
|
|
6689
|
-
child route, but this behavior should not be relied upon as only ancestor
|
|
6690
|
-
routes are certain to be loaded in time.
|
|
6691
|
-
Example
|
|
6692
|
-
```app/router.js
|
|
6693
|
-
// ...
|
|
6694
|
-
Router.map(function() {
|
|
6695
|
-
this.route('member', { path: ':name' }, function() {
|
|
6696
|
-
this.route('interest', { path: ':interest' });
|
|
6697
|
-
});
|
|
6698
|
-
});
|
|
6699
|
-
```
|
|
6700
|
-
```app/routes/member.js
|
|
6701
|
-
import Route from '@ember/routing/route';
|
|
6702
|
-
export default class MemberRoute extends Route {
|
|
6703
|
-
queryParams = {
|
|
6704
|
-
memberQp: { refreshModel: true }
|
|
6705
|
-
}
|
|
6706
|
-
}
|
|
6707
|
-
```
|
|
6708
|
-
```app/routes/member/interest.js
|
|
6709
|
-
import Route from '@ember/routing/route';
|
|
6710
|
-
export default class MemberInterestRoute extends Route {
|
|
6711
|
-
queryParams = {
|
|
6712
|
-
interestQp: { refreshModel: true }
|
|
6713
|
-
}
|
|
6714
|
-
model() {
|
|
6715
|
-
return this.paramsFor('member');
|
|
6716
|
-
}
|
|
6717
|
-
}
|
|
6718
|
-
```
|
|
6719
|
-
If we visit `/turing/maths?memberQp=member&interestQp=interest` the model for
|
|
6720
|
-
the `member.interest` route is a hash with:
|
|
6721
|
-
* `name`: `turing`
|
|
6722
|
-
* `memberQp`: `member`
|
|
6723
|
-
@method paramsFor
|
|
6724
|
-
@param {String} name
|
|
6725
|
-
@return {Object} hash containing the parameters of the route `name`
|
|
6726
|
-
@since 1.4.0
|
|
6727
|
-
@public
|
|
6728
|
-
*/
|
|
6729
|
-
paramsFor(name) {
|
|
6730
|
-
let owner = getOwner(this);
|
|
6731
|
-
(isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
|
|
6732
|
-
let route = owner.lookup(`route:${name}`);
|
|
6733
|
-
if (route === undefined) {
|
|
6734
|
-
return {};
|
|
6735
|
-
}
|
|
6736
|
-
let transition = this._router._routerMicrolib.activeTransition;
|
|
6737
|
-
let state = transition ? transition[STATE_SYMBOL] : this._router._routerMicrolib.state;
|
|
6738
|
-
let fullName = route.fullRouteName;
|
|
6739
|
-
let params = {
|
|
6740
|
-
...state.params[fullName]
|
|
6741
|
-
};
|
|
6742
|
-
let queryParams = getQueryParamsFor(route, state);
|
|
6743
|
-
return Object.entries(queryParams).reduce((params, [key, value]) => {
|
|
6744
|
-
(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]));
|
|
6745
|
-
params[key] = value;
|
|
6746
|
-
return params;
|
|
6747
|
-
}, params);
|
|
6748
|
-
}
|
|
6749
|
-
|
|
6750
|
-
/**
|
|
6751
|
-
Serializes the query parameter key
|
|
6752
|
-
@method serializeQueryParamKey
|
|
6753
|
-
@param {String} controllerPropertyName
|
|
6754
|
-
@private
|
|
6755
|
-
*/
|
|
6756
|
-
serializeQueryParamKey(controllerPropertyName) {
|
|
6757
|
-
return controllerPropertyName;
|
|
6758
|
-
}
|
|
6759
|
-
|
|
6760
|
-
/**
|
|
6761
|
-
Serializes value of the query parameter based on defaultValueType
|
|
6762
|
-
@method serializeQueryParam
|
|
6763
|
-
@param {Object} value
|
|
6764
|
-
@param {String} urlKey
|
|
6765
|
-
@param {String} defaultValueType
|
|
6766
|
-
@private
|
|
6767
|
-
*/
|
|
6768
|
-
serializeQueryParam(value, _urlKey, defaultValueType) {
|
|
6769
|
-
// urlKey isn't used here, but anyone overriding
|
|
6770
|
-
// can use it to provide serialization specific
|
|
6771
|
-
// to a certain query param.
|
|
6772
|
-
return this._router._serializeQueryParam(value, defaultValueType);
|
|
6773
|
-
}
|
|
6774
|
-
|
|
6775
|
-
/**
|
|
6776
|
-
Deserializes value of the query parameter based on defaultValueType
|
|
6777
|
-
@method deserializeQueryParam
|
|
6778
|
-
@param {Object} value
|
|
6779
|
-
@param {String} urlKey
|
|
6780
|
-
@param {String} defaultValueType
|
|
6781
|
-
@private
|
|
6782
|
-
*/
|
|
6783
|
-
deserializeQueryParam(value, _urlKey, defaultValueType) {
|
|
6784
|
-
// urlKey isn't used here, but anyone overriding
|
|
6785
|
-
// can use it to provide deserialization specific
|
|
6786
|
-
// to a certain query param.
|
|
6787
|
-
return this._router._deserializeQueryParam(value, defaultValueType);
|
|
6788
|
-
}
|
|
6789
|
-
|
|
6790
|
-
/**
|
|
6791
|
-
@private
|
|
6792
|
-
@property _optionsForQueryParam
|
|
6793
|
-
*/
|
|
6794
|
-
_optionsForQueryParam(qp) {
|
|
6795
|
-
const queryParams = get(this, 'queryParams');
|
|
6796
|
-
return get(queryParams, qp.urlKey) || get(queryParams, qp.prop) || queryParams[qp.urlKey] || queryParams[qp.prop] || {};
|
|
6797
|
-
}
|
|
6798
|
-
|
|
6799
|
-
/**
|
|
6800
|
-
A hook you can use to reset controller values either when the model
|
|
6801
|
-
changes or the route is exiting.
|
|
6802
|
-
```app/routes/articles.js
|
|
6803
|
-
import Route from '@ember/routing/route';
|
|
6804
|
-
export default class ArticlesRoute extends Route {
|
|
6805
|
-
resetController(controller, isExiting, transition) {
|
|
6806
|
-
if (isExiting && transition.targetName !== 'error') {
|
|
6807
|
-
controller.set('page', 1);
|
|
6808
|
-
}
|
|
6809
|
-
}
|
|
6810
|
-
}
|
|
6811
|
-
```
|
|
6812
|
-
@method resetController
|
|
6813
|
-
@param {Controller} controller instance
|
|
6814
|
-
@param {Boolean} isExiting
|
|
6815
|
-
@param {Object} transition
|
|
6816
|
-
@since 1.7.0
|
|
6817
|
-
@public
|
|
6818
|
-
*/
|
|
6819
|
-
resetController(_controller, _isExiting, _transition) {
|
|
6820
|
-
// We document that subclasses do not have to return *anything* and in fact
|
|
6821
|
-
// do not even have to call super, so whiel we *do* return `this`, we need
|
|
6822
|
-
// to be explicit in the types that our return type is *effectively* `void`.
|
|
6823
|
-
return this;
|
|
6824
|
-
}
|
|
6825
|
-
|
|
6826
|
-
/**
|
|
6827
|
-
@private
|
|
6828
|
-
@method exit
|
|
6829
|
-
*/
|
|
6830
|
-
exit(transition) {
|
|
6831
|
-
this.deactivate(transition);
|
|
6832
|
-
this.trigger('deactivate', transition);
|
|
6833
|
-
this.teardownViews();
|
|
6834
|
-
}
|
|
6835
|
-
|
|
6836
|
-
/**
|
|
6837
|
-
@private
|
|
6838
|
-
@method _internalReset
|
|
6839
|
-
@since 3.6.0
|
|
6840
|
-
*/
|
|
6841
|
-
_internalReset(isExiting, transition) {
|
|
6842
|
-
let controller = this.controller;
|
|
6843
|
-
// SAFETY: Since `_qp` is protected we can't infer the type
|
|
6844
|
-
controller['_qpDelegate'] = get(this, '_qp').states.inactive;
|
|
6845
|
-
this.resetController(controller, isExiting, transition);
|
|
6846
|
-
}
|
|
6847
|
-
|
|
6848
|
-
/**
|
|
6849
|
-
@private
|
|
6850
|
-
@method enter
|
|
6851
|
-
*/
|
|
6852
|
-
enter(transition) {
|
|
6853
|
-
this[RENDER_STATE] = undefined;
|
|
6854
|
-
this.activate(transition);
|
|
6855
|
-
this.trigger('activate', transition);
|
|
6856
|
-
}
|
|
6857
|
-
|
|
6858
|
-
/**
|
|
6859
|
-
This event is triggered when the router enters the route. It is
|
|
6860
|
-
not executed when the model for the route changes.
|
|
6861
|
-
```app/routes/application.js
|
|
6862
|
-
import { on } from '@ember/object/evented';
|
|
6863
|
-
import Route from '@ember/routing/route';
|
|
6864
|
-
export default Route.extend({
|
|
6865
|
-
collectAnalytics: on('activate', function(){
|
|
6866
|
-
collectAnalytics();
|
|
6867
|
-
})
|
|
6868
|
-
});
|
|
6869
|
-
```
|
|
6870
|
-
@event activate
|
|
6871
|
-
@since 1.9.0
|
|
6872
|
-
@public
|
|
6873
|
-
*/
|
|
6874
|
-
|
|
6875
|
-
/**
|
|
6876
|
-
This event is triggered when the router completely exits this
|
|
6877
|
-
route. It is not executed when the model for the route changes.
|
|
6878
|
-
```app/routes/index.js
|
|
6879
|
-
import { on } from '@ember/object/evented';
|
|
6880
|
-
import Route from '@ember/routing/route';
|
|
6881
|
-
export default Route.extend({
|
|
6882
|
-
trackPageLeaveAnalytics: on('deactivate', function(){
|
|
6883
|
-
trackPageLeaveAnalytics();
|
|
6884
|
-
})
|
|
6885
|
-
});
|
|
6886
|
-
```
|
|
6887
|
-
@event deactivate
|
|
6888
|
-
@since 1.9.0
|
|
6889
|
-
@public
|
|
6890
|
-
*/
|
|
6891
|
-
|
|
6892
|
-
/**
|
|
6893
|
-
This hook is executed when the router completely exits this route. It is
|
|
6894
|
-
not executed when the model for the route changes.
|
|
6895
|
-
@method deactivate
|
|
6896
|
-
@param {Transition} transition
|
|
6897
|
-
@since 1.0.0
|
|
6898
|
-
@public
|
|
6899
|
-
*/
|
|
6900
|
-
deactivate(_transition) {}
|
|
6901
|
-
|
|
6902
|
-
/**
|
|
6903
|
-
This hook is executed when the router enters the route. It is not executed
|
|
6904
|
-
when the model for the route changes.
|
|
6905
|
-
@method activate
|
|
6906
|
-
@param {Transition} transition
|
|
6907
|
-
@since 1.0.0
|
|
6908
|
-
@public
|
|
6909
|
-
*/
|
|
6910
|
-
activate(_transition) {}
|
|
6911
|
-
|
|
6912
|
-
/**
|
|
6913
|
-
Perform a synchronous transition into another route without attempting
|
|
6914
|
-
to resolve promises, update the URL, or abort any currently active
|
|
6915
|
-
asynchronous transitions (i.e. regular transitions caused by
|
|
6916
|
-
`transitionTo` or URL changes).
|
|
6917
|
-
This method is handy for performing intermediate transitions on the
|
|
6918
|
-
way to a final destination route, and is called internally by the
|
|
6919
|
-
default implementations of the `error` and `loading` handlers.
|
|
6920
|
-
@method intermediateTransitionTo
|
|
6921
|
-
@param {String} name the name of the route
|
|
6922
|
-
@param {...Object} models the model(s) to be used while transitioning
|
|
6923
|
-
to the route.
|
|
6924
|
-
@since 1.2.0
|
|
6925
|
-
@public
|
|
6926
|
-
*/
|
|
6927
|
-
intermediateTransitionTo(...args) {
|
|
6928
|
-
let [name, ...preparedArgs] = prefixRouteNameArg(this, args);
|
|
6929
|
-
this._router.intermediateTransitionTo(name, ...preparedArgs);
|
|
6930
|
-
}
|
|
6931
|
-
|
|
6932
|
-
/**
|
|
6933
|
-
Refresh the model on this route and any child routes, firing the
|
|
6934
|
-
`beforeModel`, `model`, and `afterModel` hooks in a similar fashion
|
|
6935
|
-
to how routes are entered when transitioning in from other route.
|
|
6936
|
-
The current route params (e.g. `article_id`) will be passed in
|
|
6937
|
-
to the respective model hooks, and if a different model is returned,
|
|
6938
|
-
`setupController` and associated route hooks will re-fire as well.
|
|
6939
|
-
An example usage of this method is re-querying the server for the
|
|
6940
|
-
latest information using the same parameters as when the route
|
|
6941
|
-
was first entered.
|
|
6942
|
-
Note that this will cause `model` hooks to fire even on routes
|
|
6943
|
-
that were provided a model object when the route was initially
|
|
6944
|
-
entered.
|
|
6945
|
-
@method refresh
|
|
6946
|
-
@return {Transition} the transition object associated with this
|
|
6947
|
-
attempted transition
|
|
6948
|
-
@since 1.4.0
|
|
6949
|
-
@public
|
|
6950
|
-
*/
|
|
6951
|
-
refresh() {
|
|
6952
|
-
return this._router._routerMicrolib.refresh(this);
|
|
6953
|
-
}
|
|
6954
|
-
|
|
6955
|
-
/**
|
|
6956
|
-
This hook is the entry point for router.js
|
|
6957
|
-
@private
|
|
6958
|
-
@method setup
|
|
6959
|
-
*/
|
|
6960
|
-
setup(context, transition) {
|
|
6961
|
-
let controllerName = this.controllerName || this.routeName;
|
|
6962
|
-
let definedController = this.controllerFor(controllerName, true);
|
|
6963
|
-
let controller = definedController ?? this.generateController(controllerName);
|
|
6964
|
-
|
|
6965
|
-
// SAFETY: Since `_qp` is protected we can't infer the type
|
|
6966
|
-
let queryParams = get(this, '_qp');
|
|
6967
|
-
|
|
6968
|
-
// Assign the route's controller so that it can more easily be
|
|
6969
|
-
// referenced in action handlers. Side effects. Side effects everywhere.
|
|
6970
|
-
if (!this.controller) {
|
|
6971
|
-
let propNames = queryParams.propertyNames;
|
|
6972
|
-
addQueryParamsObservers(controller, propNames);
|
|
6973
|
-
this.controller = controller;
|
|
6974
|
-
}
|
|
6975
|
-
let states = queryParams.states;
|
|
6976
|
-
controller._qpDelegate = states.allowOverrides;
|
|
6977
|
-
if (transition) {
|
|
6978
|
-
// Update the model dep values used to calculate cache keys.
|
|
6979
|
-
stashParamNames(this._router, transition[STATE_SYMBOL].routeInfos);
|
|
6980
|
-
let cache = this._bucketCache;
|
|
6981
|
-
let params = transition[PARAMS_SYMBOL];
|
|
6982
|
-
let allParams = queryParams.propertyNames;
|
|
6983
|
-
allParams.forEach(prop => {
|
|
6984
|
-
let aQp = queryParams.map[prop];
|
|
6985
|
-
(isDevelopingApp() && !(aQp) && assert('expected aQp', aQp));
|
|
6986
|
-
aQp.values = params;
|
|
6987
|
-
let cacheKey = calculateCacheKey(aQp.route.fullRouteName, aQp.parts, aQp.values);
|
|
6988
|
-
let value = cache.lookup(cacheKey, prop, aQp.undecoratedDefaultValue);
|
|
6989
|
-
set(controller, prop, value);
|
|
6990
|
-
});
|
|
6991
|
-
let qpValues = getQueryParamsFor(this, transition[STATE_SYMBOL]);
|
|
6992
|
-
setProperties(controller, qpValues);
|
|
6993
|
-
}
|
|
6994
|
-
this.setupController(controller, context, transition);
|
|
6995
|
-
if (this._environment.options.shouldRender) {
|
|
6996
|
-
this[RENDER]();
|
|
6997
|
-
}
|
|
6998
|
-
|
|
6999
|
-
// Setup can cause changes to QPs which need to be propogated immediately in
|
|
7000
|
-
// some situations. Eventually, we should work on making these async somehow.
|
|
7001
|
-
flushAsyncObservers(false);
|
|
7002
|
-
}
|
|
7003
|
-
|
|
7004
|
-
/*
|
|
7005
|
-
Called when a query parameter for this route changes, regardless of whether the route
|
|
7006
|
-
is currently part of the active route hierarchy. This will update the query parameter's
|
|
7007
|
-
value in the cache so if this route becomes active, the cache value has been updated.
|
|
7008
|
-
*/
|
|
7009
|
-
_qpChanged(prop, value, qp) {
|
|
7010
|
-
if (!qp) {
|
|
7011
|
-
return;
|
|
7012
|
-
}
|
|
7013
|
-
|
|
7014
|
-
// Update model-dep cache
|
|
7015
|
-
let cache = this._bucketCache;
|
|
7016
|
-
let cacheKey = calculateCacheKey(qp.route.fullRouteName, qp.parts, qp.values);
|
|
7017
|
-
cache.stash(cacheKey, prop, value);
|
|
7018
|
-
}
|
|
7019
|
-
|
|
7020
|
-
/**
|
|
7021
|
-
This hook is the first of the route entry validation hooks
|
|
7022
|
-
called when an attempt is made to transition into a route
|
|
7023
|
-
or one of its children. It is called before `model` and
|
|
7024
|
-
`afterModel`, and is appropriate for cases when:
|
|
7025
|
-
1) A decision can be made to redirect elsewhere without
|
|
7026
|
-
needing to resolve the model first.
|
|
7027
|
-
2) Any async operations need to occur first before the
|
|
7028
|
-
model is attempted to be resolved.
|
|
7029
|
-
This hook is provided the current `transition` attempt
|
|
7030
|
-
as a parameter, which can be used to `.abort()` the transition,
|
|
7031
|
-
save it for a later `.retry()`, or retrieve values set
|
|
7032
|
-
on it from a previous hook. You can also just call
|
|
7033
|
-
`router.transitionTo` to another route to implicitly
|
|
7034
|
-
abort the `transition`.
|
|
7035
|
-
You can return a promise from this hook to pause the
|
|
7036
|
-
transition until the promise resolves (or rejects). This could
|
|
7037
|
-
be useful, for instance, for retrieving async code from
|
|
7038
|
-
the server that is required to enter a route.
|
|
7039
|
-
@method beforeModel
|
|
7040
|
-
@param {Transition} transition
|
|
7041
|
-
@return {any | Promise<any>} if the value returned from this hook is
|
|
7042
|
-
a promise, the transition will pause until the transition
|
|
7043
|
-
resolves. Otherwise, non-promise return values are not
|
|
7044
|
-
utilized in any way.
|
|
7045
|
-
@since 1.0.0
|
|
7046
|
-
@public
|
|
7047
|
-
*/
|
|
7048
|
-
|
|
7049
|
-
beforeModel(_transition) {}
|
|
7050
|
-
|
|
7051
|
-
/**
|
|
7052
|
-
This hook is called after this route's model has resolved.
|
|
7053
|
-
It follows identical async/promise semantics to `beforeModel`
|
|
7054
|
-
but is provided the route's resolved model in addition to
|
|
7055
|
-
the `transition`, and is therefore suited to performing
|
|
7056
|
-
logic that can only take place after the model has already
|
|
7057
|
-
resolved.
|
|
7058
|
-
```app/routes/posts.js
|
|
7059
|
-
import Route from '@ember/routing/route';
|
|
7060
|
-
import { service } from '@ember/service';
|
|
7061
|
-
export default class PostsRoute extends Route {
|
|
7062
|
-
@service router;
|
|
7063
|
-
afterModel(posts, transition) {
|
|
7064
|
-
if (posts.get('length') === 1) {
|
|
7065
|
-
this.router.transitionTo('post.show', posts.get('firstObject'));
|
|
7066
|
-
}
|
|
7067
|
-
}
|
|
7068
|
-
}
|
|
7069
|
-
```
|
|
7070
|
-
Refer to documentation for `beforeModel` for a description
|
|
7071
|
-
of transition-pausing semantics when a promise is returned
|
|
7072
|
-
from this hook.
|
|
7073
|
-
@method afterModel
|
|
7074
|
-
@param {Object} resolvedModel the value returned from `model`,
|
|
7075
|
-
or its resolved value if it was a promise
|
|
7076
|
-
@param {Transition} transition
|
|
7077
|
-
@return {any | Promise<any>} if the value returned from this hook is
|
|
7078
|
-
a promise, the transition will pause until the transition
|
|
7079
|
-
resolves. Otherwise, non-promise return values are not
|
|
7080
|
-
utilized in any way.
|
|
7081
|
-
@since 1.0.0
|
|
7082
|
-
@public
|
|
7083
|
-
*/
|
|
7084
|
-
|
|
7085
|
-
afterModel(_resolvedModel, _transition) {}
|
|
7086
|
-
|
|
7087
|
-
/**
|
|
7088
|
-
A hook you can implement to optionally redirect to another route.
|
|
7089
|
-
Calling `this.router.transitionTo` from inside of the `redirect` hook will
|
|
7090
|
-
abort the current transition (into the route that has implemented `redirect`).
|
|
7091
|
-
`redirect` and `afterModel` behave very similarly and are
|
|
7092
|
-
called almost at the same time, but they have an important
|
|
7093
|
-
distinction when calling `this.router.transitionTo` to a child route
|
|
7094
|
-
of the current route. From `afterModel`, this new transition
|
|
7095
|
-
invalidates the current transition, causing `beforeModel`,
|
|
7096
|
-
`model`, and `afterModel` hooks to be called again. But the
|
|
7097
|
-
same transition started from `redirect` does _not_ invalidate
|
|
7098
|
-
the current transition. In other words, by the time the `redirect`
|
|
7099
|
-
hook has been called, both the resolved model and the attempted
|
|
7100
|
-
entry into this route are considered fully validated.
|
|
7101
|
-
@method redirect
|
|
7102
|
-
@param {Object} model the model for this route
|
|
7103
|
-
@param {Transition} transition the transition object associated with the current transition
|
|
7104
|
-
@since 1.0.0
|
|
7105
|
-
@public
|
|
7106
|
-
*/
|
|
7107
|
-
redirect(_model, _transition) {}
|
|
7108
|
-
|
|
7109
|
-
/**
|
|
7110
|
-
Called when the context is changed by router.js.
|
|
7111
|
-
@private
|
|
7112
|
-
@method contextDidChange
|
|
7113
|
-
*/
|
|
7114
|
-
contextDidChange() {
|
|
7115
|
-
this.currentModel = this.context;
|
|
7116
|
-
}
|
|
7117
|
-
|
|
7118
|
-
/**
|
|
7119
|
-
A hook you can implement to convert the URL into the model for
|
|
7120
|
-
this route.
|
|
7121
|
-
```app/router.js
|
|
7122
|
-
// ...
|
|
7123
|
-
Router.map(function() {
|
|
7124
|
-
this.route('post', { path: '/posts/:post_id' });
|
|
7125
|
-
});
|
|
7126
|
-
export default Router;
|
|
7127
|
-
```
|
|
7128
|
-
Note that for routes with dynamic segments, this hook is not always
|
|
7129
|
-
executed. If the route is entered through a transition (e.g. when
|
|
7130
|
-
using the `link-to` Handlebars helper or the `transitionTo` method
|
|
7131
|
-
of routes), and a model context is already provided this hook
|
|
7132
|
-
is not called.
|
|
7133
|
-
A model context does not include a primitive string or number,
|
|
7134
|
-
which does cause the model hook to be called.
|
|
7135
|
-
Routes without dynamic segments will always execute the model hook.
|
|
7136
|
-
```javascript
|
|
7137
|
-
// no dynamic segment, model hook always called
|
|
7138
|
-
this.router.transitionTo('posts');
|
|
7139
|
-
// model passed in, so model hook not called
|
|
7140
|
-
thePost = store.findRecord('post', 1);
|
|
7141
|
-
this.router.transitionTo('post', thePost);
|
|
7142
|
-
// integer passed in, model hook is called
|
|
7143
|
-
this.router.transitionTo('post', 1);
|
|
7144
|
-
// model id passed in, model hook is called
|
|
7145
|
-
// useful for forcing the hook to execute
|
|
7146
|
-
thePost = store.findRecord('post', 1);
|
|
7147
|
-
this.router.transitionTo('post', thePost.id);
|
|
7148
|
-
```
|
|
7149
|
-
This hook follows the asynchronous/promise semantics
|
|
7150
|
-
described in the documentation for `beforeModel`. In particular,
|
|
7151
|
-
if a promise returned from `model` fails, the error will be
|
|
7152
|
-
handled by the `error` hook on `Route`.
|
|
7153
|
-
Note that the legacy behavior of automatically defining a model
|
|
7154
|
-
hook when a dynamic segment ending in `_id` is present is
|
|
7155
|
-
[deprecated](https://deprecations.emberjs.com/v5.x#toc_deprecate-implicit-route-model).
|
|
7156
|
-
You should explicitly define a model hook whenever any segments are
|
|
7157
|
-
present.
|
|
7158
|
-
Example
|
|
7159
|
-
```app/routes/post.js
|
|
7160
|
-
import Route from '@ember/routing/route';
|
|
7161
|
-
import { service } from '@ember/service';
|
|
7162
|
-
export default class PostRoute extends Route {
|
|
7163
|
-
@service store;
|
|
7164
|
-
model(params) {
|
|
7165
|
-
return this.store.findRecord('post', params.post_id);
|
|
7166
|
-
}
|
|
7167
|
-
}
|
|
7168
|
-
```
|
|
7169
|
-
@method model
|
|
7170
|
-
@param {Object} params the parameters extracted from the URL
|
|
7171
|
-
@param {Transition} transition
|
|
7172
|
-
@return {any | Promise<any>} the model for this route. If
|
|
7173
|
-
a promise is returned, the transition will pause until
|
|
7174
|
-
the promise resolves, and the resolved value of the promise
|
|
7175
|
-
will be used as the model for this route.
|
|
7176
|
-
@since 1.0.0
|
|
7177
|
-
@public
|
|
7178
|
-
*/
|
|
7179
|
-
model(params, transition) {
|
|
7180
|
-
let name, sawParams, value;
|
|
7181
|
-
// SAFETY: Since `_qp` is protected we can't infer the type
|
|
7182
|
-
let queryParams = get(this, '_qp').map;
|
|
7183
|
-
for (let prop in params) {
|
|
7184
|
-
if (prop === 'queryParams' || queryParams && prop in queryParams) {
|
|
7185
|
-
continue;
|
|
7186
|
-
}
|
|
7187
|
-
let match = prop.match(/^(.*)_id$/);
|
|
7188
|
-
if (match !== null) {
|
|
7189
|
-
name = match[1];
|
|
7190
|
-
value = params[prop];
|
|
7191
|
-
}
|
|
7192
|
-
sawParams = true;
|
|
7193
|
-
}
|
|
7194
|
-
if (!name) {
|
|
7195
|
-
if (sawParams) {
|
|
7196
|
-
// SAFETY: This should be equivalent
|
|
7197
|
-
return Object.assign({}, params);
|
|
7198
|
-
} else {
|
|
7199
|
-
if (transition.resolveIndex < 1) {
|
|
7200
|
-
return;
|
|
7201
|
-
}
|
|
7202
|
-
// SAFETY: This should be correct, but TS is unable to infer this.
|
|
7203
|
-
return transition[STATE_SYMBOL].routeInfos[transition.resolveIndex - 1].context;
|
|
7204
|
-
}
|
|
7205
|
-
}
|
|
7206
|
-
return this.findModel(name, value);
|
|
7207
|
-
}
|
|
7208
|
-
|
|
7209
|
-
/**
|
|
7210
|
-
@private
|
|
7211
|
-
@method deserialize
|
|
7212
|
-
@param {Object} params the parameters extracted from the URL
|
|
7213
|
-
@param {Transition} transition
|
|
7214
|
-
@return {any | Promise<any>} the model for this route.
|
|
7215
|
-
Router.js hook.
|
|
7216
|
-
*/
|
|
7217
|
-
deserialize(_params, transition) {
|
|
7218
|
-
return this.model(this._paramsFor(this.routeName, _params), transition);
|
|
7219
|
-
}
|
|
7220
|
-
|
|
7221
|
-
/**
|
|
7222
|
-
@method findModel
|
|
7223
|
-
@param {String} type the model type
|
|
7224
|
-
@param {Object} value the value passed to find
|
|
7225
|
-
@private
|
|
7226
|
-
*/
|
|
7227
|
-
findModel(type, value) {
|
|
7228
|
-
if (ENV._NO_IMPLICIT_ROUTE_MODEL) {
|
|
7229
|
-
return;
|
|
7230
|
-
}
|
|
7231
|
-
deprecateUntil(`The implicit model loading behavior for routes is deprecated. ` + `Please define an explicit model hook for ${this.fullRouteName}.`, DEPRECATIONS.DEPRECATE_IMPLICIT_ROUTE_MODEL);
|
|
7232
|
-
const store = 'store' in this ? this.store : get(this, '_store');
|
|
7233
|
-
(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.
|
|
7234
|
-
return store.find(type, value);
|
|
7235
|
-
}
|
|
7236
|
-
|
|
7237
|
-
/**
|
|
7238
|
-
A hook you can use to setup the controller for the current route.
|
|
7239
|
-
This method is called with the controller for the current route and the
|
|
7240
|
-
model supplied by the `model` hook.
|
|
7241
|
-
By default, the `setupController` hook sets the `model` property of
|
|
7242
|
-
the controller to the specified `model` when it is not `undefined`.
|
|
7243
|
-
If you implement the `setupController` hook in your Route, it will
|
|
7244
|
-
prevent this default behavior. If you want to preserve that behavior
|
|
7245
|
-
when implementing your `setupController` function, make sure to call
|
|
7246
|
-
`super`:
|
|
7247
|
-
```app/routes/photos.js
|
|
7248
|
-
import Route from '@ember/routing/route';
|
|
7249
|
-
import { service } from '@ember/service';
|
|
7250
|
-
export default class PhotosRoute extends Route {
|
|
7251
|
-
@service store;
|
|
7252
|
-
model() {
|
|
7253
|
-
return this.store.findAll('photo');
|
|
7254
|
-
}
|
|
7255
|
-
setupController(controller, model) {
|
|
7256
|
-
super.setupController(controller, model);
|
|
7257
|
-
this.controllerFor('application').set('showingPhotos', true);
|
|
7258
|
-
}
|
|
7259
|
-
}
|
|
7260
|
-
```
|
|
7261
|
-
The provided controller will be one resolved based on the name
|
|
7262
|
-
of this route.
|
|
7263
|
-
If no explicit controller is defined, Ember will automatically create one.
|
|
7264
|
-
As an example, consider the router:
|
|
7265
|
-
```app/router.js
|
|
7266
|
-
// ...
|
|
7267
|
-
Router.map(function() {
|
|
7268
|
-
this.route('post', { path: '/posts/:post_id' });
|
|
7269
|
-
});
|
|
7270
|
-
export default Router;
|
|
7271
|
-
```
|
|
7272
|
-
If you have defined a file for the post controller,
|
|
7273
|
-
the framework will use it.
|
|
7274
|
-
If it is not defined, a basic `Controller` instance would be used.
|
|
7275
|
-
@example Behavior of a basic Controller
|
|
7276
|
-
```app/routes/post.js
|
|
7277
|
-
import Route from '@ember/routing/route';
|
|
7278
|
-
export default class PostRoute extends Route {
|
|
7279
|
-
setupController(controller, model) {
|
|
7280
|
-
controller.set('model', model);
|
|
7281
|
-
}
|
|
7282
|
-
});
|
|
7283
|
-
```
|
|
7284
|
-
@method setupController
|
|
7285
|
-
@param {Controller} controller instance
|
|
7286
|
-
@param {Object} model
|
|
7287
|
-
@param {Transition} [transition]
|
|
7288
|
-
@since 1.0.0
|
|
7289
|
-
@public
|
|
7290
|
-
*/
|
|
7291
|
-
setupController(controller, context, _transition) {
|
|
7292
|
-
if (controller && context !== undefined) {
|
|
7293
|
-
set(controller, 'model', context);
|
|
7294
|
-
}
|
|
7295
|
-
}
|
|
7296
|
-
|
|
7297
|
-
/**
|
|
7298
|
-
Returns the controller of the current route, or a parent (or any ancestor)
|
|
7299
|
-
route in a route hierarchy.
|
|
7300
|
-
The controller instance must already have been created, either through entering the
|
|
7301
|
-
associated route or using `generateController`.
|
|
7302
|
-
```app/routes/post.js
|
|
7303
|
-
import Route from '@ember/routing/route';
|
|
7304
|
-
export default class PostRoute extends Route {
|
|
7305
|
-
setupController(controller, post) {
|
|
7306
|
-
super.setupController(controller, post);
|
|
7307
|
-
this.controllerFor('posts').set('currentPost', post);
|
|
7308
|
-
}
|
|
7309
|
-
}
|
|
7310
|
-
```
|
|
7311
|
-
@method controllerFor
|
|
7312
|
-
@param {String} name the name of the route or controller
|
|
7313
|
-
@return {Controller | undefined}
|
|
7314
|
-
@since 1.0.0
|
|
7315
|
-
@public
|
|
7316
|
-
*/
|
|
7317
|
-
|
|
7318
|
-
controllerFor(name, _skipAssert = false) {
|
|
7319
|
-
let owner = getOwner(this);
|
|
7320
|
-
(isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
|
|
7321
|
-
let route = owner.lookup(`route:${name}`);
|
|
7322
|
-
if (route && route.controllerName) {
|
|
7323
|
-
name = route.controllerName;
|
|
7324
|
-
}
|
|
7325
|
-
let controller = owner.lookup(`controller:${name}`);
|
|
7326
|
-
|
|
7327
|
-
// NOTE: We're specifically checking that skipAssert is true, because according
|
|
7328
|
-
// to the old API the second parameter was model. We do not want people who
|
|
7329
|
-
// passed a model to skip the assertion.
|
|
7330
|
-
(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));
|
|
7331
|
-
(isDevelopingApp() && !(controller === undefined || controller instanceof Controller) && assert(`Expected controller:${name} to be an instance of Controller`, controller === undefined || controller instanceof Controller));
|
|
7332
|
-
return controller;
|
|
7333
|
-
}
|
|
7334
|
-
|
|
7335
|
-
/**
|
|
7336
|
-
Generates a controller for a route.
|
|
7337
|
-
Example
|
|
7338
|
-
```app/routes/post.js
|
|
7339
|
-
import Route from '@ember/routing/route';
|
|
7340
|
-
export default class Post extends Route {
|
|
7341
|
-
setupController(controller, post) {
|
|
7342
|
-
super.setupController(controller, post);
|
|
7343
|
-
this.generateController('posts');
|
|
7344
|
-
}
|
|
7345
|
-
}
|
|
7346
|
-
```
|
|
7347
|
-
@method generateController
|
|
7348
|
-
@param {String} name the name of the controller
|
|
7349
|
-
@private
|
|
7350
|
-
*/
|
|
7351
|
-
generateController(name) {
|
|
7352
|
-
let owner = getOwner(this);
|
|
7353
|
-
(isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
|
|
7354
|
-
return generateController(owner, name);
|
|
7355
|
-
}
|
|
7356
|
-
|
|
7357
|
-
/**
|
|
7358
|
-
Returns the resolved model of a parent (or any ancestor) route
|
|
7359
|
-
in a route hierarchy. During a transition, all routes
|
|
7360
|
-
must resolve a model object, and if a route
|
|
7361
|
-
needs access to a parent route's model in order to
|
|
7362
|
-
resolve a model (or just reuse the model from a parent),
|
|
7363
|
-
it can call `this.modelFor(theNameOfParentRoute)` to
|
|
7364
|
-
retrieve it. If the ancestor route's model was a promise,
|
|
7365
|
-
its resolved result is returned.
|
|
7366
|
-
Example
|
|
7367
|
-
```app/router.js
|
|
7368
|
-
// ...
|
|
7369
|
-
Router.map(function() {
|
|
7370
|
-
this.route('post', { path: '/posts/:post_id' }, function() {
|
|
7371
|
-
this.route('comments');
|
|
7372
|
-
});
|
|
7373
|
-
});
|
|
7374
|
-
export default Router;
|
|
7375
|
-
```
|
|
7376
|
-
```app/routes/post/comments.js
|
|
7377
|
-
import Route from '@ember/routing/route';
|
|
7378
|
-
export default class PostCommentsRoute extends Route {
|
|
7379
|
-
model() {
|
|
7380
|
-
let post = this.modelFor('post');
|
|
7381
|
-
return post.comments;
|
|
7382
|
-
}
|
|
7383
|
-
}
|
|
7384
|
-
```
|
|
7385
|
-
@method modelFor
|
|
7386
|
-
@param {String} name the name of the route
|
|
7387
|
-
@return {Object} the model object
|
|
7388
|
-
@since 1.0.0
|
|
7389
|
-
@public
|
|
7390
|
-
*/
|
|
7391
|
-
modelFor(_name) {
|
|
7392
|
-
let name;
|
|
7393
|
-
let owner = getOwner(this);
|
|
7394
|
-
(isDevelopingApp() && !(owner instanceof EmberEngineInstance) && assert('Expected router owner to be an EngineInstance', owner instanceof EmberEngineInstance));
|
|
7395
|
-
let transition = this._router && this._router._routerMicrolib ? this._router._routerMicrolib.activeTransition : undefined;
|
|
7396
|
-
|
|
7397
|
-
// Only change the route name when there is an active transition.
|
|
7398
|
-
// Otherwise, use the passed in route name.
|
|
7399
|
-
if (owner.routable && transition !== undefined) {
|
|
7400
|
-
name = getEngineRouteName(owner, _name);
|
|
7401
|
-
} else {
|
|
7402
|
-
name = _name;
|
|
7403
|
-
}
|
|
7404
|
-
let route = owner.lookup(`route:${name}`);
|
|
7405
|
-
// If we are mid-transition, we want to try and look up
|
|
7406
|
-
// resolved parent contexts on the current transitionEvent.
|
|
7407
|
-
if (transition !== undefined && transition !== null) {
|
|
7408
|
-
let modelLookupName = route && route.routeName || name;
|
|
7409
|
-
if (Object.prototype.hasOwnProperty.call(transition.resolvedModels, modelLookupName)) {
|
|
7410
|
-
return transition.resolvedModels[modelLookupName];
|
|
7411
|
-
}
|
|
7412
|
-
}
|
|
7413
|
-
return route?.currentModel;
|
|
7414
|
-
}
|
|
7415
|
-
[RENDER_STATE] = undefined;
|
|
7416
|
-
|
|
7417
|
-
/**
|
|
7418
|
-
`this[RENDER]` is used to set up the rendering option for the outlet state.
|
|
7419
|
-
@method this[RENDER]
|
|
7420
|
-
@private
|
|
7421
|
-
*/
|
|
7422
|
-
[RENDER]() {
|
|
7423
|
-
this[RENDER_STATE] = buildRenderState(this);
|
|
7424
|
-
once(this._router, '_setOutlets');
|
|
7425
|
-
}
|
|
7426
|
-
willDestroy() {
|
|
7427
|
-
this.teardownViews();
|
|
7428
|
-
}
|
|
7429
|
-
|
|
7430
|
-
/**
|
|
7431
|
-
@private
|
|
7432
|
-
@method teardownViews
|
|
7433
|
-
*/
|
|
7434
|
-
teardownViews() {
|
|
7435
|
-
if (this[RENDER_STATE]) {
|
|
7436
|
-
this[RENDER_STATE] = undefined;
|
|
7437
|
-
once(this._router, '_setOutlets');
|
|
7438
|
-
}
|
|
7439
|
-
}
|
|
7440
|
-
|
|
7441
|
-
/**
|
|
7442
|
-
Allows you to produce custom metadata for the route.
|
|
7443
|
-
The return value of this method will be attached to
|
|
7444
|
-
its corresponding RouteInfoWithAttributes object.
|
|
7445
|
-
Example
|
|
7446
|
-
```app/routes/posts/index.js
|
|
7447
|
-
import Route from '@ember/routing/route';
|
|
7448
|
-
export default class PostsIndexRoute extends Route {
|
|
7449
|
-
buildRouteInfoMetadata() {
|
|
7450
|
-
return { title: 'Posts Page' }
|
|
7451
|
-
}
|
|
7452
|
-
}
|
|
7453
|
-
```
|
|
7454
|
-
```app/routes/application.js
|
|
7455
|
-
import Route from '@ember/routing/route';
|
|
7456
|
-
import { service } from '@ember/service';
|
|
7457
|
-
export default class ApplicationRoute extends Route {
|
|
7458
|
-
@service router
|
|
7459
|
-
constructor() {
|
|
7460
|
-
super(...arguments);
|
|
7461
|
-
this.router.on('routeDidChange', transition => {
|
|
7462
|
-
document.title = transition.to.metadata.title;
|
|
7463
|
-
// would update document's title to "Posts Page"
|
|
7464
|
-
});
|
|
7465
|
-
}
|
|
7466
|
-
}
|
|
7467
|
-
```
|
|
7468
|
-
@method buildRouteInfoMetadata
|
|
7469
|
-
@return any
|
|
7470
|
-
@since 3.10.0
|
|
7471
|
-
@public
|
|
7472
|
-
*/
|
|
7473
|
-
|
|
7474
|
-
buildRouteInfoMetadata() {}
|
|
7475
|
-
_paramsFor(routeName, params) {
|
|
7476
|
-
let transition = this._router._routerMicrolib.activeTransition;
|
|
7477
|
-
if (transition !== undefined) {
|
|
7478
|
-
return this.paramsFor(routeName);
|
|
7479
|
-
}
|
|
7480
|
-
return params;
|
|
7481
|
-
}
|
|
7482
|
-
|
|
7483
|
-
/** @deprecated Manually define your own store, such as with `@service store` */
|
|
7484
|
-
get _store() {
|
|
7485
|
-
const owner = getOwner(this);
|
|
7486
|
-
(isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
|
|
7487
|
-
let routeName = this.routeName;
|
|
7488
|
-
return {
|
|
7489
|
-
find(name, value) {
|
|
7490
|
-
let modelClass = owner.factoryFor(`model:${name}`);
|
|
7491
|
-
(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)));
|
|
7492
|
-
if (!modelClass) {
|
|
7493
|
-
return;
|
|
7494
|
-
}
|
|
7495
|
-
modelClass = modelClass.class;
|
|
7496
|
-
(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'));
|
|
7497
|
-
return modelClass.find(value);
|
|
7498
|
-
}
|
|
7499
|
-
};
|
|
7500
|
-
}
|
|
7501
|
-
|
|
7502
|
-
/**
|
|
7503
|
-
@private
|
|
7504
|
-
@property _qp
|
|
7505
|
-
*/
|
|
7506
|
-
static {
|
|
7507
|
-
decorateMethodV2(this.prototype, "_store", [computed]);
|
|
7508
|
-
}
|
|
7509
|
-
get _qp() {
|
|
7510
|
-
let combinedQueryParameterConfiguration = {};
|
|
7511
|
-
let controllerName = this.controllerName || this.routeName;
|
|
7512
|
-
let owner = getOwner(this);
|
|
7513
|
-
(isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
|
|
7514
|
-
let controller = owner.lookup(`controller:${controllerName}`);
|
|
7515
|
-
let queryParameterConfiguraton = get(this, 'queryParams');
|
|
7516
|
-
let hasRouterDefinedQueryParams = Object.keys(queryParameterConfiguraton).length > 0;
|
|
7517
|
-
if (controller) {
|
|
7518
|
-
(isDevelopingApp() && !(controller instanceof Controller) && assert('Expected an instance of controller', controller instanceof Controller)); // the developer has authored a controller class in their application for
|
|
7519
|
-
// this route find its query params and normalize their object shape them
|
|
7520
|
-
// merge in the query params for the route. As a mergedProperty,
|
|
7521
|
-
// Route#queryParams is always at least `{}`
|
|
7522
|
-
let controllerDefinedQueryParameterConfiguration = get(controller, 'queryParams') || [];
|
|
7523
|
-
let normalizedControllerQueryParameterConfiguration = normalizeControllerQueryParams(controllerDefinedQueryParameterConfiguration);
|
|
7524
|
-
combinedQueryParameterConfiguration = mergeEachQueryParams(normalizedControllerQueryParameterConfiguration, queryParameterConfiguraton);
|
|
7525
|
-
} else if (hasRouterDefinedQueryParams) {
|
|
7526
|
-
// the developer has not defined a controller but *has* supplied route query params.
|
|
7527
|
-
// Generate a class for them so we can later insert default values
|
|
7528
|
-
controller = generateController(owner, controllerName);
|
|
7529
|
-
combinedQueryParameterConfiguration = queryParameterConfiguraton;
|
|
7530
|
-
}
|
|
7531
|
-
let qps = [];
|
|
7532
|
-
let map = {};
|
|
7533
|
-
let propertyNames = [];
|
|
7534
|
-
for (let propName in combinedQueryParameterConfiguration) {
|
|
7535
|
-
if (!Object.prototype.hasOwnProperty.call(combinedQueryParameterConfiguration, propName)) {
|
|
7536
|
-
continue;
|
|
7537
|
-
}
|
|
7538
|
-
|
|
7539
|
-
// to support the dubious feature of using unknownProperty
|
|
7540
|
-
// on queryParams configuration
|
|
7541
|
-
if (propName === 'unknownProperty' || propName === '_super') {
|
|
7542
|
-
// possible todo: issue deprecation warning?
|
|
7543
|
-
continue;
|
|
7544
|
-
}
|
|
7545
|
-
let desc = combinedQueryParameterConfiguration[propName];
|
|
7546
|
-
(isDevelopingApp() && !(desc) && assert(`[BUG] missing query parameter configuration for ${propName}`, desc));
|
|
7547
|
-
let scope = desc.scope || 'model';
|
|
7548
|
-
let parts = undefined;
|
|
7549
|
-
if (scope === 'controller') {
|
|
7550
|
-
parts = [];
|
|
7551
|
-
}
|
|
7552
|
-
let urlKey = desc.as || this.serializeQueryParamKey(propName);
|
|
7553
|
-
let defaultValue = get(controller, propName);
|
|
7554
|
-
defaultValue = copyDefaultValue(defaultValue);
|
|
7555
|
-
let type = desc.type || typeOf(defaultValue);
|
|
7556
|
-
let defaultValueSerialized = this.serializeQueryParam(defaultValue, urlKey, type);
|
|
7557
|
-
let scopedPropertyName = `${controllerName}:${propName}`;
|
|
7558
|
-
let qp = {
|
|
7559
|
-
undecoratedDefaultValue: get(controller, propName),
|
|
7560
|
-
defaultValue,
|
|
7561
|
-
serializedDefaultValue: defaultValueSerialized,
|
|
7562
|
-
serializedValue: defaultValueSerialized,
|
|
7563
|
-
type,
|
|
7564
|
-
urlKey,
|
|
7565
|
-
prop: propName,
|
|
7566
|
-
scopedPropertyName,
|
|
7567
|
-
controllerName,
|
|
7568
|
-
route: this,
|
|
7569
|
-
parts,
|
|
7570
|
-
// provided later when stashNames is called if 'model' scope
|
|
7571
|
-
values: null,
|
|
7572
|
-
// provided later when setup is called. no idea why.
|
|
7573
|
-
scope
|
|
7574
|
-
};
|
|
7575
|
-
map[propName] = map[urlKey] = map[scopedPropertyName] = qp;
|
|
7576
|
-
qps.push(qp);
|
|
7577
|
-
propertyNames.push(propName);
|
|
7578
|
-
}
|
|
7579
|
-
return {
|
|
7580
|
-
qps,
|
|
7581
|
-
map,
|
|
7582
|
-
propertyNames,
|
|
7583
|
-
states: {
|
|
7584
|
-
/*
|
|
7585
|
-
Called when a query parameter changes in the URL, this route cares
|
|
7586
|
-
about that query parameter, but the route is not currently
|
|
7587
|
-
in the active route hierarchy.
|
|
7588
|
-
*/
|
|
7589
|
-
inactive: (prop, value) => {
|
|
7590
|
-
let qp = map[prop];
|
|
7591
|
-
(isDevelopingApp() && !(qp) && assert('expected inactive callback to only be called for registered qps', qp));
|
|
7592
|
-
this._qpChanged(prop, value, qp);
|
|
7593
|
-
},
|
|
7594
|
-
/*
|
|
7595
|
-
Called when a query parameter changes in the URL, this route cares
|
|
7596
|
-
about that query parameter, and the route is currently
|
|
7597
|
-
in the active route hierarchy.
|
|
7598
|
-
*/
|
|
7599
|
-
active: (prop, value) => {
|
|
7600
|
-
let qp = map[prop];
|
|
7601
|
-
(isDevelopingApp() && !(qp) && assert('expected active callback to only be called for registered qps', qp));
|
|
7602
|
-
this._qpChanged(prop, value, qp);
|
|
7603
|
-
return this._activeQPChanged(qp, value);
|
|
7604
|
-
},
|
|
7605
|
-
/*
|
|
7606
|
-
Called when a value of a query parameter this route handles changes in a controller
|
|
7607
|
-
and the route is currently in the active route hierarchy.
|
|
7608
|
-
*/
|
|
7609
|
-
allowOverrides: (prop, value) => {
|
|
7610
|
-
let qp = map[prop];
|
|
7611
|
-
(isDevelopingApp() && !(qp) && assert('expected allowOverrides callback to only be called for registered qps', qp));
|
|
7612
|
-
this._qpChanged(prop, value, qp);
|
|
7613
|
-
return this._updatingQPChanged(qp);
|
|
7614
|
-
}
|
|
7615
|
-
}
|
|
7616
|
-
};
|
|
7617
|
-
}
|
|
7618
|
-
|
|
7619
|
-
// Set in reopen
|
|
7620
|
-
static {
|
|
7621
|
-
decorateMethodV2(this.prototype, "_qp", [computed]);
|
|
7622
|
-
}
|
|
7623
|
-
/**
|
|
7624
|
-
Sends an action to the router, which will delegate it to the currently
|
|
7625
|
-
active route hierarchy per the bubbling rules explained under `actions`.
|
|
7626
|
-
Example
|
|
7627
|
-
```app/router.js
|
|
7628
|
-
// ...
|
|
7629
|
-
Router.map(function() {
|
|
7630
|
-
this.route('index');
|
|
7631
|
-
});
|
|
7632
|
-
export default Router;
|
|
7633
|
-
```
|
|
7634
|
-
```app/routes/application.js
|
|
7635
|
-
import Route from '@ember/routing/route';
|
|
7636
|
-
import { action } from '@ember/object';
|
|
7637
|
-
export default class ApplicationRoute extends Route {
|
|
7638
|
-
@action
|
|
7639
|
-
track(arg) {
|
|
7640
|
-
console.log(arg, 'was clicked');
|
|
7641
|
-
}
|
|
7642
|
-
}
|
|
7643
|
-
```
|
|
7644
|
-
```app/routes/index.js
|
|
7645
|
-
import Route from '@ember/routing/route';
|
|
7646
|
-
import { action } from '@ember/object';
|
|
7647
|
-
export default class IndexRoute extends Route {
|
|
7648
|
-
@action
|
|
7649
|
-
trackIfDebug(arg) {
|
|
7650
|
-
if (debug) {
|
|
7651
|
-
this.send('track', arg);
|
|
7652
|
-
}
|
|
7653
|
-
}
|
|
7654
|
-
}
|
|
7655
|
-
```
|
|
7656
|
-
@method send
|
|
7657
|
-
@param {String} name the name of the action to trigger
|
|
7658
|
-
@param {...*} args
|
|
7659
|
-
@since 1.0.0
|
|
7660
|
-
@public
|
|
7661
|
-
*/
|
|
7662
|
-
// Set with reopen to override parent behavior
|
|
7663
|
-
}
|
|
7664
|
-
function getRenderState(route) {
|
|
7665
|
-
return route[RENDER_STATE];
|
|
7666
|
-
}
|
|
7667
|
-
function buildRenderState(route) {
|
|
7668
|
-
let owner = getOwner(route);
|
|
7669
|
-
(isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
|
|
7670
|
-
let name = route.routeName;
|
|
7671
|
-
let controller = owner.lookup(`controller:${route.controllerName || name}`);
|
|
7672
|
-
(isDevelopingApp() && !(controller instanceof Controller) && assert('Expected an instance of controller', controller instanceof Controller));
|
|
7673
|
-
let model = route.currentModel;
|
|
7674
|
-
let template = owner.lookup(`template:${route.templateName || name}`);
|
|
7675
|
-
let render = {
|
|
7676
|
-
owner,
|
|
7677
|
-
into: undefined,
|
|
7678
|
-
outlet: 'main',
|
|
7679
|
-
name,
|
|
7680
|
-
controller,
|
|
7681
|
-
model,
|
|
7682
|
-
template: template?.(owner) ?? route._topLevelViewTemplate(owner)
|
|
7683
|
-
};
|
|
7684
|
-
if (isDevelopingApp()) {
|
|
7685
|
-
let LOG_VIEW_LOOKUPS = get(route._router, 'namespace.LOG_VIEW_LOOKUPS');
|
|
7686
|
-
if (LOG_VIEW_LOOKUPS && !template) {
|
|
7687
|
-
info(`Could not find "${name}" template. Nothing will be rendered`, {
|
|
7688
|
-
fullName: `template:${name}`
|
|
7689
|
-
});
|
|
7690
|
-
}
|
|
7691
|
-
}
|
|
7692
|
-
return render;
|
|
7693
|
-
}
|
|
7694
|
-
function getFullQueryParams(router, state) {
|
|
7695
|
-
if (state.fullQueryParams) {
|
|
7696
|
-
return state.fullQueryParams;
|
|
7697
|
-
}
|
|
7698
|
-
let haveAllRouteInfosResolved = state.routeInfos.every(routeInfo => routeInfo.route);
|
|
7699
|
-
let fullQueryParamsState = {
|
|
7700
|
-
...state.queryParams
|
|
7701
|
-
};
|
|
7702
|
-
router._deserializeQueryParams(state.routeInfos, fullQueryParamsState);
|
|
7703
|
-
|
|
7704
|
-
// only cache query params state if all routeinfos have resolved; it's possible
|
|
7705
|
-
// for lazy routes to not have resolved when `getFullQueryParams` is called, so
|
|
7706
|
-
// we wait until all routes have resolved prior to caching query params state
|
|
7707
|
-
if (haveAllRouteInfosResolved) {
|
|
7708
|
-
state.fullQueryParams = fullQueryParamsState;
|
|
7709
|
-
}
|
|
7710
|
-
return fullQueryParamsState;
|
|
7711
|
-
}
|
|
7712
|
-
function getQueryParamsFor(route, state) {
|
|
7713
|
-
state.queryParamsFor = state.queryParamsFor || {};
|
|
7714
|
-
let name = route.fullRouteName;
|
|
7715
|
-
let existing = state.queryParamsFor[name];
|
|
7716
|
-
if (existing) {
|
|
7717
|
-
return existing;
|
|
7718
|
-
}
|
|
7719
|
-
let fullQueryParams = getFullQueryParams(route._router, state);
|
|
7720
|
-
let params = state.queryParamsFor[name] = {};
|
|
7721
|
-
|
|
7722
|
-
// Copy over all the query params for this route/controller into params hash.
|
|
7723
|
-
// SAFETY: Since `_qp` is protected we can't infer the type
|
|
7724
|
-
let qps = get(route, '_qp').qps;
|
|
7725
|
-
for (let qp of qps) {
|
|
7726
|
-
// Put deserialized qp on params hash.
|
|
7727
|
-
let qpValueWasPassedIn = (qp.prop in fullQueryParams);
|
|
7728
|
-
params[qp.prop] = qpValueWasPassedIn ? fullQueryParams[qp.prop] : copyDefaultValue(qp.defaultValue);
|
|
7729
|
-
}
|
|
7730
|
-
return params;
|
|
7731
|
-
}
|
|
7732
|
-
|
|
7733
|
-
// FIXME: This should probably actually return a `NativeArray` if the passed in value is an Array.
|
|
7734
|
-
function copyDefaultValue(value) {
|
|
7735
|
-
if (Array.isArray(value)) {
|
|
7736
|
-
// SAFETY: We lost the type data about the array if we don't cast.
|
|
7737
|
-
return A(value.slice());
|
|
7738
|
-
}
|
|
7739
|
-
return value;
|
|
7740
|
-
}
|
|
7741
|
-
|
|
7742
|
-
/*
|
|
7743
|
-
Merges all query parameters from a controller with those from
|
|
7744
|
-
a route, returning a new object and avoiding any mutations to
|
|
7745
|
-
the existing objects.
|
|
7746
|
-
*/
|
|
7747
|
-
function mergeEachQueryParams(controllerQP, routeQP) {
|
|
7748
|
-
let qps = {};
|
|
7749
|
-
let keysAlreadyMergedOrSkippable = {
|
|
7750
|
-
defaultValue: true,
|
|
7751
|
-
type: true,
|
|
7752
|
-
scope: true,
|
|
7753
|
-
as: true
|
|
7754
|
-
};
|
|
7755
|
-
|
|
7756
|
-
// first loop over all controller qps, merging them with any matching route qps
|
|
7757
|
-
// into a new empty object to avoid mutating.
|
|
7758
|
-
for (let cqpName in controllerQP) {
|
|
7759
|
-
if (!Object.prototype.hasOwnProperty.call(controllerQP, cqpName)) {
|
|
7760
|
-
continue;
|
|
7761
|
-
}
|
|
7762
|
-
qps[cqpName] = {
|
|
7763
|
-
...controllerQP[cqpName],
|
|
7764
|
-
...routeQP[cqpName]
|
|
7765
|
-
};
|
|
7766
|
-
|
|
7767
|
-
// allows us to skip this QP when we check route QPs.
|
|
7768
|
-
keysAlreadyMergedOrSkippable[cqpName] = true;
|
|
7769
|
-
}
|
|
7770
|
-
|
|
7771
|
-
// loop over all route qps, skipping those that were merged in the first pass
|
|
7772
|
-
// because they also appear in controller qps
|
|
7773
|
-
for (let rqpName in routeQP) {
|
|
7774
|
-
if (!Object.prototype.hasOwnProperty.call(routeQP, rqpName) || keysAlreadyMergedOrSkippable[rqpName]) {
|
|
7775
|
-
continue;
|
|
7776
|
-
}
|
|
7777
|
-
qps[rqpName] = {
|
|
7778
|
-
...routeQP[rqpName],
|
|
7779
|
-
...controllerQP[rqpName]
|
|
7780
|
-
};
|
|
7781
|
-
}
|
|
7782
|
-
return qps;
|
|
7783
|
-
}
|
|
7784
|
-
function addQueryParamsObservers(controller, propNames) {
|
|
7785
|
-
propNames.forEach(prop => {
|
|
7786
|
-
if (descriptorForProperty(controller, prop) === undefined) {
|
|
7787
|
-
let desc = lookupDescriptor(controller, prop);
|
|
7788
|
-
if (desc !== null && (typeof desc.get === 'function' || typeof desc.set === 'function')) {
|
|
7789
|
-
defineProperty(controller, prop, dependentKeyCompat({
|
|
7790
|
-
get: desc.get,
|
|
7791
|
-
set: desc.set
|
|
7792
|
-
}));
|
|
7793
|
-
}
|
|
7794
|
-
}
|
|
7795
|
-
addObserver(controller, `${prop}.[]`, controller, controller._qpChanged, false);
|
|
7796
|
-
});
|
|
7797
|
-
}
|
|
7798
|
-
function getEngineRouteName(engine, routeName) {
|
|
7799
|
-
if (engine.routable) {
|
|
7800
|
-
let prefix = engine.mountPoint;
|
|
7801
|
-
if (routeName === 'application') {
|
|
7802
|
-
return prefix;
|
|
7803
|
-
} else {
|
|
7804
|
-
return `${prefix}.${routeName}`;
|
|
7805
|
-
}
|
|
7806
|
-
}
|
|
7807
|
-
return routeName;
|
|
7808
|
-
}
|
|
7809
|
-
const defaultSerialize = Route.prototype.serialize;
|
|
7810
|
-
function hasDefaultSerialize(route) {
|
|
7811
|
-
return route.serialize === defaultSerialize;
|
|
7812
|
-
}
|
|
7813
|
-
|
|
7814
|
-
// Set these here so they can be overridden with extend
|
|
7815
|
-
Route.reopen({
|
|
7816
|
-
mergedProperties: ['queryParams'],
|
|
7817
|
-
queryParams: {},
|
|
7818
|
-
templateName: null,
|
|
7819
|
-
controllerName: null,
|
|
7820
|
-
send(...args) {
|
|
7821
|
-
(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));
|
|
7822
|
-
if (this._router && this._router._routerMicrolib || !isTesting()) {
|
|
7823
|
-
this._router.send(...args);
|
|
7824
|
-
} else {
|
|
7825
|
-
let name = args.shift();
|
|
7826
|
-
let action = this.actions[name];
|
|
7827
|
-
if (action) {
|
|
7828
|
-
return action.apply(this, args);
|
|
7829
|
-
}
|
|
7830
|
-
}
|
|
7831
|
-
},
|
|
7832
|
-
/**
|
|
7833
|
-
The controller associated with this route.
|
|
7834
|
-
Example
|
|
7835
|
-
```app/routes/form.js
|
|
7836
|
-
import Route from '@ember/routing/route';
|
|
7837
|
-
import { action } from '@ember/object';
|
|
7838
|
-
export default class FormRoute extends Route {
|
|
7839
|
-
@action
|
|
7840
|
-
willTransition(transition) {
|
|
7841
|
-
if (this.controller.get('userHasEnteredData') &&
|
|
7842
|
-
!confirm('Are you sure you want to abandon progress?')) {
|
|
7843
|
-
transition.abort();
|
|
7844
|
-
} else {
|
|
7845
|
-
// Bubble the `willTransition` action so that
|
|
7846
|
-
// parent routes can decide whether or not to abort.
|
|
7847
|
-
return true;
|
|
7848
|
-
}
|
|
7849
|
-
}
|
|
7850
|
-
}
|
|
7851
|
-
```
|
|
7852
|
-
@property controller
|
|
7853
|
-
@type Controller
|
|
7854
|
-
@since 1.6.0
|
|
7855
|
-
@public
|
|
7856
|
-
*/
|
|
7857
|
-
|
|
7858
|
-
actions: {
|
|
7859
|
-
/**
|
|
7860
|
-
This action is called when one or more query params have changed. Bubbles.
|
|
7861
|
-
@method queryParamsDidChange
|
|
7862
|
-
@param changed {Object} Keys are names of query params that have changed.
|
|
7863
|
-
@param totalPresent {Object} Keys are names of query params that are currently set.
|
|
7864
|
-
@param removed {Object} Keys are names of query params that have been removed.
|
|
7865
|
-
@returns {boolean}
|
|
7866
|
-
@private
|
|
7867
|
-
*/
|
|
7868
|
-
queryParamsDidChange(changed, _totalPresent, removed) {
|
|
7869
|
-
// SAFETY: Since `_qp` is protected we can't infer the type
|
|
7870
|
-
let qpMap = get(this, '_qp').map;
|
|
7871
|
-
let totalChanged = Object.keys(changed).concat(Object.keys(removed));
|
|
7872
|
-
for (let change of totalChanged) {
|
|
7873
|
-
let qp = qpMap[change];
|
|
7874
|
-
if (qp) {
|
|
7875
|
-
let options = this._optionsForQueryParam(qp);
|
|
7876
|
-
(isDevelopingApp() && !(options && typeof options === 'object') && assert('options exists', options && typeof options === 'object'));
|
|
7877
|
-
if (get(options, 'refreshModel') && this._router.currentState) {
|
|
7878
|
-
this.refresh();
|
|
7879
|
-
break;
|
|
7880
|
-
}
|
|
7881
|
-
}
|
|
7882
|
-
}
|
|
7883
|
-
return true;
|
|
7884
|
-
},
|
|
7885
|
-
finalizeQueryParamChange(params, finalParams, transition) {
|
|
7886
|
-
if (this.fullRouteName !== 'application') {
|
|
7887
|
-
return true;
|
|
7888
|
-
}
|
|
7889
|
-
|
|
7890
|
-
// Transition object is absent for intermediate transitions.
|
|
7891
|
-
if (!transition) {
|
|
7892
|
-
return;
|
|
7893
|
-
}
|
|
7894
|
-
let routeInfos = transition[STATE_SYMBOL].routeInfos;
|
|
7895
|
-
let router = this._router;
|
|
7896
|
-
let qpMeta = router._queryParamsFor(routeInfos);
|
|
7897
|
-
let changes = router._qpUpdates;
|
|
7898
|
-
let qpUpdated = false;
|
|
7899
|
-
let replaceUrl;
|
|
7900
|
-
stashParamNames(router, routeInfos);
|
|
7901
|
-
for (let qp of qpMeta.qps) {
|
|
7902
|
-
let route = qp.route;
|
|
7903
|
-
let controller = route.controller;
|
|
7904
|
-
let presentKey = qp.urlKey in params && qp.urlKey;
|
|
7905
|
-
|
|
7906
|
-
// Do a reverse lookup to see if the changed query
|
|
7907
|
-
// param URL key corresponds to a QP property on
|
|
7908
|
-
// this controller.
|
|
7909
|
-
let value;
|
|
7910
|
-
let svalue;
|
|
7911
|
-
if (changes.has(qp.urlKey)) {
|
|
7912
|
-
// Value updated in/before setupController
|
|
7913
|
-
value = get(controller, qp.prop);
|
|
7914
|
-
svalue = route.serializeQueryParam(value, qp.urlKey, qp.type);
|
|
7915
|
-
} else {
|
|
7916
|
-
if (presentKey) {
|
|
7917
|
-
svalue = params[presentKey];
|
|
7918
|
-
if (svalue !== undefined) {
|
|
7919
|
-
value = route.deserializeQueryParam(svalue, qp.urlKey, qp.type);
|
|
7920
|
-
}
|
|
7921
|
-
} else {
|
|
7922
|
-
// No QP provided; use default value.
|
|
7923
|
-
svalue = qp.serializedDefaultValue;
|
|
7924
|
-
value = copyDefaultValue(qp.defaultValue);
|
|
7925
|
-
}
|
|
7926
|
-
}
|
|
7927
|
-
|
|
7928
|
-
// SAFETY: Since `_qp` is protected we can't infer the type
|
|
7929
|
-
controller._qpDelegate = get(route, '_qp').states.inactive;
|
|
7930
|
-
let thisQueryParamChanged = svalue !== qp.serializedValue;
|
|
7931
|
-
if (thisQueryParamChanged) {
|
|
7932
|
-
if (transition.queryParamsOnly && replaceUrl !== false) {
|
|
7933
|
-
let options = route._optionsForQueryParam(qp);
|
|
7934
|
-
let replaceConfigValue = get(options, 'replace');
|
|
7935
|
-
if (replaceConfigValue) {
|
|
7936
|
-
replaceUrl = true;
|
|
7937
|
-
} else if (replaceConfigValue === false) {
|
|
7938
|
-
// Explicit pushState wins over any other replaceStates.
|
|
7939
|
-
replaceUrl = false;
|
|
7940
|
-
}
|
|
7941
|
-
}
|
|
7942
|
-
set(controller, qp.prop, value);
|
|
7943
|
-
qpUpdated = true;
|
|
7944
|
-
}
|
|
7945
|
-
|
|
7946
|
-
// Stash current serialized value of controller.
|
|
7947
|
-
qp.serializedValue = svalue;
|
|
7948
|
-
let thisQueryParamHasDefaultValue = qp.serializedDefaultValue === svalue;
|
|
7949
|
-
if (!thisQueryParamHasDefaultValue) {
|
|
7950
|
-
finalParams.push({
|
|
7951
|
-
value: svalue,
|
|
7952
|
-
visible: true,
|
|
7953
|
-
key: presentKey || qp.urlKey
|
|
7954
|
-
});
|
|
7955
|
-
}
|
|
7956
|
-
}
|
|
7957
|
-
|
|
7958
|
-
// Some QPs have been updated, and those changes need to be propogated
|
|
7959
|
-
// immediately. Eventually, we should work on making this async somehow.
|
|
7960
|
-
if (qpUpdated === true) {
|
|
7961
|
-
flushAsyncObservers(false);
|
|
7962
|
-
}
|
|
7963
|
-
if (replaceUrl) {
|
|
7964
|
-
transition.method('replace');
|
|
7965
|
-
}
|
|
7966
|
-
qpMeta.qps.forEach(qp => {
|
|
7967
|
-
// SAFETY: Since `_qp` is protected we can't infer the type
|
|
7968
|
-
let routeQpMeta = get(qp.route, '_qp');
|
|
7969
|
-
let finalizedController = qp.route.controller;
|
|
7970
|
-
finalizedController['_qpDelegate'] = get(routeQpMeta, 'states.active');
|
|
7971
|
-
});
|
|
7972
|
-
router._qpUpdates.clear();
|
|
7973
|
-
return;
|
|
7974
|
-
}
|
|
7975
|
-
}
|
|
7976
|
-
});
|
|
7977
|
-
|
|
7978
|
-
/**
|
|
7979
|
-
@module @ember/routing/router
|
|
7980
|
-
*/
|
|
7981
|
-
|
|
7982
|
-
function defaultDidTransition(infos) {
|
|
7983
|
-
updatePaths(this);
|
|
7984
|
-
this._cancelSlowTransitionTimer();
|
|
7985
|
-
this.notifyPropertyChange('url');
|
|
7986
|
-
this.set('currentState', this.targetState);
|
|
7987
|
-
if (isDevelopingApp()) {
|
|
7988
|
-
// @ts-expect-error namespace isn't public
|
|
7989
|
-
if (this.namespace.LOG_TRANSITIONS) {
|
|
7990
|
-
// eslint-disable-next-line no-console
|
|
7991
|
-
console.log(`Transitioned into '${EmberRouter._routePath(infos)}'`);
|
|
7992
|
-
}
|
|
7993
|
-
}
|
|
7994
|
-
}
|
|
7995
|
-
function defaultWillTransition(oldInfos, newInfos) {
|
|
7996
|
-
if (isDevelopingApp()) {
|
|
7997
|
-
// @ts-expect-error namespace isn't public
|
|
7998
|
-
if (this.namespace.LOG_TRANSITIONS) {
|
|
7999
|
-
// eslint-disable-next-line no-console
|
|
8000
|
-
console.log(`Preparing to transition from '${EmberRouter._routePath(oldInfos)}' to '${EmberRouter._routePath(newInfos)}'`);
|
|
8001
|
-
}
|
|
8002
|
-
}
|
|
8003
|
-
}
|
|
8004
|
-
let freezeRouteInfo;
|
|
8005
|
-
if (isDevelopingApp()) {
|
|
8006
|
-
freezeRouteInfo = transition => {
|
|
8007
|
-
if (transition.from !== null && !Object.isFrozen(transition.from)) {
|
|
8008
|
-
Object.freeze(transition.from);
|
|
8009
|
-
}
|
|
8010
|
-
if (transition.to !== null && !Object.isFrozen(transition.to)) {
|
|
8011
|
-
Object.freeze(transition.to);
|
|
8012
|
-
}
|
|
8013
|
-
};
|
|
8014
|
-
}
|
|
8015
|
-
function K() {
|
|
8016
|
-
return this;
|
|
8017
|
-
}
|
|
8018
|
-
const {
|
|
8019
|
-
slice
|
|
8020
|
-
} = Array.prototype;
|
|
8021
|
-
|
|
8022
|
-
/**
|
|
8023
|
-
The `EmberRouter` class manages the application state and URLs. Refer to
|
|
8024
|
-
the [routing guide](https://guides.emberjs.com/release/routing/) for documentation.
|
|
8025
|
-
|
|
8026
|
-
@class EmberRouter
|
|
8027
|
-
@extends EmberObject
|
|
8028
|
-
@uses Evented
|
|
8029
|
-
@public
|
|
8030
|
-
*/
|
|
8031
|
-
class EmberRouter extends EmberObject.extend(Evented) {
|
|
8032
|
-
/**
|
|
8033
|
-
Represents the URL of the root of the application, often '/'. This prefix is
|
|
8034
|
-
assumed on all routes defined on this router.
|
|
8035
|
-
@property rootURL
|
|
8036
|
-
@default '/'
|
|
8037
|
-
@public
|
|
8038
|
-
*/
|
|
8039
|
-
// Set with reopen to allow overriding via extend
|
|
8040
|
-
|
|
8041
|
-
/**
|
|
8042
|
-
The `location` property determines the type of URL's that your
|
|
8043
|
-
application will use.
|
|
8044
|
-
The following location types are currently available:
|
|
8045
|
-
* `history` - use the browser's history API to make the URLs look just like any standard URL
|
|
8046
|
-
* `hash` - use `#` to separate the server part of the URL from the Ember part: `/blog/#/posts/new`
|
|
8047
|
-
* `none` - do not store the Ember URL in the actual browser URL (mainly used for testing)
|
|
8048
|
-
* `auto` - use the best option based on browser capabilities: `history` if possible, then `hash` if possible, otherwise `none`
|
|
8049
|
-
This value is defaulted to `history` by the `locationType` setting of `/config/environment.js`
|
|
8050
|
-
@property location
|
|
8051
|
-
@default 'hash'
|
|
8052
|
-
@see {Location}
|
|
8053
|
-
@public
|
|
8054
|
-
*/
|
|
8055
|
-
// Set with reopen to allow overriding via extend
|
|
8056
|
-
|
|
8057
|
-
_routerMicrolib;
|
|
8058
|
-
_didSetupRouter = false;
|
|
8059
|
-
_initialTransitionStarted = false;
|
|
8060
|
-
currentURL = null;
|
|
8061
|
-
currentRouteName = null;
|
|
8062
|
-
currentPath = null;
|
|
8063
|
-
currentRoute = null;
|
|
8064
|
-
_qpCache = Object.create(null);
|
|
8065
|
-
|
|
8066
|
-
// Set of QueryParam['urlKey']
|
|
8067
|
-
_qpUpdates = new Set();
|
|
8068
|
-
_queuedQPChanges = {};
|
|
8069
|
-
_bucketCache;
|
|
8070
|
-
_toplevelView = null;
|
|
8071
|
-
_handledErrors = new Set();
|
|
8072
|
-
_engineInstances = Object.create(null);
|
|
8073
|
-
_engineInfoByRoute = Object.create(null);
|
|
8074
|
-
_routerService;
|
|
8075
|
-
_slowTransitionTimer = null;
|
|
8076
|
-
namespace;
|
|
8077
|
-
|
|
8078
|
-
// Begin Evented
|
|
8079
|
-
|
|
8080
|
-
// End Evented
|
|
8081
|
-
|
|
8082
|
-
// Set with reopenClass
|
|
8083
|
-
static dslCallbacks;
|
|
8084
|
-
|
|
8085
|
-
/**
|
|
8086
|
-
The `Router.map` function allows you to define mappings from URLs to routes
|
|
8087
|
-
in your application. These mappings are defined within the
|
|
8088
|
-
supplied callback function using `this.route`.
|
|
8089
|
-
The first parameter is the name of the route which is used by default as the
|
|
8090
|
-
path name as well.
|
|
8091
|
-
The second parameter is the optional options hash. Available options are:
|
|
8092
|
-
* `path`: allows you to provide your own path as well as mark dynamic
|
|
8093
|
-
segments.
|
|
8094
|
-
* `resetNamespace`: false by default; when nesting routes, ember will
|
|
8095
|
-
combine the route names to form the fully-qualified route name, which is
|
|
8096
|
-
used with `{{link-to}}` or manually transitioning to routes. Setting
|
|
8097
|
-
`resetNamespace: true` will cause the route not to inherit from its
|
|
8098
|
-
parent route's names. This is handy for preventing extremely long route names.
|
|
8099
|
-
Keep in mind that the actual URL path behavior is still retained.
|
|
8100
|
-
The third parameter is a function, which can be used to nest routes.
|
|
8101
|
-
Nested routes, by default, will have the parent route tree's route name and
|
|
8102
|
-
path prepended to it's own.
|
|
8103
|
-
```app/router.js
|
|
8104
|
-
Router.map(function(){
|
|
8105
|
-
this.route('post', { path: '/post/:post_id' }, function() {
|
|
8106
|
-
this.route('edit');
|
|
8107
|
-
this.route('comments', { resetNamespace: true }, function() {
|
|
8108
|
-
this.route('new');
|
|
8109
|
-
});
|
|
8110
|
-
});
|
|
8111
|
-
});
|
|
8112
|
-
```
|
|
8113
|
-
@method map
|
|
8114
|
-
@param callback
|
|
8115
|
-
@public
|
|
8116
|
-
*/
|
|
8117
|
-
static map(callback) {
|
|
8118
|
-
if (!this.dslCallbacks) {
|
|
8119
|
-
this.dslCallbacks = [];
|
|
8120
|
-
// FIXME: Can we remove this?
|
|
8121
|
-
this.reopenClass({
|
|
8122
|
-
dslCallbacks: this.dslCallbacks
|
|
8123
|
-
});
|
|
8124
|
-
}
|
|
8125
|
-
this.dslCallbacks.push(callback);
|
|
8126
|
-
return this;
|
|
8127
|
-
}
|
|
8128
|
-
static _routePath(routeInfos) {
|
|
8129
|
-
let path = [];
|
|
8130
|
-
|
|
8131
|
-
// We have to handle coalescing resource names that
|
|
8132
|
-
// are prefixed with their parent's names, e.g.
|
|
8133
|
-
// ['foo', 'foo.bar.baz'] => 'foo.bar.baz', not 'foo.foo.bar.baz'
|
|
8134
|
-
|
|
8135
|
-
function intersectionMatches(a1, a2) {
|
|
8136
|
-
for (let i = 0; i < a1.length; ++i) {
|
|
8137
|
-
if (a1[i] !== a2[i]) {
|
|
8138
|
-
return false;
|
|
8139
|
-
}
|
|
8140
|
-
}
|
|
8141
|
-
return true;
|
|
8142
|
-
}
|
|
8143
|
-
let name, nameParts, oldNameParts;
|
|
8144
|
-
for (let i = 1; i < routeInfos.length; i++) {
|
|
8145
|
-
let routeInfo = routeInfos[i];
|
|
8146
|
-
(isDevelopingApp() && !(routeInfo) && assert('has routeInfo', routeInfo));
|
|
8147
|
-
name = routeInfo.name;
|
|
8148
|
-
nameParts = name.split('.');
|
|
8149
|
-
oldNameParts = slice.call(path);
|
|
8150
|
-
while (oldNameParts.length) {
|
|
8151
|
-
if (intersectionMatches(oldNameParts, nameParts)) {
|
|
8152
|
-
break;
|
|
8153
|
-
}
|
|
8154
|
-
oldNameParts.shift();
|
|
8155
|
-
}
|
|
8156
|
-
path.push(...nameParts.slice(oldNameParts.length));
|
|
8157
|
-
}
|
|
8158
|
-
return path.join('.');
|
|
8159
|
-
}
|
|
8160
|
-
|
|
8161
|
-
// Note that owner is actually required in this scenario, but since it is strictly
|
|
8162
|
-
// optional in other contexts trying to make it required here confuses TS.
|
|
8163
|
-
constructor(owner) {
|
|
8164
|
-
super(owner);
|
|
8165
|
-
(isDevelopingApp() && !(owner) && assert('BUG: Missing owner', owner));
|
|
8166
|
-
this._resetQueuedQueryParameterChanges();
|
|
8167
|
-
this.namespace = owner.lookup('application:main');
|
|
8168
|
-
let bucketCache = owner.lookup(privatize`-bucket-cache:main`);
|
|
8169
|
-
(isDevelopingApp() && !(bucketCache instanceof BucketCache) && assert('BUG: BucketCache should always be present', bucketCache instanceof BucketCache));
|
|
8170
|
-
this._bucketCache = bucketCache;
|
|
8171
|
-
let routerService = owner.lookup('service:router');
|
|
8172
|
-
(isDevelopingApp() && !(routerService !== undefined) && assert('BUG: RouterService should always be present', routerService !== undefined));
|
|
8173
|
-
this._routerService = routerService;
|
|
8174
|
-
}
|
|
8175
|
-
_initRouterJs() {
|
|
8176
|
-
let location = get(this, 'location');
|
|
8177
|
-
let router = this;
|
|
8178
|
-
const owner = getOwner$1(this);
|
|
8179
|
-
(isDevelopingApp() && !(owner) && assert('Router is unexpectedly missing an owner', owner));
|
|
8180
|
-
let seen = Object.create(null);
|
|
8181
|
-
class PrivateRouter extends Router {
|
|
8182
|
-
getRoute(name) {
|
|
8183
|
-
let routeName = name;
|
|
8184
|
-
let routeOwner = owner;
|
|
8185
|
-
let engineInfo = router._engineInfoByRoute[routeName];
|
|
8186
|
-
if (engineInfo) {
|
|
8187
|
-
let engineInstance = router._getEngineInstance(engineInfo);
|
|
8188
|
-
routeOwner = engineInstance;
|
|
8189
|
-
routeName = engineInfo.localFullName;
|
|
8190
|
-
}
|
|
8191
|
-
let fullRouteName = `route:${routeName}`;
|
|
8192
|
-
(isDevelopingApp() && !(routeOwner) && assert('Route is unexpectedly missing an owner', routeOwner));
|
|
8193
|
-
let route = routeOwner.lookup(fullRouteName);
|
|
8194
|
-
if (seen[name]) {
|
|
8195
|
-
(isDevelopingApp() && !(route) && assert('seen routes should exist', route));
|
|
8196
|
-
return route;
|
|
8197
|
-
}
|
|
8198
|
-
seen[name] = true;
|
|
8199
|
-
if (!route) {
|
|
8200
|
-
// SAFETY: this is configured in `commonSetupRegistry` in the
|
|
8201
|
-
// `@ember/application/lib` package.
|
|
8202
|
-
let DefaultRoute = routeOwner.factoryFor('route:basic').class;
|
|
8203
|
-
routeOwner.register(fullRouteName, DefaultRoute.extend());
|
|
8204
|
-
route = routeOwner.lookup(fullRouteName);
|
|
8205
|
-
if (isDevelopingApp()) {
|
|
8206
|
-
if (router.namespace.LOG_ACTIVE_GENERATION) {
|
|
8207
|
-
info(`generated -> ${fullRouteName}`, {
|
|
8208
|
-
fullName: fullRouteName
|
|
8209
|
-
});
|
|
8210
|
-
}
|
|
8211
|
-
}
|
|
8212
|
-
}
|
|
8213
|
-
route._setRouteName(routeName);
|
|
8214
|
-
if (engineInfo && !hasDefaultSerialize(route)) {
|
|
8215
|
-
throw new Error('Defining a custom serialize method on an Engine route is not supported.');
|
|
8216
|
-
}
|
|
8217
|
-
return route;
|
|
8218
|
-
}
|
|
8219
|
-
getSerializer(name) {
|
|
8220
|
-
let engineInfo = router._engineInfoByRoute[name];
|
|
8221
|
-
|
|
8222
|
-
// If this is not an Engine route, we fall back to the handler for serialization
|
|
8223
|
-
if (!engineInfo) {
|
|
8224
|
-
return;
|
|
8225
|
-
}
|
|
8226
|
-
return engineInfo.serializeMethod || defaultSerialize;
|
|
8227
|
-
}
|
|
8228
|
-
updateURL(path) {
|
|
8229
|
-
once(() => {
|
|
8230
|
-
location.setURL(path);
|
|
8231
|
-
set(router, 'currentURL', path);
|
|
8232
|
-
});
|
|
8233
|
-
}
|
|
8234
|
-
|
|
8235
|
-
// TODO: merge into routeDidChange
|
|
8236
|
-
didTransition(infos) {
|
|
8237
|
-
(isDevelopingApp() && !(router.didTransition === defaultDidTransition) && assert('You attempted to override the "didTransition" method which has been deprecated. Please inject the router service and listen to the "routeDidChange" event.', router.didTransition === defaultDidTransition));
|
|
8238
|
-
router.didTransition(infos);
|
|
8239
|
-
}
|
|
8240
|
-
|
|
8241
|
-
// TODO: merge into routeWillChange
|
|
8242
|
-
willTransition(oldInfos, newInfos) {
|
|
8243
|
-
(isDevelopingApp() && !(router.willTransition === defaultWillTransition) && assert('You attempted to override the "willTransition" method which has been deprecated. Please inject the router service and listen to the "routeWillChange" event.', router.willTransition === defaultWillTransition));
|
|
8244
|
-
router.willTransition(oldInfos, newInfos);
|
|
8245
|
-
}
|
|
8246
|
-
triggerEvent(routeInfos, ignoreFailure, name, args) {
|
|
8247
|
-
return triggerEvent.bind(router)(routeInfos, ignoreFailure, name, args);
|
|
8248
|
-
}
|
|
8249
|
-
routeWillChange(transition) {
|
|
8250
|
-
router.trigger('routeWillChange', transition);
|
|
8251
|
-
if (isDevelopingApp()) {
|
|
8252
|
-
freezeRouteInfo(transition);
|
|
8253
|
-
}
|
|
8254
|
-
router._routerService.trigger('routeWillChange', transition);
|
|
8255
|
-
|
|
8256
|
-
// in case of intermediate transition we update the current route
|
|
8257
|
-
// to make router.currentRoute.name consistent with router.currentRouteName
|
|
8258
|
-
// see https://github.com/emberjs/ember.js/issues/19449
|
|
8259
|
-
if (transition.isIntermediate) {
|
|
8260
|
-
router.set('currentRoute', transition.to);
|
|
8261
|
-
}
|
|
8262
|
-
}
|
|
8263
|
-
routeDidChange(transition) {
|
|
8264
|
-
router.set('currentRoute', transition.to);
|
|
8265
|
-
once(() => {
|
|
8266
|
-
router.trigger('routeDidChange', transition);
|
|
8267
|
-
if (isDevelopingApp()) {
|
|
8268
|
-
freezeRouteInfo(transition);
|
|
8269
|
-
}
|
|
8270
|
-
router._routerService.trigger('routeDidChange', transition);
|
|
8271
|
-
});
|
|
8272
|
-
}
|
|
8273
|
-
transitionDidError(error, transition) {
|
|
8274
|
-
if (error.wasAborted || transition.isAborted) {
|
|
8275
|
-
// If the error was a transition erorr or the transition aborted
|
|
8276
|
-
// log the abort.
|
|
8277
|
-
return logAbort(transition);
|
|
8278
|
-
} else {
|
|
8279
|
-
// Otherwise trigger the "error" event to attempt an intermediate
|
|
8280
|
-
// transition into an error substate
|
|
8281
|
-
transition.trigger(false, 'error', error.error, transition, error.route);
|
|
8282
|
-
if (router._isErrorHandled(error.error)) {
|
|
8283
|
-
// If we handled the error with a substate just roll the state back on
|
|
8284
|
-
// the transition and send the "routeDidChange" event for landing on
|
|
8285
|
-
// the error substate and return the error.
|
|
8286
|
-
transition.rollback();
|
|
8287
|
-
this.routeDidChange(transition);
|
|
8288
|
-
return error.error;
|
|
8289
|
-
} else {
|
|
8290
|
-
// If it was not handled, abort the transition completely and return
|
|
8291
|
-
// the error.
|
|
8292
|
-
transition.abort();
|
|
8293
|
-
return error.error;
|
|
8294
|
-
}
|
|
8295
|
-
}
|
|
8296
|
-
}
|
|
8297
|
-
replaceURL(url) {
|
|
8298
|
-
if (location.replaceURL) {
|
|
8299
|
-
let doReplaceURL = () => {
|
|
8300
|
-
location.replaceURL(url);
|
|
8301
|
-
set(router, 'currentURL', url);
|
|
8302
|
-
};
|
|
8303
|
-
once(doReplaceURL);
|
|
8304
|
-
} else {
|
|
8305
|
-
this.updateURL(url);
|
|
8306
|
-
}
|
|
8307
|
-
}
|
|
8308
|
-
}
|
|
8309
|
-
let routerMicrolib = this._routerMicrolib = new PrivateRouter();
|
|
8310
|
-
let dslCallbacks = this.constructor.dslCallbacks || [K];
|
|
8311
|
-
let dsl = this._buildDSL();
|
|
8312
|
-
dsl.route('application', {
|
|
8313
|
-
path: '/',
|
|
8314
|
-
resetNamespace: true,
|
|
8315
|
-
overrideNameAssertion: true
|
|
8316
|
-
}, function () {
|
|
8317
|
-
for (let i = 0; i < dslCallbacks.length; i++) {
|
|
8318
|
-
dslCallbacks[i].call(this);
|
|
8319
|
-
}
|
|
8320
|
-
});
|
|
8321
|
-
if (isDevelopingApp()) {
|
|
8322
|
-
if (this.namespace.LOG_TRANSITIONS_INTERNAL) {
|
|
8323
|
-
routerMicrolib.log = console.log.bind(console); // eslint-disable-line no-console
|
|
8324
|
-
}
|
|
8325
|
-
}
|
|
8326
|
-
routerMicrolib.map(dsl.generate());
|
|
8327
|
-
}
|
|
8328
|
-
_buildDSL() {
|
|
8329
|
-
let enableLoadingSubstates = this._hasModuleBasedResolver();
|
|
8330
|
-
let router = this;
|
|
8331
|
-
const owner = getOwner$1(this);
|
|
8332
|
-
(isDevelopingApp() && !(owner) && assert('Router is unexpectedly missing an owner', owner));
|
|
8333
|
-
let options = {
|
|
8334
|
-
enableLoadingSubstates,
|
|
8335
|
-
resolveRouteMap(name) {
|
|
8336
|
-
return owner.factoryFor(`route-map:${name}`);
|
|
8337
|
-
},
|
|
8338
|
-
addRouteForEngine(name, engineInfo) {
|
|
8339
|
-
if (!router._engineInfoByRoute[name]) {
|
|
8340
|
-
router._engineInfoByRoute[name] = engineInfo;
|
|
8341
|
-
}
|
|
8342
|
-
}
|
|
8343
|
-
};
|
|
8344
|
-
return new DSLImpl(null, options);
|
|
8345
|
-
}
|
|
8346
|
-
|
|
8347
|
-
/*
|
|
8348
|
-
Resets all pending query parameter changes.
|
|
8349
|
-
Called after transitioning to a new route
|
|
8350
|
-
based on query parameter changes.
|
|
8351
|
-
*/
|
|
8352
|
-
_resetQueuedQueryParameterChanges() {
|
|
8353
|
-
this._queuedQPChanges = {};
|
|
8354
|
-
}
|
|
8355
|
-
_hasModuleBasedResolver() {
|
|
8356
|
-
let owner = getOwner$1(this);
|
|
8357
|
-
(isDevelopingApp() && !(owner) && assert('Router is unexpectedly missing an owner', owner));
|
|
8358
|
-
let resolver = get(owner, 'application.__registry__.resolver.moduleBasedResolver');
|
|
8359
|
-
return Boolean(resolver);
|
|
8360
|
-
}
|
|
8361
|
-
|
|
8362
|
-
/**
|
|
8363
|
-
Initializes the current router instance and sets up the change handling
|
|
8364
|
-
event listeners used by the instances `location` implementation.
|
|
8365
|
-
A property named `initialURL` will be used to determine the initial URL.
|
|
8366
|
-
If no value is found `/` will be used.
|
|
8367
|
-
@method startRouting
|
|
8368
|
-
@private
|
|
8369
|
-
*/
|
|
8370
|
-
startRouting() {
|
|
8371
|
-
if (this.setupRouter()) {
|
|
8372
|
-
let initialURL = get(this, 'initialURL');
|
|
8373
|
-
if (initialURL === undefined) {
|
|
8374
|
-
initialURL = get(this, 'location').getURL();
|
|
8375
|
-
}
|
|
8376
|
-
let initialTransition = this.handleURL(initialURL);
|
|
8377
|
-
if (initialTransition && initialTransition.error) {
|
|
8378
|
-
throw initialTransition.error;
|
|
8379
|
-
}
|
|
8380
|
-
}
|
|
8381
|
-
}
|
|
8382
|
-
setupRouter() {
|
|
8383
|
-
if (this._didSetupRouter) {
|
|
8384
|
-
return false;
|
|
8385
|
-
}
|
|
8386
|
-
this._didSetupRouter = true;
|
|
8387
|
-
this._setupLocation();
|
|
8388
|
-
let location = get(this, 'location');
|
|
8389
|
-
|
|
8390
|
-
// Allow the Location class to cancel the router setup while it refreshes
|
|
8391
|
-
// the page
|
|
8392
|
-
if (get(location, 'cancelRouterSetup')) {
|
|
8393
|
-
return false;
|
|
8394
|
-
}
|
|
8395
|
-
this._initRouterJs();
|
|
8396
|
-
location.onUpdateURL(url => {
|
|
8397
|
-
this.handleURL(url);
|
|
8398
|
-
});
|
|
8399
|
-
return true;
|
|
8400
|
-
}
|
|
8401
|
-
_setOutlets() {
|
|
8402
|
-
// This is triggered async during Route#willDestroy.
|
|
8403
|
-
// If the router is also being destroyed we do not want to
|
|
8404
|
-
// to create another this._toplevelView (and leak the renderer)
|
|
8405
|
-
if (this.isDestroying || this.isDestroyed) {
|
|
8406
|
-
return;
|
|
8407
|
-
}
|
|
8408
|
-
let routeInfos = this._routerMicrolib.currentRouteInfos;
|
|
8409
|
-
if (!routeInfos) {
|
|
8410
|
-
return;
|
|
8411
|
-
}
|
|
8412
|
-
let root = null;
|
|
8413
|
-
let parent = null;
|
|
8414
|
-
for (let routeInfo of routeInfos) {
|
|
8415
|
-
let route = routeInfo.route;
|
|
8416
|
-
let render = getRenderState(route);
|
|
8417
|
-
if (render) {
|
|
8418
|
-
let state = {
|
|
8419
|
-
render,
|
|
8420
|
-
outlets: {
|
|
8421
|
-
main: undefined
|
|
8422
|
-
}
|
|
8423
|
-
};
|
|
8424
|
-
if (parent) {
|
|
8425
|
-
parent.outlets.main = state;
|
|
8426
|
-
} else {
|
|
8427
|
-
root = state;
|
|
8428
|
-
}
|
|
8429
|
-
parent = state;
|
|
8430
|
-
} else {
|
|
8431
|
-
// It used to be that we would create a stub entry and keep traversing,
|
|
8432
|
-
// but I don't think that is necessary anymore – if a parent route did
|
|
8433
|
-
// not render, then the child routes have nowhere to render into these
|
|
8434
|
-
// days. That wasn't always the case since in the past any route can
|
|
8435
|
-
// render into any other route's outlets.
|
|
8436
|
-
break;
|
|
8437
|
-
}
|
|
8438
|
-
}
|
|
8439
|
-
|
|
8440
|
-
// when a transitionTo happens after the validation phase
|
|
8441
|
-
// during the initial transition _setOutlets is called
|
|
8442
|
-
// when no routes are active. However, it will get called
|
|
8443
|
-
// again with the correct values during the next turn of
|
|
8444
|
-
// the runloop
|
|
8445
|
-
if (root === null) {
|
|
8446
|
-
return;
|
|
8447
|
-
}
|
|
8448
|
-
if (!this._toplevelView) {
|
|
8449
|
-
let owner = getOwner$1(this);
|
|
8450
|
-
(isDevelopingApp() && !(owner) && assert('Router is unexpectedly missing an owner', owner)); // SAFETY: we don't presently have any type registries internally to make
|
|
8451
|
-
// this safe, so in each of these cases we assume that nothing *else* is
|
|
8452
|
-
// registered at this `FullName`, and simply check to make sure that
|
|
8453
|
-
// *something* is.
|
|
8454
|
-
let OutletView = owner.factoryFor('view:-outlet');
|
|
8455
|
-
(isDevelopingApp() && !(OutletView !== undefined) && assert('[BUG] unexpectedly missing `view:-outlet`', OutletView !== undefined));
|
|
8456
|
-
let application = owner.lookup('application:main');
|
|
8457
|
-
(isDevelopingApp() && !(application !== undefined) && assert('[BUG] unexpectedly missing `application:-main`', application !== undefined));
|
|
8458
|
-
let environment = owner.lookup('-environment:main');
|
|
8459
|
-
(isDevelopingApp() && !(environment !== undefined) && assert('[BUG] unexpectedly missing `-environment:main`', environment !== undefined));
|
|
8460
|
-
let template = owner.lookup('template:-outlet');
|
|
8461
|
-
(isDevelopingApp() && !(template !== undefined) && assert('[BUG] unexpectedly missing `template:-outlet`', template !== undefined));
|
|
8462
|
-
this._toplevelView = OutletView.create({
|
|
8463
|
-
environment,
|
|
8464
|
-
template,
|
|
8465
|
-
application
|
|
8466
|
-
});
|
|
8467
|
-
this._toplevelView.setOutletState(root);
|
|
8468
|
-
|
|
8469
|
-
// TODO(SAFETY): At least one test runs without this set correctly. At a
|
|
8470
|
-
// later time, update the test to configure this correctly. The test ID:
|
|
8471
|
-
// `Router Service - non application test: RouterService#transitionTo with basic route`
|
|
8472
|
-
let instance = owner.lookup('-application-instance:main');
|
|
8473
|
-
// let instance = owner.lookup('-application-instance:main') as ApplicationInstance | undefined;
|
|
8474
|
-
// assert('[BUG] unexpectedly missing `-application-instance:main`', instance !== undefined);
|
|
8475
|
-
|
|
8476
|
-
if (instance) {
|
|
8477
|
-
// SAFETY: LOL. This is calling a deprecated API with a type that we
|
|
8478
|
-
// cannot actually confirm at a type level *is* a `ViewMixin`. Seems:
|
|
8479
|
-
// not great on multiple fronts!
|
|
8480
|
-
instance.didCreateRootView(this._toplevelView);
|
|
8481
|
-
}
|
|
8482
|
-
} else {
|
|
8483
|
-
this._toplevelView.setOutletState(root);
|
|
8484
|
-
}
|
|
8485
|
-
}
|
|
8486
|
-
handleURL(url) {
|
|
8487
|
-
// Until we have an ember-idiomatic way of accessing #hashes, we need to
|
|
8488
|
-
// remove it because router.js doesn't know how to handle it.
|
|
8489
|
-
let _url = url.split(/#(.+)?/)[0];
|
|
8490
|
-
return this._doURLTransition('handleURL', _url);
|
|
8491
|
-
}
|
|
8492
|
-
_doURLTransition(routerJsMethod, url) {
|
|
8493
|
-
this._initialTransitionStarted = true;
|
|
8494
|
-
let transition = this._routerMicrolib[routerJsMethod](url || '/');
|
|
8495
|
-
didBeginTransition(transition, this);
|
|
8496
|
-
return transition;
|
|
8497
|
-
}
|
|
8498
|
-
|
|
8499
|
-
/**
|
|
8500
|
-
Transition the application into another route. The route may
|
|
8501
|
-
be either a single route or route path:
|
|
8502
|
-
@method transitionTo
|
|
8503
|
-
@param {String} [name] the name of the route or a URL
|
|
8504
|
-
@param {...Object} models the model(s) or identifier(s) to be used while
|
|
8505
|
-
transitioning to the route.
|
|
8506
|
-
@param {Object} [options] optional hash with a queryParams property
|
|
8507
|
-
containing a mapping of query parameters
|
|
8508
|
-
@return {Transition} the transition object associated with this
|
|
8509
|
-
attempted transition
|
|
8510
|
-
@public
|
|
8511
|
-
*/
|
|
8512
|
-
transitionTo(...args) {
|
|
8513
|
-
if (resemblesURL(args[0])) {
|
|
8514
|
-
(isDevelopingApp() && !(!this.isDestroying && !this.isDestroyed) && assert(`A transition was attempted from '${this.currentRouteName}' to '${args[0]}' but the application instance has already been destroyed.`, !this.isDestroying && !this.isDestroyed));
|
|
8515
|
-
return this._doURLTransition('transitionTo', args[0]);
|
|
8516
|
-
}
|
|
8517
|
-
let {
|
|
8518
|
-
routeName,
|
|
8519
|
-
models,
|
|
8520
|
-
queryParams
|
|
8521
|
-
} = extractRouteArgs(args);
|
|
8522
|
-
(isDevelopingApp() && !(!this.isDestroying && !this.isDestroyed) && assert(`A transition was attempted from '${this.currentRouteName}' to '${routeName}' but the application instance has already been destroyed.`, !this.isDestroying && !this.isDestroyed));
|
|
8523
|
-
return this._doTransition(routeName, models, queryParams);
|
|
8524
|
-
}
|
|
8525
|
-
intermediateTransitionTo(name, ...args) {
|
|
8526
|
-
this._routerMicrolib.intermediateTransitionTo(name, ...args);
|
|
8527
|
-
updatePaths(this);
|
|
8528
|
-
if (isDevelopingApp()) {
|
|
8529
|
-
let infos = this._routerMicrolib.currentRouteInfos;
|
|
8530
|
-
if (this.namespace.LOG_TRANSITIONS) {
|
|
8531
|
-
(isDevelopingApp() && !(infos) && assert('expected infos to be set', infos)); // eslint-disable-next-line no-console
|
|
8532
|
-
console.log(`Intermediate-transitioned into '${EmberRouter._routePath(infos)}'`);
|
|
8533
|
-
}
|
|
8534
|
-
}
|
|
8535
|
-
}
|
|
8536
|
-
|
|
8537
|
-
/**
|
|
8538
|
-
Similar to `transitionTo`, but instead of adding the destination to the browser's URL history,
|
|
8539
|
-
it replaces the entry for the current route.
|
|
8540
|
-
When the user clicks the "back" button in the browser, there will be fewer steps.
|
|
8541
|
-
This is most commonly used to manage redirects in a way that does not cause confusing additions
|
|
8542
|
-
to the user's browsing history.
|
|
8543
|
-
@method replaceWith
|
|
8544
|
-
@param {String} [name] the name of the route or a URL
|
|
8545
|
-
@param {...Object} models the model(s) or identifier(s) to be used while
|
|
8546
|
-
transitioning to the route.
|
|
8547
|
-
@param {Object} [options] optional hash with a queryParams property
|
|
8548
|
-
containing a mapping of query parameters
|
|
8549
|
-
@return {Transition} the transition object associated with this
|
|
8550
|
-
attempted transition
|
|
8551
|
-
@public
|
|
8552
|
-
*/
|
|
8553
|
-
replaceWith(...args) {
|
|
8554
|
-
return this.transitionTo(...args).method('replace');
|
|
8555
|
-
}
|
|
8556
|
-
generate(name, ...args) {
|
|
8557
|
-
let url = this._routerMicrolib.generate(name, ...args);
|
|
8558
|
-
(isDevelopingApp() && !(typeof this.location !== 'string') && assert('expected non-string location', typeof this.location !== 'string'));
|
|
8559
|
-
return this.location.formatURL(url);
|
|
8560
|
-
}
|
|
8561
|
-
|
|
8562
|
-
/**
|
|
8563
|
-
Determines if the supplied route is currently active.
|
|
8564
|
-
@method isActive
|
|
8565
|
-
@param routeName
|
|
8566
|
-
@return {Boolean}
|
|
8567
|
-
@private
|
|
8568
|
-
*/
|
|
8569
|
-
isActive(routeName) {
|
|
8570
|
-
return this._routerMicrolib.isActive(routeName);
|
|
8571
|
-
}
|
|
8572
|
-
|
|
8573
|
-
/**
|
|
8574
|
-
An alternative form of `isActive` that doesn't require
|
|
8575
|
-
manual concatenation of the arguments into a single
|
|
8576
|
-
array.
|
|
8577
|
-
@method isActiveIntent
|
|
8578
|
-
@param routeName
|
|
8579
|
-
@param models
|
|
8580
|
-
@param queryParams
|
|
8581
|
-
@return {Boolean}
|
|
8582
|
-
@private
|
|
8583
|
-
@since 1.7.0
|
|
8584
|
-
*/
|
|
8585
|
-
isActiveIntent(routeName, models, queryParams) {
|
|
8586
|
-
return this.currentState.isActiveIntent(routeName, models, queryParams);
|
|
8587
|
-
}
|
|
8588
|
-
send(name, ...args) {
|
|
8589
|
-
/*name, context*/
|
|
8590
|
-
this._routerMicrolib.trigger(name, ...args);
|
|
8591
|
-
}
|
|
8592
|
-
|
|
8593
|
-
/**
|
|
8594
|
-
Does this router instance have the given route.
|
|
8595
|
-
@method hasRoute
|
|
8596
|
-
@return {Boolean}
|
|
8597
|
-
@private
|
|
8598
|
-
*/
|
|
8599
|
-
hasRoute(route) {
|
|
8600
|
-
return this._routerMicrolib.hasRoute(route);
|
|
8601
|
-
}
|
|
8602
|
-
|
|
8603
|
-
/**
|
|
8604
|
-
Resets the state of the router by clearing the current route
|
|
8605
|
-
handlers and deactivating them.
|
|
8606
|
-
@private
|
|
8607
|
-
@method reset
|
|
8608
|
-
*/
|
|
8609
|
-
reset() {
|
|
8610
|
-
this._didSetupRouter = false;
|
|
8611
|
-
this._initialTransitionStarted = false;
|
|
8612
|
-
if (this._routerMicrolib) {
|
|
8613
|
-
this._routerMicrolib.reset();
|
|
8614
|
-
}
|
|
8615
|
-
}
|
|
8616
|
-
willDestroy() {
|
|
8617
|
-
if (this._toplevelView) {
|
|
8618
|
-
this._toplevelView.destroy();
|
|
8619
|
-
this._toplevelView = null;
|
|
8620
|
-
}
|
|
8621
|
-
super.willDestroy();
|
|
8622
|
-
this.reset();
|
|
8623
|
-
let instances = this._engineInstances;
|
|
8624
|
-
for (let name in instances) {
|
|
8625
|
-
let instanceMap = instances[name];
|
|
8626
|
-
(isDevelopingApp() && !(instanceMap) && assert('has instanceMap', instanceMap));
|
|
8627
|
-
for (let id in instanceMap) {
|
|
8628
|
-
let instance = instanceMap[id];
|
|
8629
|
-
(isDevelopingApp() && !(instance) && assert('has instance', instance));
|
|
8630
|
-
run(instance, 'destroy');
|
|
8631
|
-
}
|
|
8632
|
-
}
|
|
8633
|
-
}
|
|
8634
|
-
|
|
8635
|
-
/*
|
|
8636
|
-
Called when an active route's query parameter has changed.
|
|
8637
|
-
These changes are batched into a runloop run and trigger
|
|
8638
|
-
a single transition.
|
|
8639
|
-
*/
|
|
8640
|
-
_activeQPChanged(queryParameterName, newValue) {
|
|
8641
|
-
this._queuedQPChanges[queryParameterName] = newValue;
|
|
8642
|
-
once(this, this._fireQueryParamTransition);
|
|
8643
|
-
}
|
|
8644
|
-
|
|
8645
|
-
// The queryParameterName is QueryParam['urlKey']
|
|
8646
|
-
_updatingQPChanged(queryParameterName) {
|
|
8647
|
-
this._qpUpdates.add(queryParameterName);
|
|
8648
|
-
}
|
|
8649
|
-
|
|
8650
|
-
/*
|
|
8651
|
-
Triggers a transition to a route based on query parameter changes.
|
|
8652
|
-
This is called once per runloop, to batch changes.
|
|
8653
|
-
e.g.
|
|
8654
|
-
if these methods are called in succession:
|
|
8655
|
-
this._activeQPChanged('foo', '10');
|
|
8656
|
-
// results in _queuedQPChanges = { foo: '10' }
|
|
8657
|
-
this._activeQPChanged('bar', false);
|
|
8658
|
-
// results in _queuedQPChanges = { foo: '10', bar: false }
|
|
8659
|
-
_queuedQPChanges will represent both of these changes
|
|
8660
|
-
and the transition using `transitionTo` will be triggered
|
|
8661
|
-
once.
|
|
8662
|
-
*/
|
|
8663
|
-
_fireQueryParamTransition() {
|
|
8664
|
-
this.transitionTo({
|
|
8665
|
-
queryParams: this._queuedQPChanges
|
|
8666
|
-
});
|
|
8667
|
-
this._resetQueuedQueryParameterChanges();
|
|
8668
|
-
}
|
|
8669
|
-
_setupLocation() {
|
|
8670
|
-
let location = this.location;
|
|
8671
|
-
let rootURL = this.rootURL;
|
|
8672
|
-
let owner = getOwner$1(this);
|
|
8673
|
-
(isDevelopingApp() && !(owner) && assert('Router is unexpectedly missing an owner', owner));
|
|
8674
|
-
if ('string' === typeof location) {
|
|
8675
|
-
let resolvedLocation = owner.lookup(`location:${location}`);
|
|
8676
|
-
(isDevelopingApp() && !(resolvedLocation) && assert(`Could not resolve a location class at 'location:${location}'`, resolvedLocation));
|
|
8677
|
-
location = set(this, 'location', resolvedLocation);
|
|
8678
|
-
}
|
|
8679
|
-
if (location !== null && typeof location === 'object') {
|
|
8680
|
-
if (rootURL) {
|
|
8681
|
-
set(location, 'rootURL', rootURL);
|
|
8682
|
-
}
|
|
8683
|
-
|
|
8684
|
-
// ensure that initState is called AFTER the rootURL is set on
|
|
8685
|
-
// the location instance
|
|
8686
|
-
if (typeof location.initState === 'function') {
|
|
8687
|
-
location.initState();
|
|
8688
|
-
}
|
|
8689
|
-
}
|
|
8690
|
-
}
|
|
8691
|
-
|
|
8692
|
-
/**
|
|
8693
|
-
Serializes the given query params according to their QP meta information.
|
|
8694
|
-
@private
|
|
8695
|
-
@method _serializeQueryParams
|
|
8696
|
-
@param {Arrray<RouteInfo>} routeInfos
|
|
8697
|
-
@param {Object} queryParams
|
|
8698
|
-
@return {Void}
|
|
8699
|
-
*/
|
|
8700
|
-
_serializeQueryParams(routeInfos, queryParams) {
|
|
8701
|
-
forEachQueryParam(this, routeInfos, queryParams, (key, value, qp) => {
|
|
8702
|
-
if (qp) {
|
|
8703
|
-
delete queryParams[key];
|
|
8704
|
-
queryParams[qp.urlKey] = qp.route.serializeQueryParam(value, qp.urlKey, qp.type);
|
|
8705
|
-
} else if (value === undefined) {
|
|
8706
|
-
return; // We don't serialize undefined values
|
|
8707
|
-
} else {
|
|
8708
|
-
queryParams[key] = this._serializeQueryParam(value, typeOf(value));
|
|
8709
|
-
}
|
|
8710
|
-
});
|
|
8711
|
-
}
|
|
8712
|
-
|
|
8713
|
-
/**
|
|
8714
|
-
Serializes the value of a query parameter based on a type
|
|
8715
|
-
@private
|
|
8716
|
-
@method _serializeQueryParam
|
|
8717
|
-
@param {Object} value
|
|
8718
|
-
@param {String} type
|
|
8719
|
-
*/
|
|
8720
|
-
_serializeQueryParam(value, type) {
|
|
8721
|
-
if (value === null || value === undefined) {
|
|
8722
|
-
return value;
|
|
8723
|
-
} else if (type === 'array') {
|
|
8724
|
-
return JSON.stringify(value);
|
|
8725
|
-
}
|
|
8726
|
-
return `${value}`;
|
|
8727
|
-
}
|
|
8728
|
-
|
|
8729
|
-
/**
|
|
8730
|
-
Deserializes the given query params according to their QP meta information.
|
|
8731
|
-
@private
|
|
8732
|
-
@method _deserializeQueryParams
|
|
8733
|
-
@param {Array<RouteInfo>} routeInfos
|
|
8734
|
-
@param {Object} queryParams
|
|
8735
|
-
@return {Void}
|
|
8736
|
-
*/
|
|
8737
|
-
_deserializeQueryParams(routeInfos, queryParams) {
|
|
8738
|
-
forEachQueryParam(this, routeInfos, queryParams, (key, value, qp) => {
|
|
8739
|
-
// If we don't have QP meta info for a given key, then we do nothing
|
|
8740
|
-
// because all values will be treated as strings
|
|
8741
|
-
if (qp) {
|
|
8742
|
-
delete queryParams[key];
|
|
8743
|
-
queryParams[qp.prop] = qp.route.deserializeQueryParam(value, qp.urlKey, qp.type);
|
|
8744
|
-
}
|
|
8745
|
-
});
|
|
8746
|
-
}
|
|
8747
|
-
|
|
8748
|
-
/**
|
|
8749
|
-
Deserializes the value of a query parameter based on a default type
|
|
8750
|
-
@private
|
|
8751
|
-
@method _deserializeQueryParam
|
|
8752
|
-
@param {Object} value
|
|
8753
|
-
@param {String} defaultType
|
|
8754
|
-
*/
|
|
8755
|
-
_deserializeQueryParam(value, defaultType) {
|
|
8756
|
-
if (value === null || value === undefined) {
|
|
8757
|
-
return value;
|
|
8758
|
-
} else if (defaultType === 'boolean') {
|
|
8759
|
-
return value === 'true';
|
|
8760
|
-
} else if (defaultType === 'number') {
|
|
8761
|
-
return Number(value).valueOf();
|
|
8762
|
-
} else if (defaultType === 'array') {
|
|
8763
|
-
return A(JSON.parse(value));
|
|
8764
|
-
}
|
|
8765
|
-
return value;
|
|
8766
|
-
}
|
|
8767
|
-
|
|
8768
|
-
/**
|
|
8769
|
-
Removes (prunes) any query params with default values from the given QP
|
|
8770
|
-
object. Default values are determined from the QP meta information per key.
|
|
8771
|
-
@private
|
|
8772
|
-
@method _pruneDefaultQueryParamValues
|
|
8773
|
-
@param {Array<RouteInfo>} routeInfos
|
|
8774
|
-
@param {Object} queryParams
|
|
8775
|
-
@return {Void}
|
|
8776
|
-
*/
|
|
8777
|
-
_pruneDefaultQueryParamValues(routeInfos, queryParams) {
|
|
8778
|
-
let qps = this._queryParamsFor(routeInfos);
|
|
8779
|
-
for (let key in queryParams) {
|
|
8780
|
-
let qp = qps.map[key];
|
|
8781
|
-
if (qp && qp.serializedDefaultValue === queryParams[key]) {
|
|
8782
|
-
delete queryParams[key];
|
|
8783
|
-
}
|
|
8784
|
-
}
|
|
8785
|
-
}
|
|
8786
|
-
_doTransition(_targetRouteName, models, _queryParams, _fromRouterService) {
|
|
8787
|
-
let targetRouteName = _targetRouteName || getActiveTargetName(this._routerMicrolib);
|
|
8788
|
-
(isDevelopingApp() && !(Boolean(targetRouteName) && this._routerMicrolib.hasRoute(targetRouteName)) && assert(`The route ${targetRouteName} was not found`, Boolean(targetRouteName) && this._routerMicrolib.hasRoute(targetRouteName)));
|
|
8789
|
-
this._initialTransitionStarted = true;
|
|
8790
|
-
let queryParams = {};
|
|
8791
|
-
this._processActiveTransitionQueryParams(targetRouteName, models, queryParams, _queryParams);
|
|
8792
|
-
Object.assign(queryParams, _queryParams);
|
|
8793
|
-
this._prepareQueryParams(targetRouteName, models, queryParams, Boolean(_fromRouterService));
|
|
8794
|
-
let transition = this._routerMicrolib.transitionTo(targetRouteName, ...models, {
|
|
8795
|
-
queryParams
|
|
8796
|
-
});
|
|
8797
|
-
didBeginTransition(transition, this);
|
|
8798
|
-
return transition;
|
|
8799
|
-
}
|
|
8800
|
-
_processActiveTransitionQueryParams(targetRouteName, models, queryParams, _queryParams) {
|
|
8801
|
-
// merge in any queryParams from the active transition which could include
|
|
8802
|
-
// queryParams from the url on initial load.
|
|
8803
|
-
if (!this._routerMicrolib.activeTransition) {
|
|
8804
|
-
return;
|
|
8805
|
-
}
|
|
8806
|
-
let unchangedQPs = {};
|
|
8807
|
-
let qpUpdates = this._qpUpdates;
|
|
8808
|
-
let params = getFullQueryParams(this, this._routerMicrolib.activeTransition[STATE_SYMBOL]);
|
|
8809
|
-
for (let key in params) {
|
|
8810
|
-
if (!qpUpdates.has(key)) {
|
|
8811
|
-
unchangedQPs[key] = params[key];
|
|
8812
|
-
}
|
|
8813
|
-
}
|
|
8814
|
-
|
|
8815
|
-
// We need to fully scope queryParams so that we can create one object
|
|
8816
|
-
// that represents both passed-in queryParams and ones that aren't changed
|
|
8817
|
-
// from the active transition.
|
|
8818
|
-
this._fullyScopeQueryParams(targetRouteName, models, _queryParams);
|
|
8819
|
-
this._fullyScopeQueryParams(targetRouteName, models, unchangedQPs);
|
|
8820
|
-
Object.assign(queryParams, unchangedQPs);
|
|
8821
|
-
}
|
|
8822
|
-
|
|
8823
|
-
/**
|
|
8824
|
-
Prepares the query params for a URL or Transition. Restores any undefined QP
|
|
8825
|
-
keys/values, serializes all values, and then prunes any default values.
|
|
8826
|
-
@private
|
|
8827
|
-
@method _prepareQueryParams
|
|
8828
|
-
@param {String} targetRouteName
|
|
8829
|
-
@param {Array<Object>} models
|
|
8830
|
-
@param {Object} queryParams
|
|
8831
|
-
@param {boolean} keepDefaultQueryParamValues
|
|
8832
|
-
@return {Void}
|
|
8833
|
-
*/
|
|
8834
|
-
_prepareQueryParams(targetRouteName, models, queryParams, _fromRouterService) {
|
|
8835
|
-
let state = calculatePostTransitionState(this, targetRouteName, models);
|
|
8836
|
-
this._hydrateUnsuppliedQueryParams(state, queryParams, Boolean(_fromRouterService));
|
|
8837
|
-
this._serializeQueryParams(state.routeInfos, queryParams);
|
|
8838
|
-
if (!_fromRouterService) {
|
|
8839
|
-
this._pruneDefaultQueryParamValues(state.routeInfos, queryParams);
|
|
8840
|
-
}
|
|
8841
|
-
}
|
|
8842
|
-
|
|
8843
|
-
/**
|
|
8844
|
-
Returns the meta information for the query params of a given route. This
|
|
8845
|
-
will be overridden to allow support for lazy routes.
|
|
8846
|
-
@private
|
|
8847
|
-
@method _getQPMeta
|
|
8848
|
-
@param {RouteInfo} routeInfo
|
|
8849
|
-
@return {Object}
|
|
8850
|
-
*/
|
|
8851
|
-
_getQPMeta(routeInfo) {
|
|
8852
|
-
let route = routeInfo.route;
|
|
8853
|
-
return route && get(route, '_qp');
|
|
8854
|
-
}
|
|
8855
|
-
|
|
8856
|
-
/**
|
|
8857
|
-
Returns a merged query params meta object for a given set of routeInfos.
|
|
8858
|
-
Useful for knowing what query params are available for a given route hierarchy.
|
|
8859
|
-
@private
|
|
8860
|
-
@method _queryParamsFor
|
|
8861
|
-
@param {Array<RouteInfo>} routeInfos
|
|
8862
|
-
@return {Object}
|
|
8863
|
-
*/
|
|
8864
|
-
_queryParamsFor(routeInfos) {
|
|
8865
|
-
let routeInfoLength = routeInfos.length;
|
|
8866
|
-
let leafRouteName = routeInfos[routeInfoLength - 1].name;
|
|
8867
|
-
let cached = this._qpCache[leafRouteName];
|
|
8868
|
-
if (cached !== undefined) {
|
|
8869
|
-
return cached;
|
|
8870
|
-
}
|
|
8871
|
-
let shouldCache = true;
|
|
8872
|
-
let map = {};
|
|
8873
|
-
let qps = [];
|
|
8874
|
-
let qpsByUrlKey = isDevelopingApp() ? {} : null;
|
|
8875
|
-
let qpMeta;
|
|
8876
|
-
let urlKey;
|
|
8877
|
-
let qpOther;
|
|
8878
|
-
for (let routeInfo of routeInfos) {
|
|
8879
|
-
qpMeta = this._getQPMeta(routeInfo);
|
|
8880
|
-
if (!qpMeta) {
|
|
8881
|
-
shouldCache = false;
|
|
8882
|
-
continue;
|
|
8883
|
-
}
|
|
8884
|
-
|
|
8885
|
-
// Loop over each QP to make sure we don't have any collisions by urlKey
|
|
8886
|
-
for (let qp of qpMeta.qps) {
|
|
8887
|
-
if (isDevelopingApp()) {
|
|
8888
|
-
urlKey = qp.urlKey;
|
|
8889
|
-
qpOther = qpsByUrlKey[urlKey];
|
|
8890
|
-
if (qpOther && qpOther.controllerName !== qp.controllerName) {
|
|
8891
|
-
(isDevelopingApp() && !(false) && assert(`You're not allowed to have more than one controller property map to the same query param key, but both \`${qpOther.scopedPropertyName}\` and \`${qp.scopedPropertyName}\` map to \`${urlKey}\`. You can fix this by mapping one of the controller properties to a different query param key via the \`as\` config option, e.g. \`${qpOther.prop}: { as: 'other-${qpOther.prop}' }\``, false));
|
|
8892
|
-
}
|
|
8893
|
-
qpsByUrlKey[urlKey] = qp;
|
|
8894
|
-
}
|
|
8895
|
-
qps.push(qp);
|
|
8896
|
-
}
|
|
8897
|
-
Object.assign(map, qpMeta.map);
|
|
8898
|
-
}
|
|
8899
|
-
let finalQPMeta = {
|
|
8900
|
-
qps,
|
|
8901
|
-
map
|
|
8902
|
-
};
|
|
8903
|
-
if (shouldCache) {
|
|
8904
|
-
this._qpCache[leafRouteName] = finalQPMeta;
|
|
8905
|
-
}
|
|
8906
|
-
return finalQPMeta;
|
|
8907
|
-
}
|
|
8908
|
-
|
|
8909
|
-
/**
|
|
8910
|
-
Maps all query param keys to their fully scoped property name of the form
|
|
8911
|
-
`controllerName:propName`.
|
|
8912
|
-
@private
|
|
8913
|
-
@method _fullyScopeQueryParams
|
|
8914
|
-
@param {String} leafRouteName
|
|
8915
|
-
@param {Array<Object>} contexts
|
|
8916
|
-
@param {Object} queryParams
|
|
8917
|
-
@return {Void}
|
|
8918
|
-
*/
|
|
8919
|
-
_fullyScopeQueryParams(leafRouteName, contexts, queryParams) {
|
|
8920
|
-
let state = calculatePostTransitionState(this, leafRouteName, contexts);
|
|
8921
|
-
let routeInfos = state.routeInfos;
|
|
8922
|
-
let qpMeta;
|
|
8923
|
-
for (let routeInfo of routeInfos) {
|
|
8924
|
-
qpMeta = this._getQPMeta(routeInfo);
|
|
8925
|
-
if (!qpMeta) {
|
|
8926
|
-
continue;
|
|
8927
|
-
}
|
|
8928
|
-
for (let qp of qpMeta.qps) {
|
|
8929
|
-
let presentProp = qp.prop in queryParams && qp.prop || qp.scopedPropertyName in queryParams && qp.scopedPropertyName || qp.urlKey in queryParams && qp.urlKey;
|
|
8930
|
-
if (presentProp) {
|
|
8931
|
-
if (presentProp !== qp.scopedPropertyName) {
|
|
8932
|
-
queryParams[qp.scopedPropertyName] = queryParams[presentProp];
|
|
8933
|
-
delete queryParams[presentProp];
|
|
8934
|
-
}
|
|
8935
|
-
}
|
|
8936
|
-
}
|
|
8937
|
-
}
|
|
8938
|
-
}
|
|
8939
|
-
|
|
8940
|
-
/**
|
|
8941
|
-
Hydrates (adds/restores) any query params that have pre-existing values into
|
|
8942
|
-
the given queryParams hash. This is what allows query params to be "sticky"
|
|
8943
|
-
and restore their last known values for their scope.
|
|
8944
|
-
@private
|
|
8945
|
-
@method _hydrateUnsuppliedQueryParams
|
|
8946
|
-
@param {TransitionState} state
|
|
8947
|
-
@param {Object} queryParams
|
|
8948
|
-
@return {Void}
|
|
8949
|
-
*/
|
|
8950
|
-
_hydrateUnsuppliedQueryParams(state, queryParams, _fromRouterService) {
|
|
8951
|
-
let routeInfos = state.routeInfos;
|
|
8952
|
-
let appCache = this._bucketCache;
|
|
8953
|
-
let qpMeta;
|
|
8954
|
-
let qp;
|
|
8955
|
-
let presentProp;
|
|
8956
|
-
for (let routeInfo of routeInfos) {
|
|
8957
|
-
qpMeta = this._getQPMeta(routeInfo);
|
|
8958
|
-
if (!qpMeta) {
|
|
8959
|
-
continue;
|
|
8960
|
-
}
|
|
8961
|
-
|
|
8962
|
-
// Needs to stay for index loop to avoid throwIfClosureRequired
|
|
8963
|
-
for (let j = 0, qpLen = qpMeta.qps.length; j < qpLen; ++j) {
|
|
8964
|
-
qp = qpMeta.qps[j];
|
|
8965
|
-
(isDevelopingApp() && !(qp) && assert('expected qp', qp));
|
|
8966
|
-
presentProp = qp.prop in queryParams && qp.prop || qp.scopedPropertyName in queryParams && qp.scopedPropertyName || qp.urlKey in queryParams && qp.urlKey;
|
|
8967
|
-
(isDevelopingApp() && !(function () {
|
|
8968
|
-
if (qp.urlKey === presentProp || qp.scopedPropertyName === presentProp) {
|
|
8969
|
-
return true;
|
|
8970
|
-
}
|
|
8971
|
-
if (_fromRouterService && presentProp !== false && qp.urlKey !== qp.prop) {
|
|
8972
|
-
// assumptions (mainly from current transitionTo_test):
|
|
8973
|
-
// - this is only supposed to be run when there is an alias to a query param and the alias is used to set the param
|
|
8974
|
-
// - when there is no alias: qp.urlKey == qp.prop
|
|
8975
|
-
return false;
|
|
8976
|
-
}
|
|
8977
|
-
return true;
|
|
8978
|
-
}()) && assert(`You passed the \`${presentProp}\` query parameter during a transition into ${qp.route.routeName}, please update to ${qp.urlKey}`, function () {
|
|
8979
|
-
if (qp.urlKey === presentProp || qp.scopedPropertyName === presentProp) {
|
|
8980
|
-
return true;
|
|
8981
|
-
}
|
|
8982
|
-
if (_fromRouterService && presentProp !== false && qp.urlKey !== qp.prop) {
|
|
8983
|
-
return false;
|
|
8984
|
-
}
|
|
8985
|
-
return true;
|
|
8986
|
-
}()));
|
|
8987
|
-
if (presentProp) {
|
|
8988
|
-
if (presentProp !== qp.scopedPropertyName) {
|
|
8989
|
-
queryParams[qp.scopedPropertyName] = queryParams[presentProp];
|
|
8990
|
-
delete queryParams[presentProp];
|
|
8991
|
-
}
|
|
8992
|
-
} else {
|
|
8993
|
-
let cacheKey = calculateCacheKey(qp.route.fullRouteName, qp.parts, state.params);
|
|
8994
|
-
(isDevelopingApp() && !(appCache) && assert('ROUTER BUG: expected appCache to be defined. This is an internal bug, please open an issue on Github if you see this message!', appCache));
|
|
8995
|
-
queryParams[qp.scopedPropertyName] = appCache.lookup(cacheKey, qp.prop, qp.defaultValue);
|
|
8996
|
-
}
|
|
8997
|
-
}
|
|
8998
|
-
}
|
|
8999
|
-
}
|
|
9000
|
-
_scheduleLoadingEvent(transition, originRoute) {
|
|
9001
|
-
this._cancelSlowTransitionTimer();
|
|
9002
|
-
this._slowTransitionTimer = scheduleOnce('routerTransitions', this, this._handleSlowTransition, transition, originRoute);
|
|
9003
|
-
}
|
|
9004
|
-
currentState = null;
|
|
9005
|
-
targetState = null;
|
|
9006
|
-
_handleSlowTransition(transition, originRoute) {
|
|
9007
|
-
if (!this._routerMicrolib.activeTransition) {
|
|
9008
|
-
// Don't fire an event if we've since moved on from
|
|
9009
|
-
// the transition that put us in a loading state.
|
|
9010
|
-
return;
|
|
9011
|
-
}
|
|
9012
|
-
let targetState = new RouterState(this, this._routerMicrolib, this._routerMicrolib.activeTransition[STATE_SYMBOL]);
|
|
9013
|
-
this.set('targetState', targetState);
|
|
9014
|
-
transition.trigger(true, 'loading', transition, originRoute);
|
|
9015
|
-
}
|
|
9016
|
-
_cancelSlowTransitionTimer() {
|
|
9017
|
-
if (this._slowTransitionTimer) {
|
|
9018
|
-
cancel(this._slowTransitionTimer);
|
|
9019
|
-
}
|
|
9020
|
-
this._slowTransitionTimer = null;
|
|
9021
|
-
}
|
|
9022
|
-
|
|
9023
|
-
// These three helper functions are used to ensure errors aren't
|
|
9024
|
-
// re-raised if they're handled in a route's error action.
|
|
9025
|
-
_markErrorAsHandled(error) {
|
|
9026
|
-
this._handledErrors.add(error);
|
|
9027
|
-
}
|
|
9028
|
-
_isErrorHandled(error) {
|
|
9029
|
-
return this._handledErrors.has(error);
|
|
9030
|
-
}
|
|
9031
|
-
_clearHandledError(error) {
|
|
9032
|
-
this._handledErrors.delete(error);
|
|
9033
|
-
}
|
|
9034
|
-
_getEngineInstance({
|
|
9035
|
-
name,
|
|
9036
|
-
instanceId,
|
|
9037
|
-
mountPoint
|
|
9038
|
-
}) {
|
|
9039
|
-
let engineInstances = this._engineInstances;
|
|
9040
|
-
let namedInstances = engineInstances[name];
|
|
9041
|
-
if (!namedInstances) {
|
|
9042
|
-
namedInstances = Object.create(null);
|
|
9043
|
-
engineInstances[name] = namedInstances;
|
|
9044
|
-
}
|
|
9045
|
-
|
|
9046
|
-
// We just set these!
|
|
9047
|
-
(isDevelopingApp() && !(namedInstances) && assert('has namedInstances', namedInstances));
|
|
9048
|
-
let engineInstance = namedInstances[instanceId];
|
|
9049
|
-
if (!engineInstance) {
|
|
9050
|
-
let owner = getOwner$1(this);
|
|
9051
|
-
(isDevelopingApp() && !(owner instanceof EmberEngineInstance) && assert('Expected router to have EngineInstance as owner', owner instanceof EmberEngineInstance));
|
|
9052
|
-
(isDevelopingApp() && !(owner.hasRegistration(`engine:${name}`)) && assert(`You attempted to mount the engine '${name}' in your router map, but the engine can not be found.`, owner.hasRegistration(`engine:${name}`)));
|
|
9053
|
-
engineInstance = owner.buildChildEngineInstance(name, {
|
|
9054
|
-
routable: true,
|
|
9055
|
-
mountPoint
|
|
9056
|
-
});
|
|
9057
|
-
engineInstance.boot();
|
|
9058
|
-
namedInstances[instanceId] = engineInstance;
|
|
9059
|
-
}
|
|
9060
|
-
return engineInstance;
|
|
9061
|
-
}
|
|
9062
|
-
|
|
9063
|
-
/**
|
|
9064
|
-
Handles updating the paths and notifying any listeners of the URL
|
|
9065
|
-
change.
|
|
9066
|
-
Triggers the router level `didTransition` hook.
|
|
9067
|
-
For example, to notify google analytics when the route changes,
|
|
9068
|
-
you could use this hook. (Note: requires also including GA scripts, etc.)
|
|
9069
|
-
```javascript
|
|
9070
|
-
import config from './config/environment';
|
|
9071
|
-
import EmberRouter from '@ember/routing/router';
|
|
9072
|
-
import { service } from '@ember/service';
|
|
9073
|
-
let Router = EmberRouter.extend({
|
|
9074
|
-
location: config.locationType,
|
|
9075
|
-
router: service(),
|
|
9076
|
-
didTransition: function() {
|
|
9077
|
-
this._super(...arguments);
|
|
9078
|
-
ga('send', 'pageview', {
|
|
9079
|
-
page: this.router.currentURL,
|
|
9080
|
-
title: this.router.currentRouteName,
|
|
9081
|
-
});
|
|
9082
|
-
}
|
|
9083
|
-
});
|
|
9084
|
-
```
|
|
9085
|
-
@method didTransition
|
|
9086
|
-
@private
|
|
9087
|
-
@since 1.2.0
|
|
9088
|
-
*/
|
|
9089
|
-
// Set with reopen to allow overriding via extend
|
|
9090
|
-
|
|
9091
|
-
/**
|
|
9092
|
-
Handles notifying any listeners of an impending URL
|
|
9093
|
-
change.
|
|
9094
|
-
Triggers the router level `willTransition` hook.
|
|
9095
|
-
@method willTransition
|
|
9096
|
-
@private
|
|
9097
|
-
@since 1.11.0
|
|
9098
|
-
*/
|
|
9099
|
-
// Set with reopen to allow overriding via extend
|
|
9100
|
-
|
|
9101
|
-
/**
|
|
9102
|
-
Represents the current URL.
|
|
9103
|
-
@property url
|
|
9104
|
-
@type {String}
|
|
9105
|
-
@private
|
|
9106
|
-
*/
|
|
9107
|
-
// Set with reopen to allow overriding via extend
|
|
9108
|
-
}
|
|
9109
|
-
|
|
9110
|
-
/*
|
|
9111
|
-
Helper function for iterating over routes in a set of routeInfos that are
|
|
9112
|
-
at or above the given origin route. Example: if `originRoute` === 'foo.bar'
|
|
9113
|
-
and the routeInfos given were for 'foo.bar.baz', then the given callback
|
|
9114
|
-
will be invoked with the routes for 'foo.bar', 'foo', and 'application'
|
|
9115
|
-
individually.
|
|
9116
|
-
|
|
9117
|
-
If the callback returns anything other than `true`, then iteration will stop.
|
|
9118
|
-
|
|
9119
|
-
@private
|
|
9120
|
-
@param {Route} originRoute
|
|
9121
|
-
@param {Array<RouteInfo>} routeInfos
|
|
9122
|
-
@param {Function} callback
|
|
9123
|
-
@return {Void}
|
|
9124
|
-
*/
|
|
9125
|
-
function forEachRouteAbove(routeInfos, callback) {
|
|
9126
|
-
for (let i = routeInfos.length - 1; i >= 0; --i) {
|
|
9127
|
-
let routeInfo = routeInfos[i];
|
|
9128
|
-
(isDevelopingApp() && !(routeInfo) && assert('has routeInfo', routeInfo));
|
|
9129
|
-
let route = routeInfo.route;
|
|
9130
|
-
|
|
9131
|
-
// routeInfo.handler being `undefined` generally means either:
|
|
9132
|
-
//
|
|
9133
|
-
// 1. an error occurred during creation of the route in question
|
|
9134
|
-
// 2. the route is across an async boundary (e.g. within an engine)
|
|
9135
|
-
//
|
|
9136
|
-
// In both of these cases, we cannot invoke the callback on that specific
|
|
9137
|
-
// route, because it just doesn't exist...
|
|
9138
|
-
if (route === undefined) {
|
|
9139
|
-
continue;
|
|
9140
|
-
}
|
|
9141
|
-
if (callback(route, routeInfo) !== true) {
|
|
9142
|
-
return;
|
|
9143
|
-
}
|
|
9144
|
-
}
|
|
9145
|
-
}
|
|
9146
|
-
|
|
9147
|
-
// These get invoked when an action bubbles above ApplicationRoute
|
|
9148
|
-
// and are not meant to be overridable.
|
|
9149
|
-
let defaultActionHandlers = {
|
|
9150
|
-
willResolveModel(_routeInfos, transition, originRoute) {
|
|
9151
|
-
this._scheduleLoadingEvent(transition, originRoute);
|
|
9152
|
-
},
|
|
9153
|
-
// Attempt to find an appropriate error route or substate to enter.
|
|
9154
|
-
error(routeInfos, error, transition) {
|
|
9155
|
-
let router = this;
|
|
9156
|
-
let routeInfoWithError = routeInfos[routeInfos.length - 1];
|
|
9157
|
-
forEachRouteAbove(routeInfos, (route, routeInfo) => {
|
|
9158
|
-
// We don't check the leaf most routeInfo since that would
|
|
9159
|
-
// technically be below where we're at in the route hierarchy.
|
|
9160
|
-
if (routeInfo !== routeInfoWithError) {
|
|
9161
|
-
// Check for the existence of an 'error' route.
|
|
9162
|
-
let errorRouteName = findRouteStateName(route, 'error');
|
|
9163
|
-
if (errorRouteName) {
|
|
9164
|
-
router._markErrorAsHandled(error);
|
|
9165
|
-
router.intermediateTransitionTo(errorRouteName, error);
|
|
9166
|
-
return false;
|
|
9167
|
-
}
|
|
9168
|
-
}
|
|
9169
|
-
|
|
9170
|
-
// Check for an 'error' substate route
|
|
9171
|
-
let errorSubstateName = findRouteSubstateName(route, 'error');
|
|
9172
|
-
if (errorSubstateName) {
|
|
9173
|
-
router._markErrorAsHandled(error);
|
|
9174
|
-
router.intermediateTransitionTo(errorSubstateName, error);
|
|
9175
|
-
return false;
|
|
9176
|
-
}
|
|
9177
|
-
return true;
|
|
9178
|
-
});
|
|
9179
|
-
logError(error, `Error while processing route: ${transition.targetName}`);
|
|
9180
|
-
},
|
|
9181
|
-
// Attempt to find an appropriate loading route or substate to enter.
|
|
9182
|
-
loading(routeInfos, transition) {
|
|
9183
|
-
let router = this;
|
|
9184
|
-
let routeInfoWithSlowLoading = routeInfos[routeInfos.length - 1];
|
|
9185
|
-
forEachRouteAbove(routeInfos, (route, routeInfo) => {
|
|
9186
|
-
// We don't check the leaf most routeInfos since that would
|
|
9187
|
-
// technically be below where we're at in the route hierarchy.
|
|
9188
|
-
if (routeInfo !== routeInfoWithSlowLoading) {
|
|
9189
|
-
// Check for the existence of a 'loading' route.
|
|
9190
|
-
let loadingRouteName = findRouteStateName(route, 'loading');
|
|
9191
|
-
if (loadingRouteName) {
|
|
9192
|
-
router.intermediateTransitionTo(loadingRouteName);
|
|
9193
|
-
return false;
|
|
9194
|
-
}
|
|
9195
|
-
}
|
|
9196
|
-
|
|
9197
|
-
// Check for loading substate
|
|
9198
|
-
let loadingSubstateName = findRouteSubstateName(route, 'loading');
|
|
9199
|
-
if (loadingSubstateName) {
|
|
9200
|
-
router.intermediateTransitionTo(loadingSubstateName);
|
|
9201
|
-
return false;
|
|
9202
|
-
}
|
|
9203
|
-
|
|
9204
|
-
// Don't bubble above pivot route.
|
|
9205
|
-
return transition.pivotHandler !== route;
|
|
9206
|
-
});
|
|
9207
|
-
}
|
|
9208
|
-
};
|
|
9209
|
-
function logError(_error, initialMessage) {
|
|
9210
|
-
let errorArgs = [];
|
|
9211
|
-
let error;
|
|
9212
|
-
if (_error && typeof _error === 'object' && typeof _error.errorThrown === 'object') {
|
|
9213
|
-
error = _error.errorThrown;
|
|
9214
|
-
} else {
|
|
9215
|
-
error = _error;
|
|
9216
|
-
}
|
|
9217
|
-
if (initialMessage) {
|
|
9218
|
-
errorArgs.push(initialMessage);
|
|
9219
|
-
}
|
|
9220
|
-
if (error) {
|
|
9221
|
-
if (error.message) {
|
|
9222
|
-
errorArgs.push(error.message);
|
|
9223
|
-
}
|
|
9224
|
-
if (error.stack) {
|
|
9225
|
-
errorArgs.push(error.stack);
|
|
9226
|
-
}
|
|
9227
|
-
if (typeof error === 'string') {
|
|
9228
|
-
errorArgs.push(error);
|
|
9229
|
-
}
|
|
9230
|
-
}
|
|
9231
|
-
console.error(...errorArgs); //eslint-disable-line no-console
|
|
9232
|
-
}
|
|
9233
|
-
|
|
9234
|
-
/**
|
|
9235
|
-
Finds the name of the substate route if it exists for the given route. A
|
|
9236
|
-
substate route is of the form `route_state`, such as `foo_loading`.
|
|
9237
|
-
|
|
9238
|
-
@private
|
|
9239
|
-
@param {Route} route
|
|
9240
|
-
@param {String} state
|
|
9241
|
-
@return {String}
|
|
9242
|
-
*/
|
|
9243
|
-
function findRouteSubstateName(route, state) {
|
|
9244
|
-
let owner = getOwner$1(route);
|
|
9245
|
-
(isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
|
|
9246
|
-
let {
|
|
9247
|
-
routeName,
|
|
9248
|
-
fullRouteName,
|
|
9249
|
-
_router: router
|
|
9250
|
-
} = route;
|
|
9251
|
-
let substateName = `${routeName}_${state}`;
|
|
9252
|
-
let substateNameFull = `${fullRouteName}_${state}`;
|
|
9253
|
-
return routeHasBeenDefined(owner, router, substateName, substateNameFull) ? substateNameFull : '';
|
|
9254
|
-
}
|
|
9255
|
-
|
|
9256
|
-
/**
|
|
9257
|
-
Finds the name of the state route if it exists for the given route. A state
|
|
9258
|
-
route is of the form `route.state`, such as `foo.loading`. Properly Handles
|
|
9259
|
-
`application` named routes.
|
|
9260
|
-
|
|
9261
|
-
@private
|
|
9262
|
-
@param {Route} route
|
|
9263
|
-
@param {String} state
|
|
9264
|
-
@return {String}
|
|
9265
|
-
*/
|
|
9266
|
-
function findRouteStateName(route, state) {
|
|
9267
|
-
let owner = getOwner$1(route);
|
|
9268
|
-
(isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
|
|
9269
|
-
let {
|
|
9270
|
-
routeName,
|
|
9271
|
-
fullRouteName,
|
|
9272
|
-
_router: router
|
|
9273
|
-
} = route;
|
|
9274
|
-
let stateName = routeName === 'application' ? state : `${routeName}.${state}`;
|
|
9275
|
-
let stateNameFull = fullRouteName === 'application' ? state : `${fullRouteName}.${state}`;
|
|
9276
|
-
return routeHasBeenDefined(owner, router, stateName, stateNameFull) ? stateNameFull : '';
|
|
9277
|
-
}
|
|
9278
|
-
|
|
9279
|
-
/**
|
|
9280
|
-
Determines whether or not a route has been defined by checking that the route
|
|
9281
|
-
is in the Router's map and the owner has a registration for that route.
|
|
9282
|
-
|
|
9283
|
-
@private
|
|
9284
|
-
@param {Owner} owner
|
|
9285
|
-
@param {Router} router
|
|
9286
|
-
@param {String} localName
|
|
9287
|
-
@param {String} fullName
|
|
9288
|
-
@return {Boolean}
|
|
9289
|
-
*/
|
|
9290
|
-
function routeHasBeenDefined(owner, router, localName, fullName) {
|
|
9291
|
-
let routerHasRoute = router.hasRoute(fullName);
|
|
9292
|
-
let ownerHasRoute = owner.factoryFor(`template:${localName}`) || owner.factoryFor(`route:${localName}`);
|
|
9293
|
-
return routerHasRoute && ownerHasRoute;
|
|
9294
|
-
}
|
|
9295
|
-
function triggerEvent(routeInfos, ignoreFailure, name, args) {
|
|
9296
|
-
if (!routeInfos) {
|
|
9297
|
-
if (ignoreFailure) {
|
|
9298
|
-
return;
|
|
9299
|
-
}
|
|
9300
|
-
// TODO: update?
|
|
9301
|
-
throw new Error(`Can't trigger action '${name}' because your app hasn't finished transitioning into its first route. To trigger an action on destination routes during a transition, you can call \`.send()\` on the \`Transition\` object passed to the \`model/beforeModel/afterModel\` hooks.`);
|
|
9302
|
-
}
|
|
9303
|
-
let eventWasHandled = false;
|
|
9304
|
-
let routeInfo, handler, actionHandler;
|
|
9305
|
-
for (let i = routeInfos.length - 1; i >= 0; i--) {
|
|
9306
|
-
routeInfo = routeInfos[i];
|
|
9307
|
-
(isDevelopingApp() && !(routeInfo) && assert('[BUG] Missing routeInfo', routeInfo));
|
|
9308
|
-
handler = routeInfo.route;
|
|
9309
|
-
actionHandler = handler && handler.actions && handler.actions[name];
|
|
9310
|
-
if (actionHandler) {
|
|
9311
|
-
if (actionHandler.apply(handler, args) === true) {
|
|
9312
|
-
eventWasHandled = true;
|
|
9313
|
-
} else {
|
|
9314
|
-
// Should only hit here if a non-bubbling error action is triggered on a route.
|
|
9315
|
-
if (name === 'error') {
|
|
9316
|
-
(isDevelopingApp() && !(handler) && assert('[BUG] Missing handler', handler));
|
|
9317
|
-
handler._router._markErrorAsHandled(args[0]);
|
|
9318
|
-
}
|
|
9319
|
-
return;
|
|
9320
|
-
}
|
|
9321
|
-
}
|
|
9322
|
-
}
|
|
9323
|
-
let defaultHandler = defaultActionHandlers[name];
|
|
9324
|
-
if (defaultHandler) {
|
|
9325
|
-
defaultHandler.call(this, routeInfos, ...args);
|
|
9326
|
-
return;
|
|
9327
|
-
}
|
|
9328
|
-
if (!eventWasHandled && !ignoreFailure) {
|
|
9329
|
-
throw new Error(`Nothing handled the action '${name}'. If you did handle the action, this error can be caused by returning true from an action handler in a controller, causing the action to bubble.`);
|
|
9330
|
-
}
|
|
9331
|
-
}
|
|
9332
|
-
function calculatePostTransitionState(emberRouter, leafRouteName, contexts) {
|
|
9333
|
-
let state = emberRouter._routerMicrolib.applyIntent(leafRouteName, contexts);
|
|
9334
|
-
let {
|
|
9335
|
-
routeInfos,
|
|
9336
|
-
params
|
|
9337
|
-
} = state;
|
|
9338
|
-
for (let routeInfo of routeInfos) {
|
|
9339
|
-
// If the routeInfo is not resolved, we serialize the context into params
|
|
9340
|
-
if (!routeInfo.isResolved) {
|
|
9341
|
-
params[routeInfo.name] = routeInfo.serialize(routeInfo.context);
|
|
9342
|
-
} else {
|
|
9343
|
-
params[routeInfo.name] = routeInfo.params;
|
|
9344
|
-
}
|
|
9345
|
-
}
|
|
9346
|
-
return state;
|
|
9347
|
-
}
|
|
9348
|
-
function updatePaths(router) {
|
|
9349
|
-
let infos = router._routerMicrolib.currentRouteInfos;
|
|
9350
|
-
if (infos.length === 0) {
|
|
9351
|
-
return;
|
|
9352
|
-
}
|
|
9353
|
-
let path = EmberRouter._routePath(infos);
|
|
9354
|
-
let info = infos[infos.length - 1];
|
|
9355
|
-
(isDevelopingApp() && !(info) && assert('expected info', info));
|
|
9356
|
-
let currentRouteName = info.name;
|
|
9357
|
-
let location = router.location;
|
|
9358
|
-
(isDevelopingApp() && !(typeof location !== 'string') && assert('expected location to not be a string', typeof location !== 'string'));
|
|
9359
|
-
let currentURL = location.getURL();
|
|
9360
|
-
set(router, 'currentPath', path);
|
|
9361
|
-
set(router, 'currentRouteName', currentRouteName);
|
|
9362
|
-
set(router, 'currentURL', currentURL);
|
|
9363
|
-
}
|
|
9364
|
-
function didBeginTransition(transition, router) {
|
|
9365
|
-
let routerState = new RouterState(router, router._routerMicrolib, transition[STATE_SYMBOL]);
|
|
9366
|
-
if (!router.currentState) {
|
|
9367
|
-
router.set('currentState', routerState);
|
|
9368
|
-
}
|
|
9369
|
-
router.set('targetState', routerState);
|
|
9370
|
-
transition.promise = transition.catch(error => {
|
|
9371
|
-
if (router._isErrorHandled(error)) {
|
|
9372
|
-
router._clearHandledError(error);
|
|
9373
|
-
} else {
|
|
9374
|
-
throw error;
|
|
9375
|
-
}
|
|
9376
|
-
}, 'Transition Error');
|
|
9377
|
-
}
|
|
9378
|
-
function forEachQueryParam(router, routeInfos, queryParams, callback) {
|
|
9379
|
-
let qpCache = router._queryParamsFor(routeInfos);
|
|
9380
|
-
for (let key in queryParams) {
|
|
9381
|
-
if (!Object.prototype.hasOwnProperty.call(queryParams, key)) {
|
|
9382
|
-
continue;
|
|
9383
|
-
}
|
|
9384
|
-
let value = queryParams[key];
|
|
9385
|
-
let qp = qpCache.map[key];
|
|
9386
|
-
callback(key, value, qp);
|
|
9387
|
-
}
|
|
9388
|
-
}
|
|
9389
|
-
EmberRouter.reopen({
|
|
9390
|
-
didTransition: defaultDidTransition,
|
|
9391
|
-
willTransition: defaultWillTransition,
|
|
9392
|
-
rootURL: '/',
|
|
9393
|
-
location: 'hash',
|
|
9394
|
-
// FIXME: Does this need to be overrideable via extend?
|
|
9395
|
-
url: computed(function () {
|
|
9396
|
-
let location = get(this, 'location');
|
|
9397
|
-
if (typeof location === 'string') {
|
|
9398
|
-
return undefined;
|
|
9399
|
-
}
|
|
9400
|
-
return location.getURL();
|
|
9401
|
-
})
|
|
9402
|
-
});
|
|
9403
|
-
const EmberRouter$1 = EmberRouter;
|
|
9404
|
-
|
|
9405
|
-
/**
|
|
9406
|
-
* @module @ember/routing/router-service
|
|
9407
|
-
*/
|
|
9408
|
-
const ROUTER = Symbol('ROUTER');
|
|
9409
|
-
function cleanURL(url, rootURL) {
|
|
9410
|
-
if (rootURL === '/') {
|
|
9411
|
-
return url;
|
|
9412
|
-
}
|
|
9413
|
-
return url.substring(rootURL.length);
|
|
9414
|
-
}
|
|
9415
|
-
|
|
9416
|
-
/**
|
|
9417
|
-
The Router service is the public API that provides access to the router.
|
|
9418
|
-
|
|
9419
|
-
The immediate benefit of the Router service is that you can inject it into components,
|
|
9420
|
-
giving them a friendly way to initiate transitions and ask questions about the current
|
|
9421
|
-
global router state.
|
|
9422
|
-
|
|
9423
|
-
In this example, the Router service is injected into a component to initiate a transition
|
|
9424
|
-
to a dedicated route:
|
|
9425
|
-
|
|
9426
|
-
```app/components/example.js
|
|
9427
|
-
import Component from '@glimmer/component';
|
|
9428
|
-
import { action } from '@ember/object';
|
|
9429
|
-
import { service } from '@ember/service';
|
|
9430
|
-
|
|
9431
|
-
export default class ExampleComponent extends Component {
|
|
9432
|
-
@service router;
|
|
9433
|
-
|
|
9434
|
-
@action
|
|
9435
|
-
next() {
|
|
9436
|
-
this.router.transitionTo('other.route');
|
|
9437
|
-
}
|
|
9438
|
-
}
|
|
9439
|
-
```
|
|
9440
|
-
|
|
9441
|
-
Like any service, it can also be injected into helpers, routes, etc.
|
|
9442
|
-
|
|
9443
|
-
@public
|
|
9444
|
-
@extends Service
|
|
9445
|
-
@class RouterService
|
|
9446
|
-
*/
|
|
9447
|
-
|
|
9448
|
-
class RouterService extends Service.extend(Evented) {
|
|
9449
|
-
[ROUTER];
|
|
9450
|
-
get _router() {
|
|
9451
|
-
let router = this[ROUTER];
|
|
9452
|
-
if (router !== undefined) {
|
|
9453
|
-
return router;
|
|
9454
|
-
}
|
|
9455
|
-
let owner = getOwner(this);
|
|
9456
|
-
(isDevelopingApp() && !(owner) && assert('RouterService is unexpectedly missing an owner', owner));
|
|
9457
|
-
let _router = owner.lookup('router:main');
|
|
9458
|
-
(isDevelopingApp() && !(_router instanceof EmberRouter$1) && assert('ROUTER SERVICE BUG: Expected router to be an instance of EmberRouter', _router instanceof EmberRouter$1));
|
|
9459
|
-
return this[ROUTER] = _router;
|
|
9460
|
-
}
|
|
9461
|
-
willDestroy() {
|
|
9462
|
-
super.willDestroy();
|
|
9463
|
-
this[ROUTER] = undefined;
|
|
9464
|
-
}
|
|
9465
|
-
|
|
9466
|
-
/**
|
|
9467
|
-
Transition the application into another route. The route may
|
|
9468
|
-
be either a single route or route path:
|
|
9469
|
-
Calling `transitionTo` from the Router service will cause default query parameter values to be included in the URL.
|
|
9470
|
-
This behavior is different from calling `transitionTo` on a route or `transitionToRoute` on a controller.
|
|
9471
|
-
See the [Router Service RFC](https://github.com/emberjs/rfcs/blob/master/text/0095-router-service.md#query-parameter-semantics) for more info.
|
|
9472
|
-
In the following example we use the Router service to navigate to a route with a
|
|
9473
|
-
specific model from a Component in the first action, and in the second we trigger
|
|
9474
|
-
a query-params only transition.
|
|
9475
|
-
```app/components/example.js
|
|
9476
|
-
import Component from '@glimmer/component';
|
|
9477
|
-
import { action } from '@ember/object';
|
|
9478
|
-
import { service } from '@ember/service';
|
|
9479
|
-
export default class extends Component {
|
|
9480
|
-
@service router;
|
|
9481
|
-
@action
|
|
9482
|
-
goToComments(post) {
|
|
9483
|
-
this.router.transitionTo('comments', post);
|
|
9484
|
-
}
|
|
9485
|
-
@action
|
|
9486
|
-
fetchMoreComments(latestComment) {
|
|
9487
|
-
this.router.transitionTo({
|
|
9488
|
-
queryParams: { commentsAfter: latestComment }
|
|
9489
|
-
});
|
|
9490
|
-
}
|
|
9491
|
-
}
|
|
9492
|
-
```
|
|
9493
|
-
@method transitionTo
|
|
9494
|
-
@param {String} [routeNameOrUrl] the name of the route or a URL
|
|
9495
|
-
@param {...Object} [models] the model(s) or identifier(s) to be used while
|
|
9496
|
-
transitioning to the route.
|
|
9497
|
-
@param {Object} [options] optional hash with a queryParams property
|
|
9498
|
-
containing a mapping of query parameters. May be supplied as the only
|
|
9499
|
-
parameter to trigger a query-parameter-only transition.
|
|
9500
|
-
@return {Transition} the transition object associated with this
|
|
9501
|
-
attempted transition
|
|
9502
|
-
@public
|
|
9503
|
-
*/
|
|
9504
|
-
transitionTo(...args) {
|
|
9505
|
-
if (resemblesURL(args[0])) {
|
|
9506
|
-
// NOTE: this `args[0] as string` cast is safe and TS correctly infers it
|
|
9507
|
-
// in 3.6+, so it can be removed when TS is upgraded.
|
|
9508
|
-
return this._router._doURLTransition('transitionTo', args[0]);
|
|
9509
|
-
}
|
|
9510
|
-
let {
|
|
9511
|
-
routeName,
|
|
9512
|
-
models,
|
|
9513
|
-
queryParams
|
|
9514
|
-
} = extractRouteArgs(args);
|
|
9515
|
-
let transition = this._router._doTransition(routeName, models, queryParams, true);
|
|
9516
|
-
return transition;
|
|
9517
|
-
}
|
|
9518
|
-
|
|
9519
|
-
/**
|
|
9520
|
-
Similar to `transitionTo`, but instead of adding the destination to the browser's URL history,
|
|
9521
|
-
it replaces the entry for the current route.
|
|
9522
|
-
When the user clicks the "back" button in the browser, there will be fewer steps.
|
|
9523
|
-
This is most commonly used to manage redirects in a way that does not cause confusing additions
|
|
9524
|
-
to the user's browsing history.
|
|
9525
|
-
Calling `replaceWith` from the Router service will cause default query parameter values to be included in the URL.
|
|
9526
|
-
This behavior is different from calling `replaceWith` on a route.
|
|
9527
|
-
See the [Router Service RFC](https://github.com/emberjs/rfcs/blob/master/text/0095-router-service.md#query-parameter-semantics) for more info.
|
|
9528
|
-
Usage example:
|
|
9529
|
-
```app/routes/application.js
|
|
9530
|
-
import Route from '@ember/routing/route';
|
|
9531
|
-
import { service } from '@ember/service';
|
|
9532
|
-
export default class extends Route {
|
|
9533
|
-
@service router;
|
|
9534
|
-
beforeModel() {
|
|
9535
|
-
if (!authorized()){
|
|
9536
|
-
this.router.replaceWith('unauthorized');
|
|
9537
|
-
}
|
|
9538
|
-
}
|
|
9539
|
-
});
|
|
9540
|
-
```
|
|
9541
|
-
@method replaceWith
|
|
9542
|
-
@param {String} routeNameOrUrl the name of the route or a URL of the desired destination
|
|
9543
|
-
@param {...Object} models the model(s) or identifier(s) to be used while
|
|
9544
|
-
transitioning to the route i.e. an object of params to pass to the destination route
|
|
9545
|
-
@param {Object} [options] optional hash with a queryParams property
|
|
9546
|
-
containing a mapping of query parameters
|
|
9547
|
-
@return {Transition} the transition object associated with this
|
|
9548
|
-
attempted transition
|
|
9549
|
-
@public
|
|
9550
|
-
*/
|
|
9551
|
-
replaceWith(...args) {
|
|
9552
|
-
return this.transitionTo(...args).method('replace');
|
|
9553
|
-
}
|
|
9554
|
-
|
|
9555
|
-
/**
|
|
9556
|
-
Generate a URL based on the supplied route name and optionally a model. The
|
|
9557
|
-
URL is returned as a string that can be used for any purpose.
|
|
9558
|
-
In this example, the URL for the `author.books` route for a given author
|
|
9559
|
-
is copied to the clipboard.
|
|
9560
|
-
```app/templates/application.hbs
|
|
9561
|
-
<CopyLink @author={{hash id="tomster" name="Tomster"}} />
|
|
9562
|
-
```
|
|
9563
|
-
```app/components/copy-link.js
|
|
9564
|
-
import Component from '@glimmer/component';
|
|
9565
|
-
import { service } from '@ember/service';
|
|
9566
|
-
import { action } from '@ember/object';
|
|
9567
|
-
export default class CopyLinkComponent extends Component {
|
|
9568
|
-
@service router;
|
|
9569
|
-
@service clipboard;
|
|
9570
|
-
@action
|
|
9571
|
-
copyBooksURL() {
|
|
9572
|
-
if (this.author) {
|
|
9573
|
-
const url = this.router.urlFor('author.books', this.args.author);
|
|
9574
|
-
this.clipboard.set(url);
|
|
9575
|
-
// Clipboard now has /author/tomster/books
|
|
9576
|
-
}
|
|
9577
|
-
}
|
|
9578
|
-
}
|
|
9579
|
-
```
|
|
9580
|
-
Just like with `transitionTo` and `replaceWith`, `urlFor` can also handle
|
|
9581
|
-
query parameters.
|
|
9582
|
-
```app/templates/application.hbs
|
|
9583
|
-
<CopyLink @author={{hash id="tomster" name="Tomster"}} />
|
|
9584
|
-
```
|
|
9585
|
-
```app/components/copy-link.js
|
|
9586
|
-
import Component from '@glimmer/component';
|
|
9587
|
-
import { service } from '@ember/service';
|
|
9588
|
-
import { action } from '@ember/object';
|
|
9589
|
-
export default class CopyLinkComponent extends Component {
|
|
9590
|
-
@service router;
|
|
9591
|
-
@service clipboard;
|
|
9592
|
-
@action
|
|
9593
|
-
copyOnlyEmberBooksURL() {
|
|
9594
|
-
if (this.author) {
|
|
9595
|
-
const url = this.router.urlFor('author.books', this.author, {
|
|
9596
|
-
queryParams: { filter: 'emberjs' }
|
|
9597
|
-
});
|
|
9598
|
-
this.clipboard.set(url);
|
|
9599
|
-
// Clipboard now has /author/tomster/books?filter=emberjs
|
|
9600
|
-
}
|
|
9601
|
-
}
|
|
9602
|
-
}
|
|
9603
|
-
```
|
|
9604
|
-
@method urlFor
|
|
9605
|
-
@param {String} routeName the name of the route
|
|
9606
|
-
@param {...Object} models the model(s) for the route.
|
|
9607
|
-
@param {Object} [options] optional hash with a queryParams property
|
|
9608
|
-
containing a mapping of query parameters
|
|
9609
|
-
@return {String} the string representing the generated URL
|
|
9610
|
-
@public
|
|
9611
|
-
*/
|
|
9612
|
-
urlFor(routeName, ...args) {
|
|
9613
|
-
this._router.setupRouter();
|
|
9614
|
-
return this._router.generate(routeName, ...args);
|
|
9615
|
-
}
|
|
9616
|
-
|
|
9617
|
-
/**
|
|
9618
|
-
Returns `true` if `routeName/models/queryParams` is the active route, where `models` and `queryParams` are optional.
|
|
9619
|
-
See [model](api/ember/release/classes/Route/methods/model?anchor=model) and
|
|
9620
|
-
[queryParams](/api/ember/3.7/classes/Route/properties/queryParams?anchor=queryParams) for more information about these arguments.
|
|
9621
|
-
In the following example, `isActive` will return `true` if the current route is `/posts`.
|
|
9622
|
-
```app/components/posts.js
|
|
9623
|
-
import Component from '@glimmer/component';
|
|
9624
|
-
import { service } from '@ember/service';
|
|
9625
|
-
export default class extends Component {
|
|
9626
|
-
@service router;
|
|
9627
|
-
displayComments() {
|
|
9628
|
-
return this.router.isActive('posts');
|
|
9629
|
-
}
|
|
9630
|
-
});
|
|
9631
|
-
```
|
|
9632
|
-
The next example includes a dynamic segment, and will return `true` if the current route is `/posts/1`,
|
|
9633
|
-
assuming the post has an id of 1:
|
|
9634
|
-
```app/components/posts.js
|
|
9635
|
-
import Component from '@glimmer/component';
|
|
9636
|
-
import { service } from '@ember/service';
|
|
9637
|
-
export default class extends Component {
|
|
9638
|
-
@service router;
|
|
9639
|
-
displayComments(post) {
|
|
9640
|
-
return this.router.isActive('posts', post.id);
|
|
9641
|
-
}
|
|
9642
|
-
});
|
|
9643
|
-
```
|
|
9644
|
-
Where `post.id` is the id of a specific post, which is represented in the route as /posts/[post.id].
|
|
9645
|
-
If `post.id` is equal to 1, then isActive will return true if the current route is /posts/1, and false if the route is anything else.
|
|
9646
|
-
@method isActive
|
|
9647
|
-
@param {String} routeName the name of the route
|
|
9648
|
-
@param {...Object} models the model(s) or identifier(s) to be used when determining the active route.
|
|
9649
|
-
@param {Object} [options] optional hash with a queryParams property
|
|
9650
|
-
containing a mapping of query parameters
|
|
9651
|
-
@return {boolean} true if the provided routeName/models/queryParams are active
|
|
9652
|
-
@public
|
|
9653
|
-
*/
|
|
9654
|
-
isActive(...args) {
|
|
9655
|
-
let {
|
|
9656
|
-
routeName,
|
|
9657
|
-
models,
|
|
9658
|
-
queryParams
|
|
9659
|
-
} = extractRouteArgs(args);
|
|
9660
|
-
let routerMicrolib = this._router._routerMicrolib;
|
|
9661
|
-
|
|
9662
|
-
// When using isActive() in a getter, we want to entagle with the auto-tracking system
|
|
9663
|
-
// for example,
|
|
9664
|
-
// in
|
|
9665
|
-
// get isBarActive() {
|
|
9666
|
-
// return isActive('foo.bar');
|
|
9667
|
-
// }
|
|
9668
|
-
//
|
|
9669
|
-
// you'd expect isBarActive to be dirtied when the route changes.
|
|
9670
|
-
//
|
|
9671
|
-
// https://github.com/emberjs/ember.js/issues/19004
|
|
9672
|
-
consumeTag(tagFor(this._router, 'currentURL'));
|
|
9673
|
-
|
|
9674
|
-
// UNSAFE: casting `routeName as string` here encodes the existing
|
|
9675
|
-
// assumption but may be wrong: `extractRouteArgs` correctly returns it as
|
|
9676
|
-
// `string | undefined`. There may be bugs if `isActiveIntent` does
|
|
9677
|
-
// not correctly account for `undefined` values for `routeName`. Spoilers:
|
|
9678
|
-
// it *does not* account for this being `undefined`.
|
|
9679
|
-
if (!routerMicrolib.isActiveIntent(routeName, models)) {
|
|
9680
|
-
return false;
|
|
9681
|
-
}
|
|
9682
|
-
let hasQueryParams = Object.keys(queryParams).length > 0;
|
|
9683
|
-
if (hasQueryParams) {
|
|
9684
|
-
// UNSAFE: casting `routeName as string` here encodes the existing
|
|
9685
|
-
// assumption but may be wrong: `extractRouteArgs` correctly returns it
|
|
9686
|
-
// as `string | undefined`. There may be bugs if `_prepareQueryParams`
|
|
9687
|
-
// does not correctly account for `undefined` values for `routeName`.
|
|
9688
|
-
// Spoilers: under the hood this currently uses router.js APIs which
|
|
9689
|
-
// *do not* account for this being `undefined`.
|
|
9690
|
-
let targetRouteName = routeName;
|
|
9691
|
-
queryParams = Object.assign({}, queryParams);
|
|
9692
|
-
this._router._prepareQueryParams(targetRouteName, models, queryParams, true /* fromRouterService */);
|
|
9693
|
-
let currentQueryParams = Object.assign({}, routerMicrolib.state.queryParams);
|
|
9694
|
-
this._router._prepareQueryParams(targetRouteName, models, currentQueryParams, true /* fromRouterService */);
|
|
9695
|
-
return shallowEqual(queryParams, currentQueryParams);
|
|
9696
|
-
}
|
|
9697
|
-
return true;
|
|
9698
|
-
}
|
|
9699
|
-
|
|
9700
|
-
/**
|
|
9701
|
-
Takes a string URL and returns a `RouteInfo` for the leafmost route represented
|
|
9702
|
-
by the URL. Returns `null` if the URL is not recognized. This method expects to
|
|
9703
|
-
receive the actual URL as seen by the browser including the app's `rootURL`.
|
|
9704
|
-
See [RouteInfo](/ember/release/classes/RouteInfo) for more info.
|
|
9705
|
-
In the following example `recognize` is used to verify if a path belongs to our
|
|
9706
|
-
application before transitioning to it.
|
|
9707
|
-
```
|
|
9708
|
-
import Component from '@ember/component';
|
|
9709
|
-
import { service } from '@ember/service';
|
|
9710
|
-
export default class extends Component {
|
|
9711
|
-
@service router;
|
|
9712
|
-
path = '/';
|
|
9713
|
-
click() {
|
|
9714
|
-
if (this.router.recognize(this.path)) {
|
|
9715
|
-
this.router.transitionTo(this.path);
|
|
9716
|
-
}
|
|
9717
|
-
}
|
|
9718
|
-
}
|
|
9719
|
-
```
|
|
9720
|
-
@method recognize
|
|
9721
|
-
@param {String} url
|
|
9722
|
-
@return {RouteInfo | null}
|
|
9723
|
-
@public
|
|
9724
|
-
*/
|
|
9725
|
-
recognize(url) {
|
|
9726
|
-
(isDevelopingApp() && !(url.indexOf(this.rootURL) === 0) && assert(`You must pass a url that begins with the application's rootURL "${this.rootURL}"`, url.indexOf(this.rootURL) === 0));
|
|
9727
|
-
this._router.setupRouter();
|
|
9728
|
-
let internalURL = cleanURL(url, this.rootURL);
|
|
9729
|
-
return this._router._routerMicrolib.recognize(internalURL);
|
|
9730
|
-
}
|
|
9731
|
-
|
|
9732
|
-
/**
|
|
9733
|
-
Takes a string URL and returns a promise that resolves to a
|
|
9734
|
-
`RouteInfoWithAttributes` for the leafmost route represented by the URL.
|
|
9735
|
-
The promise rejects if the URL is not recognized or an unhandled exception
|
|
9736
|
-
is encountered. This method expects to receive the actual URL as seen by
|
|
9737
|
-
the browser including the app's `rootURL`.
|
|
9738
|
-
@method recognizeAndLoad
|
|
9739
|
-
@param {String} url
|
|
9740
|
-
@return {RouteInfo}
|
|
9741
|
-
@public
|
|
9742
|
-
*/
|
|
9743
|
-
recognizeAndLoad(url) {
|
|
9744
|
-
(isDevelopingApp() && !(url.indexOf(this.rootURL) === 0) && assert(`You must pass a url that begins with the application's rootURL "${this.rootURL}"`, url.indexOf(this.rootURL) === 0));
|
|
9745
|
-
this._router.setupRouter();
|
|
9746
|
-
let internalURL = cleanURL(url, this.rootURL);
|
|
9747
|
-
return this._router._routerMicrolib.recognizeAndLoad(internalURL);
|
|
9748
|
-
}
|
|
9749
|
-
|
|
9750
|
-
/**
|
|
9751
|
-
You can register a listener for events emitted by this service with `.on()`:
|
|
9752
|
-
```app/routes/contact-form.js
|
|
9753
|
-
import Route from '@ember/routing';
|
|
9754
|
-
import { service } from '@ember/service';
|
|
9755
|
-
export default class extends Route {
|
|
9756
|
-
@service router;
|
|
9757
|
-
activate() {
|
|
9758
|
-
this.router.on('routeWillChange', (transition) => {
|
|
9759
|
-
if (!transition.to.find(route => route.name === this.routeName)) {
|
|
9760
|
-
alert("Please save or cancel your changes.");
|
|
9761
|
-
transition.abort();
|
|
9762
|
-
}
|
|
9763
|
-
})
|
|
9764
|
-
}
|
|
9765
|
-
}
|
|
9766
|
-
```
|
|
9767
|
-
@method on
|
|
9768
|
-
@param {String} eventName
|
|
9769
|
-
@param {Function} callback
|
|
9770
|
-
@public
|
|
9771
|
-
*/
|
|
9772
|
-
|
|
9773
|
-
/**
|
|
9774
|
-
You can unregister a listener for events emitted by this service with `.off()`:
|
|
9775
|
-
```app/routes/contact-form.js
|
|
9776
|
-
import Route from '@ember/routing';
|
|
9777
|
-
import { service } from '@ember/service';
|
|
9778
|
-
export default class ContactFormRoute extends Route {
|
|
9779
|
-
@service router;
|
|
9780
|
-
callback = (transition) => {
|
|
9781
|
-
if (!transition.to.find(route => route.name === this.routeName)) {
|
|
9782
|
-
alert('Please save or cancel your changes.');
|
|
9783
|
-
transition.abort();
|
|
9784
|
-
}
|
|
9785
|
-
};
|
|
9786
|
-
activate() {
|
|
9787
|
-
this.router.on('routeWillChange', this.callback);
|
|
9788
|
-
}
|
|
9789
|
-
deactivate() {
|
|
9790
|
-
this.router.off('routeWillChange', this.callback);
|
|
9791
|
-
}
|
|
9792
|
-
}
|
|
9793
|
-
```
|
|
9794
|
-
@method off
|
|
9795
|
-
@param {String} eventName
|
|
9796
|
-
@param {Function} callback
|
|
9797
|
-
@public
|
|
9798
|
-
*/
|
|
9799
|
-
|
|
9800
|
-
/**
|
|
9801
|
-
The `routeWillChange` event is fired at the beginning of any
|
|
9802
|
-
attempted transition with a `Transition` object as the sole
|
|
9803
|
-
argument. This action can be used for aborting, redirecting,
|
|
9804
|
-
or decorating the transition from the currently active routes.
|
|
9805
|
-
A good example is preventing navigation when a form is
|
|
9806
|
-
half-filled out:
|
|
9807
|
-
```app/routes/contact-form.js
|
|
9808
|
-
import Route from '@ember/routing';
|
|
9809
|
-
import { service } from '@ember/service';
|
|
9810
|
-
export default class extends Route {
|
|
9811
|
-
@service router;
|
|
9812
|
-
activate() {
|
|
9813
|
-
this.router.on('routeWillChange', (transition) => {
|
|
9814
|
-
if (!transition.to.find(route => route.name === this.routeName)) {
|
|
9815
|
-
alert("Please save or cancel your changes.");
|
|
9816
|
-
transition.abort();
|
|
9817
|
-
}
|
|
9818
|
-
})
|
|
9819
|
-
}
|
|
9820
|
-
}
|
|
9821
|
-
```
|
|
9822
|
-
The `routeWillChange` event fires whenever a new route is chosen as the desired target of a transition. This includes `transitionTo`, `replaceWith`, all redirection for any reason including error handling, and abort. Aborting implies changing the desired target back to where you already were. Once a transition has completed, `routeDidChange` fires.
|
|
9823
|
-
@event routeWillChange
|
|
9824
|
-
@param {Transition} transition
|
|
9825
|
-
@public
|
|
9826
|
-
*/
|
|
9827
|
-
|
|
9828
|
-
/**
|
|
9829
|
-
The `routeDidChange` event only fires once a transition has settled.
|
|
9830
|
-
This includes aborts and error substates. Like the `routeWillChange` event
|
|
9831
|
-
it receives a Transition as the sole argument.
|
|
9832
|
-
A good example is sending some analytics when the route has transitioned:
|
|
9833
|
-
```app/routes/contact-form.js
|
|
9834
|
-
import Route from '@ember/routing';
|
|
9835
|
-
import { service } from '@ember/service';
|
|
9836
|
-
export default class extends Route {
|
|
9837
|
-
@service router;
|
|
9838
|
-
activate() {
|
|
9839
|
-
this.router.on('routeDidChange', (transition) => {
|
|
9840
|
-
ga.send('pageView', {
|
|
9841
|
-
current: transition.to.name,
|
|
9842
|
-
from: transition.from.name
|
|
9843
|
-
});
|
|
9844
|
-
})
|
|
9845
|
-
}
|
|
9846
|
-
}
|
|
9847
|
-
```
|
|
9848
|
-
`routeDidChange` will be called after any `Route`'s
|
|
9849
|
-
[didTransition](/ember/release/classes/Route/events/didTransition?anchor=didTransition)
|
|
9850
|
-
action has been fired.
|
|
9851
|
-
The updates of properties
|
|
9852
|
-
[currentURL](/ember/release/classes/RouterService/properties/currentURL?anchor=currentURL),
|
|
9853
|
-
[currentRouteName](/ember/release/classes/RouterService/properties/currentURL?anchor=currentRouteName)
|
|
9854
|
-
and
|
|
9855
|
-
[currentRoute](/ember/release/classes/RouterService/properties/currentURL?anchor=currentRoute)
|
|
9856
|
-
are completed at the time `routeDidChange` is called.
|
|
9857
|
-
@event routeDidChange
|
|
9858
|
-
@param {Transition} transition
|
|
9859
|
-
@public
|
|
9860
|
-
*/
|
|
9861
|
-
|
|
9862
|
-
/**
|
|
9863
|
-
* Refreshes all currently active routes, doing a full transition.
|
|
9864
|
-
* If a route name is provided and refers to a currently active route,
|
|
9865
|
-
* it will refresh only that route and its descendents.
|
|
9866
|
-
* Returns a promise that will be resolved once the refresh is complete.
|
|
9867
|
-
* All resetController, beforeModel, model, afterModel, redirect, and setupController
|
|
9868
|
-
* hooks will be called again. You will get new data from the model hook.
|
|
9869
|
-
*
|
|
9870
|
-
* @method refresh
|
|
9871
|
-
* @param {String} [routeName] the route to refresh (along with all child routes)
|
|
9872
|
-
* @return Transition
|
|
9873
|
-
* @public
|
|
9874
|
-
*/
|
|
9875
|
-
refresh(pivotRouteName) {
|
|
9876
|
-
if (!pivotRouteName) {
|
|
9877
|
-
return this._router._routerMicrolib.refresh();
|
|
9878
|
-
}
|
|
9879
|
-
(isDevelopingApp() && !(this._router.hasRoute(pivotRouteName)) && assert(`The route "${pivotRouteName}" was not found`, this._router.hasRoute(pivotRouteName)));
|
|
9880
|
-
(isDevelopingApp() && !(this.isActive(pivotRouteName)) && assert(`The route "${pivotRouteName}" is currently not active`, this.isActive(pivotRouteName)));
|
|
9881
|
-
let owner = getOwner(this);
|
|
9882
|
-
(isDevelopingApp() && !(owner) && assert('RouterService is unexpectedly missing an owner', owner));
|
|
9883
|
-
let pivotRoute = owner.lookup(`route:${pivotRouteName}`);
|
|
9884
|
-
return this._router._routerMicrolib.refresh(pivotRoute);
|
|
9885
|
-
}
|
|
9886
|
-
|
|
9887
|
-
/**
|
|
9888
|
-
Name of the current route.
|
|
9889
|
-
This property represents the logical name of the route,
|
|
9890
|
-
which is dot separated.
|
|
9891
|
-
For the following router:
|
|
9892
|
-
```app/router.js
|
|
9893
|
-
Router.map(function() {
|
|
9894
|
-
this.route('about');
|
|
9895
|
-
this.route('blog', function () {
|
|
9896
|
-
this.route('post', { path: ':post_id' });
|
|
9897
|
-
});
|
|
9898
|
-
});
|
|
9899
|
-
```
|
|
9900
|
-
It will return:
|
|
9901
|
-
* `index` when you visit `/`
|
|
9902
|
-
* `about` when you visit `/about`
|
|
9903
|
-
* `blog.index` when you visit `/blog`
|
|
9904
|
-
* `blog.post` when you visit `/blog/some-post-id`
|
|
9905
|
-
@property currentRouteName
|
|
9906
|
-
@type {String | null}
|
|
9907
|
-
@public
|
|
9908
|
-
*/
|
|
9909
|
-
static {
|
|
9910
|
-
decorateFieldV2(this.prototype, "currentRouteName", [readOnly('_router.currentRouteName')]);
|
|
9911
|
-
}
|
|
9912
|
-
#currentRouteName = (initializeDeferredDecorator(this, "currentRouteName"), void 0);
|
|
9913
|
-
static {
|
|
9914
|
-
decorateFieldV2(this.prototype, "currentURL", [readOnly('_router.currentURL')]);
|
|
9915
|
-
}
|
|
9916
|
-
#currentURL = (initializeDeferredDecorator(this, "currentURL"), void 0);
|
|
9917
|
-
/**
|
|
9918
|
-
Current URL for the application.
|
|
9919
|
-
This property represents the URL path for this route.
|
|
9920
|
-
For the following router:
|
|
9921
|
-
```app/router.js
|
|
9922
|
-
Router.map(function() {
|
|
9923
|
-
this.route('about');
|
|
9924
|
-
this.route('blog', function () {
|
|
9925
|
-
this.route('post', { path: ':post_id' });
|
|
9926
|
-
});
|
|
9927
|
-
});
|
|
9928
|
-
```
|
|
9929
|
-
It will return:
|
|
9930
|
-
* `/` when you visit `/`
|
|
9931
|
-
* `/about` when you visit `/about`
|
|
9932
|
-
* `/blog` when you visit `/blog`
|
|
9933
|
-
* `/blog/some-post-id` when you visit `/blog/some-post-id`
|
|
9934
|
-
@property currentURL
|
|
9935
|
-
@type String
|
|
9936
|
-
@public
|
|
9937
|
-
*/
|
|
9938
|
-
static {
|
|
9939
|
-
decorateFieldV2(this.prototype, "location", [readOnly('_router.location')]);
|
|
9940
|
-
}
|
|
9941
|
-
#location = (initializeDeferredDecorator(this, "location"), void 0);
|
|
9942
|
-
/**
|
|
9943
|
-
The `location` property returns what implementation of the `location` API
|
|
9944
|
-
your application is using, which determines what type of URL is being used.
|
|
9945
|
-
See [Location](/ember/release/classes/Location) for more information.
|
|
9946
|
-
To force a particular `location` API implementation to be used in your
|
|
9947
|
-
application you can set a location type on your `config/environment`.
|
|
9948
|
-
For example, to set the `history` type:
|
|
9949
|
-
```config/environment.js
|
|
9950
|
-
'use strict';
|
|
9951
|
-
module.exports = function(environment) {
|
|
9952
|
-
let ENV = {
|
|
9953
|
-
modulePrefix: 'router-service',
|
|
9954
|
-
environment,
|
|
9955
|
-
rootURL: '/',
|
|
9956
|
-
locationType: 'history',
|
|
9957
|
-
...
|
|
9958
|
-
}
|
|
9959
|
-
}
|
|
9960
|
-
```
|
|
9961
|
-
The following location types are available by default:
|
|
9962
|
-
`hash`, `history`, `none`.
|
|
9963
|
-
See [HashLocation](/ember/release/classes/HashLocation).
|
|
9964
|
-
See [HistoryLocation](/ember/release/classes/HistoryLocation).
|
|
9965
|
-
See [NoneLocation](/ember/release/classes/NoneLocation).
|
|
9966
|
-
@property location
|
|
9967
|
-
@default 'hash'
|
|
9968
|
-
@see {Location}
|
|
9969
|
-
@public
|
|
9970
|
-
*/
|
|
9971
|
-
static {
|
|
9972
|
-
decorateFieldV2(this.prototype, "rootURL", [readOnly('_router.rootURL')]);
|
|
9973
|
-
}
|
|
9974
|
-
#rootURL = (initializeDeferredDecorator(this, "rootURL"), void 0);
|
|
9975
|
-
/**
|
|
9976
|
-
The `rootURL` property represents the URL of the root of
|
|
9977
|
-
the application, '/' by default.
|
|
9978
|
-
This prefix is assumed on all routes defined on this app.
|
|
9979
|
-
If you change the `rootURL` in your environment configuration
|
|
9980
|
-
like so:
|
|
9981
|
-
```config/environment.js
|
|
9982
|
-
'use strict';
|
|
9983
|
-
module.exports = function(environment) {
|
|
9984
|
-
let ENV = {
|
|
9985
|
-
modulePrefix: 'router-service',
|
|
9986
|
-
environment,
|
|
9987
|
-
rootURL: '/my-root',
|
|
9988
|
-
…
|
|
9989
|
-
}
|
|
9990
|
-
]
|
|
9991
|
-
```
|
|
9992
|
-
This property will return `/my-root`.
|
|
9993
|
-
@property rootURL
|
|
9994
|
-
@default '/'
|
|
9995
|
-
@public
|
|
9996
|
-
*/
|
|
9997
|
-
static {
|
|
9998
|
-
decorateFieldV2(this.prototype, "currentRoute", [readOnly('_router.currentRoute')]);
|
|
9999
|
-
}
|
|
10000
|
-
#currentRoute = (initializeDeferredDecorator(this, "currentRoute"), void 0);
|
|
10001
|
-
/**
|
|
10002
|
-
The `currentRoute` property contains metadata about the current leaf route.
|
|
10003
|
-
It returns a `RouteInfo` object that has information like the route name,
|
|
10004
|
-
params, query params and more.
|
|
10005
|
-
See [RouteInfo](/ember/release/classes/RouteInfo) for more info.
|
|
10006
|
-
This property is guaranteed to change whenever a route transition
|
|
10007
|
-
happens (even when that transition only changes parameters
|
|
10008
|
-
and doesn't change the active route).
|
|
10009
|
-
Usage example:
|
|
10010
|
-
```app/components/header.js
|
|
10011
|
-
import Component from '@glimmer/component';
|
|
10012
|
-
import { service } from '@ember/service';
|
|
10013
|
-
import { notEmpty } from '@ember/object/computed';
|
|
10014
|
-
export default class extends Component {
|
|
10015
|
-
@service router;
|
|
10016
|
-
@notEmpty('router.currentRoute.child') isChildRoute;
|
|
10017
|
-
});
|
|
10018
|
-
```
|
|
10019
|
-
@property currentRoute
|
|
10020
|
-
@type RouteInfo
|
|
10021
|
-
@public
|
|
10022
|
-
*/
|
|
10023
|
-
}
|
|
10024
|
-
|
|
10025
|
-
/**
|
|
10026
|
-
@module ember
|
|
10027
|
-
*/
|
|
10028
|
-
|
|
10029
|
-
class RoutingService extends Service {
|
|
10030
|
-
[ROUTER];
|
|
10031
|
-
get router() {
|
|
10032
|
-
let router = this[ROUTER];
|
|
10033
|
-
if (router !== undefined) {
|
|
10034
|
-
return router;
|
|
10035
|
-
}
|
|
10036
|
-
let owner = getOwner(this);
|
|
10037
|
-
(isDevelopingApp() && !(owner) && assert('RoutingService is unexpectedly missing an owner', owner));
|
|
10038
|
-
let _router = owner.lookup('router:main');
|
|
10039
|
-
(isDevelopingApp() && !(_router instanceof EmberRouter$1) && assert('ROUTING SERVICE BUG: Expected router to be an instance of EmberRouter', _router instanceof EmberRouter$1));
|
|
10040
|
-
_router.setupRouter();
|
|
10041
|
-
return this[ROUTER] = _router;
|
|
10042
|
-
}
|
|
10043
|
-
hasRoute(routeName) {
|
|
10044
|
-
return this.router.hasRoute(routeName);
|
|
10045
|
-
}
|
|
10046
|
-
transitionTo(routeName, models, queryParams, shouldReplace) {
|
|
10047
|
-
let transition = this.router._doTransition(routeName, models, queryParams);
|
|
10048
|
-
if (shouldReplace) {
|
|
10049
|
-
transition.method('replace');
|
|
10050
|
-
}
|
|
10051
|
-
return transition;
|
|
10052
|
-
}
|
|
10053
|
-
normalizeQueryParams(routeName, models, queryParams) {
|
|
10054
|
-
this.router._prepareQueryParams(routeName, models, queryParams);
|
|
10055
|
-
}
|
|
10056
|
-
_generateURL(routeName, models, queryParams) {
|
|
10057
|
-
let visibleQueryParams = {};
|
|
10058
|
-
if (queryParams) {
|
|
10059
|
-
Object.assign(visibleQueryParams, queryParams);
|
|
10060
|
-
this.normalizeQueryParams(routeName, models, visibleQueryParams);
|
|
10061
|
-
}
|
|
10062
|
-
return this.router.generate(routeName, ...models, {
|
|
10063
|
-
queryParams: visibleQueryParams
|
|
10064
|
-
});
|
|
10065
|
-
}
|
|
10066
|
-
generateURL(routeName, models, queryParams) {
|
|
10067
|
-
if (this.router._initialTransitionStarted) {
|
|
10068
|
-
return this._generateURL(routeName, models, queryParams);
|
|
10069
|
-
} else {
|
|
10070
|
-
// Swallow error when transition has not started.
|
|
10071
|
-
// When rendering in tests without visit(), we cannot infer the route context which <LinkTo/> needs be aware of
|
|
10072
|
-
try {
|
|
10073
|
-
return this._generateURL(routeName, models, queryParams);
|
|
10074
|
-
} catch (_e) {
|
|
10075
|
-
return;
|
|
10076
|
-
}
|
|
10077
|
-
}
|
|
10078
|
-
}
|
|
10079
|
-
isActiveForRoute(contexts, queryParams, routeName, routerState) {
|
|
10080
|
-
let handlers = this.router._routerMicrolib.recognizer.handlersFor(routeName);
|
|
10081
|
-
let leafName = handlers[handlers.length - 1].handler;
|
|
10082
|
-
let maximumContexts = numberOfContextsAcceptedByHandler(routeName, handlers);
|
|
10083
|
-
|
|
10084
|
-
// NOTE: any ugliness in the calculation of activeness is largely
|
|
10085
|
-
// due to the fact that we support automatic normalizing of
|
|
10086
|
-
// `resource` -> `resource.index`, even though there might be
|
|
10087
|
-
// dynamic segments / query params defined on `resource.index`
|
|
10088
|
-
// which complicates (and makes somewhat ambiguous) the calculation
|
|
10089
|
-
// of activeness for links that link to `resource` instead of
|
|
10090
|
-
// directly to `resource.index`.
|
|
10091
|
-
|
|
10092
|
-
// if we don't have enough contexts revert back to full route name
|
|
10093
|
-
// this is because the leaf route will use one of the contexts
|
|
10094
|
-
if (contexts.length > maximumContexts) {
|
|
10095
|
-
routeName = leafName;
|
|
10096
|
-
}
|
|
10097
|
-
return routerState.isActiveIntent(routeName, contexts, queryParams);
|
|
10098
|
-
}
|
|
10099
|
-
}
|
|
10100
|
-
RoutingService.reopen({
|
|
10101
|
-
targetState: readOnly('router.targetState'),
|
|
10102
|
-
currentState: readOnly('router.currentState'),
|
|
10103
|
-
currentRouteName: readOnly('router.currentRouteName'),
|
|
10104
|
-
currentPath: readOnly('router.currentPath')
|
|
10105
|
-
});
|
|
10106
|
-
function numberOfContextsAcceptedByHandler(handlerName, handlerInfos) {
|
|
10107
|
-
let req = 0;
|
|
10108
|
-
for (let i = 0; i < handlerInfos.length; i++) {
|
|
10109
|
-
req += handlerInfos[i].names.length;
|
|
10110
|
-
if (handlerInfos[i].handler === handlerName) {
|
|
10111
|
-
break;
|
|
10112
|
-
}
|
|
10113
|
-
}
|
|
10114
|
-
return req;
|
|
10115
|
-
}
|
|
10116
|
-
|
|
10117
|
-
export { getRenderState as A, getFullQueryParams as B, Component as C, defaultSerialize as D, Engine as E, hasDefaultSerialize as F, triggerEvent as G, Helper as H, Input as I, ROUTER as J, LinkTo as L, OutletView as O, RouterState as R, SafeString as S, Textarea as T, _resetRenderers as _, RoutingService as a, RootTemplate as b, htmlSafe as c, Renderer as d, escapeExpression as e, setupApplicationRegistry as f, buildInitializerMethod as g, helper$1 as h, isHTMLSafe as i, EmberRouter$1 as j, Route as k, RouterService as l, EmberEngineInstance as m, setModifierManager as n, on as o, prefixRouteNameArg as p, extractRouteArgs as q, renderSettled as r, setupEngineRegistry as s, getActiveTargetName as t, uniqueId$1 as u, stashParamNames as v, calculateCacheKey as w, normalizeControllerQueryParams as x, resemblesURL as y, shallowEqual as z };
|
|
5497
|
+
export { Component as C, Helper as H, Input as I, LinkTo as L, OutletView as O, RootTemplate as R, SafeString as S, Textarea as T, _resetRenderers as _, htmlSafe as a, Renderer as b, OutletTemplate as c, escapeExpression as e, helper$1 as h, isHTMLSafe as i, renderSettled as r, uniqueId$1 as u };
|