@angular/core 21.0.0-next.8 → 21.0.0-rc.0
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/fesm2022/_attribute-chunk.mjs +2 -14
- package/fesm2022/_attribute-chunk.mjs.map +1 -1
- package/fesm2022/_debug_node-chunk.mjs +15214 -28375
- package/fesm2022/_debug_node-chunk.mjs.map +1 -1
- package/fesm2022/_effect-chunk.mjs +402 -120
- package/fesm2022/_effect-chunk.mjs.map +1 -1
- package/fesm2022/_effect-chunk2.mjs +2951 -0
- package/fesm2022/_effect-chunk2.mjs.map +1 -0
- package/fesm2022/_not_found-chunk.mjs +18 -35
- package/fesm2022/_not_found-chunk.mjs.map +1 -1
- package/fesm2022/_resource-chunk.mjs +316 -563
- package/fesm2022/_resource-chunk.mjs.map +1 -1
- package/fesm2022/_untracked-chunk.mjs +96 -0
- package/fesm2022/_untracked-chunk.mjs.map +1 -0
- package/fesm2022/_weak_ref-chunk.mjs +2 -4
- package/fesm2022/_weak_ref-chunk.mjs.map +1 -1
- package/fesm2022/core.mjs +2466 -4309
- package/fesm2022/core.mjs.map +1 -1
- package/fesm2022/primitives-di.mjs +9 -9
- package/fesm2022/primitives-di.mjs.map +1 -1
- package/fesm2022/primitives-event-dispatch.mjs +626 -1460
- package/fesm2022/primitives-event-dispatch.mjs.map +1 -1
- package/fesm2022/primitives-signals.mjs +157 -191
- package/fesm2022/primitives-signals.mjs.map +1 -1
- package/fesm2022/rxjs-interop.mjs +208 -308
- package/fesm2022/rxjs-interop.mjs.map +1 -1
- package/fesm2022/testing.mjs +2305 -3164
- package/fesm2022/testing.mjs.map +1 -1
- package/package.json +8 -2
- package/resources/best-practices.md +56 -0
- package/schematics/bundles/add-bootstrap-context-to-server-main.cjs +7 -25
- package/schematics/bundles/application-config-core.cjs +8 -19
- package/schematics/bundles/{apply_import_manager-CBLmogDD.cjs → apply_import_manager-1Zs_gpB6.cjs} +4 -5
- package/schematics/bundles/bootstrap-options-migration.cjs +93 -132
- package/schematics/bundles/cleanup-unused-imports.cjs +9 -13
- package/schematics/bundles/common-to-standalone-migration.cjs +381 -0
- package/schematics/bundles/{compiler_host-T6xncpiw.cjs → compiler_host-DBwYMlTo.cjs} +10 -11
- package/schematics/bundles/control-flow-migration.cjs +29 -31
- package/schematics/bundles/{imports-DwPXlGFl.cjs → imports-DP72APSx.cjs} +1 -23
- package/schematics/bundles/{index-DWSaRJdz.cjs → index-B7I9sIUx.cjs} +36 -37
- package/schematics/bundles/inject-migration.cjs +9 -26
- package/schematics/bundles/leading_space-D9nQ8UQC.cjs +1 -1
- package/schematics/bundles/{migrate_ts_type_references-Cu-FR4L5.cjs → migrate_ts_type_references-UGIUl7En.cjs} +458 -24
- package/schematics/bundles/{ng_component_template-BkWiUuGG.cjs → ng_component_template-Dsuq1Lw7.cjs} +4 -5
- package/schematics/bundles/{ng_decorators-BI0uV7KI.cjs → ng_decorators-DSFlWYQY.cjs} +2 -2
- package/schematics/bundles/ngclass-to-class-migration.cjs +16 -19
- package/schematics/bundles/ngstyle-to-style-migration.cjs +15 -18
- package/schematics/bundles/nodes-B16H9JUd.cjs +1 -1
- package/schematics/bundles/output-migration.cjs +16 -19
- package/schematics/bundles/{parse_html-C97tKKp3.cjs → parse_html-8VLCL37B.cjs} +5 -5
- package/schematics/bundles/{project_paths-C6g3lqjX.cjs → project_paths-DvD50ouC.cjs} +14 -247
- package/schematics/bundles/project_tsconfig_paths-CDVxT6Ov.cjs +90 -0
- package/schematics/bundles/property_name-BBwFuqMe.cjs +1 -1
- package/schematics/bundles/route-lazy-loading.cjs +9 -25
- package/schematics/bundles/router-current-navigation.cjs +6 -17
- package/schematics/bundles/router-last-successful-navigation.cjs +6 -17
- package/schematics/bundles/router-testing-module-migration.cjs +7 -18
- package/schematics/bundles/self-closing-tags-migration.cjs +14 -17
- package/schematics/bundles/signal-input-migration.cjs +23 -26
- package/schematics/bundles/signal-queries-migration.cjs +22 -25
- package/schematics/bundles/signals.cjs +10 -13
- package/schematics/bundles/standalone-migration.cjs +22 -56
- package/schematics/bundles/symbol-BObKoqes.cjs +1 -1
- package/schematics/collection.json +6 -0
- package/schematics/migrations/common-to-standalone-migration/schema.json +14 -0
- package/types/_api-chunk.d.ts +1 -1
- package/types/_chrome_dev_tools_performance-chunk.d.ts +20 -12
- package/types/_discovery-chunk.d.ts +18 -14
- package/types/_effect-chunk.d.ts +1 -1
- package/types/_event_dispatcher-chunk.d.ts +1 -1
- package/types/_formatter-chunk.d.ts +4 -3
- package/types/_weak_ref-chunk.d.ts +1 -1
- package/types/core.d.ts +49 -100
- package/types/primitives-di.d.ts +1 -1
- package/types/primitives-event-dispatch.d.ts +1 -1
- package/types/primitives-signals.d.ts +2 -2
- package/types/rxjs-interop.d.ts +1 -1
- package/types/testing.d.ts +1 -1
- package/fesm2022/_root_effect_scheduler-chunk.mjs +0 -4630
- package/fesm2022/_root_effect_scheduler-chunk.mjs.map +0 -1
- package/fesm2022/_signal-chunk.mjs +0 -581
- package/fesm2022/_signal-chunk.mjs.map +0 -1
- package/schematics/bundles/index-BnmACOsq.cjs +0 -22319
- package/schematics/bundles/project_tsconfig_paths-CdhVNYMk.cjs +0 -51583
|
@@ -1,4630 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Angular v21.0.0-next.8
|
|
3
|
-
* (c) 2010-2025 Google LLC. https://angular.dev/
|
|
4
|
-
* License: MIT
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { getActiveConsumer, setActiveConsumer as setActiveConsumer$1, SIGNAL, createSignal } from './_signal-chunk.mjs';
|
|
8
|
-
import { isNotFound, getCurrentInjector, setCurrentInjector } from './_not_found-chunk.mjs';
|
|
9
|
-
import { setActiveConsumer } from '@angular/core/primitives/signals';
|
|
10
|
-
import { isNotFound as isNotFound$1 } from '@angular/core/primitives/di';
|
|
11
|
-
import { BehaviorSubject, Observable, Subject, Subscription } from 'rxjs';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @description Represents the version of Angular
|
|
15
|
-
*
|
|
16
|
-
* @publicApi
|
|
17
|
-
*/
|
|
18
|
-
class Version {
|
|
19
|
-
full;
|
|
20
|
-
major;
|
|
21
|
-
minor;
|
|
22
|
-
patch;
|
|
23
|
-
constructor(full) {
|
|
24
|
-
this.full = full;
|
|
25
|
-
const parts = full.split('.');
|
|
26
|
-
this.major = parts[0];
|
|
27
|
-
this.minor = parts[1];
|
|
28
|
-
this.patch = parts.slice(2).join('.');
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* @publicApi
|
|
33
|
-
*/
|
|
34
|
-
const VERSION = /* @__PURE__ */ new Version('21.0.0-next.8');
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Base URL for the error details page.
|
|
38
|
-
*
|
|
39
|
-
* Keep this constant in sync across:
|
|
40
|
-
* - packages/compiler-cli/src/ngtsc/diagnostics/src/error_details_base_url.ts
|
|
41
|
-
* - packages/core/src/error_details_base_url.ts
|
|
42
|
-
*/
|
|
43
|
-
const ERROR_DETAILS_PAGE_BASE_URL = (() => {
|
|
44
|
-
const versionSubDomain = VERSION.major !== '0' ? `v${VERSION.major}.` : '';
|
|
45
|
-
return `https://${versionSubDomain}angular.dev/errors`;
|
|
46
|
-
})();
|
|
47
|
-
/**
|
|
48
|
-
* URL for the XSS security documentation.
|
|
49
|
-
*/
|
|
50
|
-
const XSS_SECURITY_URL = 'https://angular.dev/best-practices/security#preventing-cross-site-scripting-xss';
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Class that represents a runtime error.
|
|
54
|
-
* Formats and outputs the error message in a consistent way.
|
|
55
|
-
*
|
|
56
|
-
* Example:
|
|
57
|
-
* ```ts
|
|
58
|
-
* throw new RuntimeError(
|
|
59
|
-
* RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED,
|
|
60
|
-
* ngDevMode && 'Injector has already been destroyed.');
|
|
61
|
-
* ```
|
|
62
|
-
*
|
|
63
|
-
* Note: the `message` argument contains a descriptive error message as a string in development
|
|
64
|
-
* mode (when the `ngDevMode` is defined). In production mode (after tree-shaking pass), the
|
|
65
|
-
* `message` argument becomes `false`, thus we account for it in the typings and the runtime
|
|
66
|
-
* logic.
|
|
67
|
-
*/
|
|
68
|
-
class RuntimeError extends Error {
|
|
69
|
-
code;
|
|
70
|
-
constructor(code, message) {
|
|
71
|
-
super(formatRuntimeError(code, message));
|
|
72
|
-
this.code = code;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
function formatRuntimeErrorCode(code) {
|
|
76
|
-
// Error code might be a negative number, which is a special marker that instructs the logic to
|
|
77
|
-
// generate a link to the error details page on angular.io.
|
|
78
|
-
// We also prepend `0` to non-compile-time errors.
|
|
79
|
-
return `NG0${Math.abs(code)}`;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Called to format a runtime error.
|
|
83
|
-
* See additional info on the `message` argument type in the `RuntimeError` class description.
|
|
84
|
-
*/
|
|
85
|
-
function formatRuntimeError(code, message) {
|
|
86
|
-
const fullCode = formatRuntimeErrorCode(code);
|
|
87
|
-
let errorMessage = `${fullCode}${message ? ': ' + message : ''}`;
|
|
88
|
-
if (ngDevMode && code < 0) {
|
|
89
|
-
const addPeriodSeparator = !errorMessage.match(/[.,;!?\n]$/);
|
|
90
|
-
const separator = addPeriodSeparator ? '.' : '';
|
|
91
|
-
errorMessage = `${errorMessage}${separator} Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/${fullCode}`;
|
|
92
|
-
}
|
|
93
|
-
return errorMessage;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const _global = globalThis;
|
|
97
|
-
|
|
98
|
-
function ngDevModeResetPerfCounters() {
|
|
99
|
-
const locationString = typeof location !== 'undefined' ? location.toString() : '';
|
|
100
|
-
const newCounters = {
|
|
101
|
-
hydratedNodes: 0,
|
|
102
|
-
hydratedComponents: 0,
|
|
103
|
-
dehydratedViewsRemoved: 0,
|
|
104
|
-
dehydratedViewsCleanupRuns: 0,
|
|
105
|
-
componentsSkippedHydration: 0,
|
|
106
|
-
deferBlocksWithIncrementalHydration: 0,
|
|
107
|
-
};
|
|
108
|
-
// Make sure to refer to ngDevMode as ['ngDevMode'] for closure.
|
|
109
|
-
const allowNgDevModeTrue = locationString.indexOf('ngDevMode=false') === -1;
|
|
110
|
-
if (!allowNgDevModeTrue) {
|
|
111
|
-
_global['ngDevMode'] = false;
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
if (typeof _global['ngDevMode'] !== 'object') {
|
|
115
|
-
_global['ngDevMode'] = {};
|
|
116
|
-
}
|
|
117
|
-
Object.assign(_global['ngDevMode'], newCounters);
|
|
118
|
-
}
|
|
119
|
-
return newCounters;
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* This function checks to see if the `ngDevMode` has been set. If yes,
|
|
123
|
-
* then we honor it, otherwise we default to dev mode with additional checks.
|
|
124
|
-
*
|
|
125
|
-
* The idea is that unless we are doing production build where we explicitly
|
|
126
|
-
* set `ngDevMode == false` we should be helping the developer by providing
|
|
127
|
-
* as much early warning and errors as possible.
|
|
128
|
-
*
|
|
129
|
-
* `ɵɵdefineComponent` is guaranteed to have been called before any component template functions
|
|
130
|
-
* (and thus Ivy instructions), so a single initialization there is sufficient to ensure ngDevMode
|
|
131
|
-
* is defined for the entire instruction set.
|
|
132
|
-
*
|
|
133
|
-
* When checking `ngDevMode` on toplevel, always init it before referencing it
|
|
134
|
-
* (e.g. `((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode())`), otherwise you can
|
|
135
|
-
* get a `ReferenceError` like in https://github.com/angular/angular/issues/31595.
|
|
136
|
-
*
|
|
137
|
-
* Details on possible values for `ngDevMode` can be found on its docstring.
|
|
138
|
-
*/
|
|
139
|
-
function initNgDevMode() {
|
|
140
|
-
// The below checks are to ensure that calling `initNgDevMode` multiple times does not
|
|
141
|
-
// reset the counters.
|
|
142
|
-
// If the `ngDevMode` is not an object, then it means we have not created the perf counters
|
|
143
|
-
// yet.
|
|
144
|
-
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
|
145
|
-
if (typeof ngDevMode !== 'object' || Object.keys(ngDevMode).length === 0) {
|
|
146
|
-
ngDevModeResetPerfCounters();
|
|
147
|
-
}
|
|
148
|
-
return typeof ngDevMode !== 'undefined' && !!ngDevMode;
|
|
149
|
-
}
|
|
150
|
-
return false;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function getClosureSafeProperty(objWithPropertyToExtract) {
|
|
154
|
-
for (let key in objWithPropertyToExtract) {
|
|
155
|
-
if (objWithPropertyToExtract[key] === getClosureSafeProperty) {
|
|
156
|
-
return key;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
// Cannot change it to `RuntimeError` because the `util` target cannot
|
|
160
|
-
// circularly depend on the `core` target.
|
|
161
|
-
throw Error(typeof ngDevMode !== 'undefined' && ngDevMode
|
|
162
|
-
? 'Could not find renamed property on target object.'
|
|
163
|
-
: '');
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Sets properties on a target object from a source object, but only if
|
|
167
|
-
* the property doesn't already exist on the target object.
|
|
168
|
-
* @param target The target to set properties on
|
|
169
|
-
* @param source The source of the property keys and values to set
|
|
170
|
-
*/
|
|
171
|
-
function fillProperties(target, source) {
|
|
172
|
-
for (const key in source) {
|
|
173
|
-
if (source.hasOwnProperty(key) && !target.hasOwnProperty(key)) {
|
|
174
|
-
target[key] = source[key];
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function stringify(token) {
|
|
180
|
-
if (typeof token === 'string') {
|
|
181
|
-
return token;
|
|
182
|
-
}
|
|
183
|
-
if (Array.isArray(token)) {
|
|
184
|
-
return `[${token.map(stringify).join(', ')}]`;
|
|
185
|
-
}
|
|
186
|
-
if (token == null) {
|
|
187
|
-
return '' + token;
|
|
188
|
-
}
|
|
189
|
-
const name = token.overriddenName || token.name;
|
|
190
|
-
if (name) {
|
|
191
|
-
return `${name}`;
|
|
192
|
-
}
|
|
193
|
-
const result = token.toString();
|
|
194
|
-
if (result == null) {
|
|
195
|
-
return '' + result;
|
|
196
|
-
}
|
|
197
|
-
const newLineIndex = result.indexOf('\n');
|
|
198
|
-
return newLineIndex >= 0 ? result.slice(0, newLineIndex) : result;
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Concatenates two strings with separator, allocating new strings only when necessary.
|
|
202
|
-
*
|
|
203
|
-
* @param before before string.
|
|
204
|
-
* @param separator separator string.
|
|
205
|
-
* @param after after string.
|
|
206
|
-
* @returns concatenated string.
|
|
207
|
-
*/
|
|
208
|
-
function concatStringsWithSpace(before, after) {
|
|
209
|
-
if (!before)
|
|
210
|
-
return after || '';
|
|
211
|
-
if (!after)
|
|
212
|
-
return before;
|
|
213
|
-
return `${before} ${after}`;
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Ellipses the string in the middle when longer than the max length
|
|
217
|
-
*
|
|
218
|
-
* @param string
|
|
219
|
-
* @param maxLength of the output string
|
|
220
|
-
* @returns ellipsed string with ... in the middle
|
|
221
|
-
*/
|
|
222
|
-
function truncateMiddle(str, maxLength = 100) {
|
|
223
|
-
if (!str || maxLength < 1 || str.length <= maxLength)
|
|
224
|
-
return str;
|
|
225
|
-
if (maxLength == 1)
|
|
226
|
-
return str.substring(0, 1) + '...';
|
|
227
|
-
const halfLimit = Math.round(maxLength / 2);
|
|
228
|
-
return str.substring(0, halfLimit) + '...' + str.substring(str.length - halfLimit);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const __forward_ref__ = getClosureSafeProperty({ __forward_ref__: getClosureSafeProperty });
|
|
232
|
-
/**
|
|
233
|
-
* Allows to refer to references which are not yet defined.
|
|
234
|
-
*
|
|
235
|
-
* For instance, `forwardRef` is used when the `token` which we need to refer to for the purposes of
|
|
236
|
-
* DI is declared, but not yet defined. It is also used when the `token` which we use when creating
|
|
237
|
-
* a query is not yet defined.
|
|
238
|
-
*
|
|
239
|
-
* `forwardRef` is also used to break circularities in standalone components imports.
|
|
240
|
-
*
|
|
241
|
-
* @usageNotes
|
|
242
|
-
* ### Circular dependency example
|
|
243
|
-
* {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref'}
|
|
244
|
-
*
|
|
245
|
-
* ### Circular standalone reference import example
|
|
246
|
-
* ```angular-ts
|
|
247
|
-
* @Component({
|
|
248
|
-
* imports: [ChildComponent],
|
|
249
|
-
* selector: 'app-parent',
|
|
250
|
-
* template: `<app-child [hideParent]="hideParent()"></app-child>`,
|
|
251
|
-
* })
|
|
252
|
-
* export class ParentComponent {
|
|
253
|
-
* hideParent = input.required<boolean>();
|
|
254
|
-
* }
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
* @Component({
|
|
258
|
-
* imports: [forwardRef(() => ParentComponent)],
|
|
259
|
-
* selector: 'app-child',
|
|
260
|
-
* template: `
|
|
261
|
-
* @if(!hideParent() {
|
|
262
|
-
* <app-parent/>
|
|
263
|
-
* }
|
|
264
|
-
* `,
|
|
265
|
-
* })
|
|
266
|
-
* export class ChildComponent {
|
|
267
|
-
* hideParent = input.required<boolean>();
|
|
268
|
-
* }
|
|
269
|
-
* ```
|
|
270
|
-
*
|
|
271
|
-
* @publicApi
|
|
272
|
-
*/
|
|
273
|
-
function forwardRef(forwardRefFn) {
|
|
274
|
-
forwardRefFn.__forward_ref__ = forwardRef;
|
|
275
|
-
forwardRefFn.toString = function () {
|
|
276
|
-
return stringify(this());
|
|
277
|
-
};
|
|
278
|
-
return forwardRefFn;
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Lazily retrieves the reference value from a forwardRef.
|
|
282
|
-
*
|
|
283
|
-
* Acts as the identity function when given a non-forward-ref value.
|
|
284
|
-
*
|
|
285
|
-
* @usageNotes
|
|
286
|
-
* ### Example
|
|
287
|
-
*
|
|
288
|
-
* {@example core/di/ts/forward_ref/forward_ref_spec.ts region='resolve_forward_ref'}
|
|
289
|
-
*
|
|
290
|
-
* @see {@link forwardRef}
|
|
291
|
-
* @publicApi
|
|
292
|
-
*/
|
|
293
|
-
function resolveForwardRef(type) {
|
|
294
|
-
return isForwardRef(type) ? type() : type;
|
|
295
|
-
}
|
|
296
|
-
/** Checks whether a function is wrapped by a `forwardRef`. */
|
|
297
|
-
function isForwardRef(fn) {
|
|
298
|
-
return (typeof fn === 'function' &&
|
|
299
|
-
fn.hasOwnProperty(__forward_ref__) &&
|
|
300
|
-
fn.__forward_ref__ === forwardRef);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// The functions in this file verify that the assumptions we are making
|
|
304
|
-
// about state in an instruction are correct before implementing any logic.
|
|
305
|
-
// They are meant only to be called in dev mode as sanity checks.
|
|
306
|
-
function assertNumber(actual, msg) {
|
|
307
|
-
if (!(typeof actual === 'number')) {
|
|
308
|
-
throwError(msg, typeof actual, 'number', '===');
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
function assertNumberInRange(actual, minInclusive, maxInclusive) {
|
|
312
|
-
assertNumber(actual, 'Expected a number');
|
|
313
|
-
assertLessThanOrEqual(actual, maxInclusive, 'Expected number to be less than or equal to');
|
|
314
|
-
assertGreaterThanOrEqual(actual, minInclusive, 'Expected number to be greater than or equal to');
|
|
315
|
-
}
|
|
316
|
-
function assertString(actual, msg) {
|
|
317
|
-
if (!(typeof actual === 'string')) {
|
|
318
|
-
throwError(msg, actual === null ? 'null' : typeof actual, 'string', '===');
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
function assertFunction(actual, msg) {
|
|
322
|
-
if (!(typeof actual === 'function')) {
|
|
323
|
-
throwError(msg, actual === null ? 'null' : typeof actual, 'function', '===');
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
function assertEqual(actual, expected, msg) {
|
|
327
|
-
if (!(actual == expected)) {
|
|
328
|
-
throwError(msg, actual, expected, '==');
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
function assertNotEqual(actual, expected, msg) {
|
|
332
|
-
if (!(actual != expected)) {
|
|
333
|
-
throwError(msg, actual, expected, '!=');
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
function assertSame(actual, expected, msg) {
|
|
337
|
-
if (!(actual === expected)) {
|
|
338
|
-
throwError(msg, actual, expected, '===');
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
function assertNotSame(actual, expected, msg) {
|
|
342
|
-
if (!(actual !== expected)) {
|
|
343
|
-
throwError(msg, actual, expected, '!==');
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
function assertLessThan(actual, expected, msg) {
|
|
347
|
-
if (!(actual < expected)) {
|
|
348
|
-
throwError(msg, actual, expected, '<');
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
function assertLessThanOrEqual(actual, expected, msg) {
|
|
352
|
-
if (!(actual <= expected)) {
|
|
353
|
-
throwError(msg, actual, expected, '<=');
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
function assertGreaterThan(actual, expected, msg) {
|
|
357
|
-
if (!(actual > expected)) {
|
|
358
|
-
throwError(msg, actual, expected, '>');
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
function assertGreaterThanOrEqual(actual, expected, msg) {
|
|
362
|
-
if (!(actual >= expected)) {
|
|
363
|
-
throwError(msg, actual, expected, '>=');
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
function assertNotDefined(actual, msg) {
|
|
367
|
-
if (actual != null) {
|
|
368
|
-
throwError(msg, actual, null, '==');
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
function assertDefined(actual, msg) {
|
|
372
|
-
if (actual == null) {
|
|
373
|
-
throwError(msg, actual, null, '!=');
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
function throwError(msg, actual, expected, comparison) {
|
|
377
|
-
throw new Error(`ASSERTION ERROR: ${msg}` +
|
|
378
|
-
(comparison == null ? '' : ` [Expected=> ${expected} ${comparison} ${actual} <=Actual]`));
|
|
379
|
-
}
|
|
380
|
-
function assertDomNode(node) {
|
|
381
|
-
if (!(node instanceof Node)) {
|
|
382
|
-
throwError(`The provided value must be an instance of a DOM Node but got ${stringify(node)}`);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
function assertElement(node) {
|
|
386
|
-
if (!(node instanceof Element)) {
|
|
387
|
-
throwError(`The provided value must be an element but got ${stringify(node)}`);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
function assertIndexInRange(arr, index) {
|
|
391
|
-
assertDefined(arr, 'Array must be defined.');
|
|
392
|
-
const maxLen = arr.length;
|
|
393
|
-
if (index < 0 || index >= maxLen) {
|
|
394
|
-
throwError(`Index expected to be less than ${maxLen} but got ${index}`);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
function assertOneOf(value, ...validValues) {
|
|
398
|
-
if (validValues.indexOf(value) !== -1)
|
|
399
|
-
return true;
|
|
400
|
-
throwError(`Expected value to be one of ${JSON.stringify(validValues)} but was ${JSON.stringify(value)}.`);
|
|
401
|
-
}
|
|
402
|
-
function assertNotReactive(fn) {
|
|
403
|
-
if (getActiveConsumer() !== null) {
|
|
404
|
-
throwError(`${fn}() should never be called in a reactive context.`);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* Construct an injectable definition which defines how a token will be constructed by the DI
|
|
410
|
-
* system, and in which injectors (if any) it will be available.
|
|
411
|
-
*
|
|
412
|
-
* This should be assigned to a static `ɵprov` field on a type, which will then be an
|
|
413
|
-
* `InjectableType`.
|
|
414
|
-
*
|
|
415
|
-
* Options:
|
|
416
|
-
* * `providedIn` determines which injectors will include the injectable, by either associating it
|
|
417
|
-
* with an `@NgModule` or other `InjectorType`, or by specifying that this injectable should be
|
|
418
|
-
* provided in the `'root'` injector, which will be the application-level injector in most apps.
|
|
419
|
-
* * `factory` gives the zero argument function which will create an instance of the injectable.
|
|
420
|
-
* The factory can call [`inject`](api/core/inject) to access the `Injector` and request injection
|
|
421
|
-
* of dependencies.
|
|
422
|
-
*
|
|
423
|
-
* @codeGenApi
|
|
424
|
-
* @publicApi This instruction has been emitted by ViewEngine for some time and is deployed to npm.
|
|
425
|
-
*/
|
|
426
|
-
function ɵɵdefineInjectable(opts) {
|
|
427
|
-
return {
|
|
428
|
-
token: opts.token,
|
|
429
|
-
providedIn: opts.providedIn || null,
|
|
430
|
-
factory: opts.factory,
|
|
431
|
-
value: undefined,
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Construct an `InjectorDef` which configures an injector.
|
|
436
|
-
*
|
|
437
|
-
* This should be assigned to a static injector def (`ɵinj`) field on a type, which will then be an
|
|
438
|
-
* `InjectorType`.
|
|
439
|
-
*
|
|
440
|
-
* Options:
|
|
441
|
-
*
|
|
442
|
-
* * `providers`: an optional array of providers to add to the injector. Each provider must
|
|
443
|
-
* either have a factory or point to a type which has a `ɵprov` static property (the
|
|
444
|
-
* type must be an `InjectableType`).
|
|
445
|
-
* * `imports`: an optional array of imports of other `InjectorType`s or `InjectorTypeWithModule`s
|
|
446
|
-
* whose providers will also be added to the injector. Locally provided types will override
|
|
447
|
-
* providers from imports.
|
|
448
|
-
*
|
|
449
|
-
* @codeGenApi
|
|
450
|
-
*/
|
|
451
|
-
function ɵɵdefineInjector(options) {
|
|
452
|
-
return { providers: options.providers || [], imports: options.imports || [] };
|
|
453
|
-
}
|
|
454
|
-
/**
|
|
455
|
-
* Read the injectable def (`ɵprov`) for `type` in a way which is immune to accidentally reading
|
|
456
|
-
* inherited value.
|
|
457
|
-
*
|
|
458
|
-
* @param type A type which may have its own (non-inherited) `ɵprov`.
|
|
459
|
-
*/
|
|
460
|
-
function getInjectableDef(type) {
|
|
461
|
-
return getOwnDefinition(type, NG_PROV_DEF);
|
|
462
|
-
}
|
|
463
|
-
function isInjectable(type) {
|
|
464
|
-
return getInjectableDef(type) !== null;
|
|
465
|
-
}
|
|
466
|
-
/**
|
|
467
|
-
* Return definition only if it is defined directly on `type` and is not inherited from a base
|
|
468
|
-
* class of `type`.
|
|
469
|
-
*/
|
|
470
|
-
function getOwnDefinition(type, field) {
|
|
471
|
-
// if the ɵprov prop exist but is undefined we still want to return null
|
|
472
|
-
return (type.hasOwnProperty(field) && type[field]) || null;
|
|
473
|
-
}
|
|
474
|
-
/**
|
|
475
|
-
* Read the injectable def (`ɵprov`) for `type` or read the `ɵprov` from one of its ancestors.
|
|
476
|
-
*
|
|
477
|
-
* @param type A type which may have `ɵprov`, via inheritance.
|
|
478
|
-
*
|
|
479
|
-
* @deprecated Will be removed in a future version of Angular, where an error will occur in the
|
|
480
|
-
* scenario if we find the `ɵprov` on an ancestor only.
|
|
481
|
-
*/
|
|
482
|
-
function getInheritedInjectableDef(type) {
|
|
483
|
-
// if the ɵprov prop exist but is undefined we still want to return null
|
|
484
|
-
const def = type?.[NG_PROV_DEF] ?? null;
|
|
485
|
-
if (def) {
|
|
486
|
-
ngDevMode &&
|
|
487
|
-
console.warn(`DEPRECATED: DI is instantiating a token "${type.name}" that inherits its @Injectable decorator but does not provide one itself.\n` +
|
|
488
|
-
`This will become an error in a future version of Angular. Please add @Injectable() to the "${type.name}" class.`);
|
|
489
|
-
return def;
|
|
490
|
-
}
|
|
491
|
-
else {
|
|
492
|
-
return null;
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
/**
|
|
496
|
-
* Read the injector def type in a way which is immune to accidentally reading inherited value.
|
|
497
|
-
*
|
|
498
|
-
* @param type type which may have an injector def (`ɵinj`)
|
|
499
|
-
*/
|
|
500
|
-
function getInjectorDef(type) {
|
|
501
|
-
return type && type.hasOwnProperty(NG_INJ_DEF) ? type[NG_INJ_DEF] : null;
|
|
502
|
-
}
|
|
503
|
-
const NG_PROV_DEF = getClosureSafeProperty({ ɵprov: getClosureSafeProperty });
|
|
504
|
-
const NG_INJ_DEF = getClosureSafeProperty({ ɵinj: getClosureSafeProperty });
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* Creates a token that can be used in a DI Provider.
|
|
508
|
-
*
|
|
509
|
-
* Use an `InjectionToken` whenever the type you are injecting is not reified (does not have a
|
|
510
|
-
* runtime representation) such as when injecting an interface, callable type, array or
|
|
511
|
-
* parameterized type.
|
|
512
|
-
*
|
|
513
|
-
* `InjectionToken` is parameterized on `T` which is the type of object which will be returned by
|
|
514
|
-
* the `Injector`. This provides an additional level of type safety.
|
|
515
|
-
*
|
|
516
|
-
* <div class="docs-alert docs-alert-helpful">
|
|
517
|
-
*
|
|
518
|
-
* **Important Note**: Ensure that you use the same instance of the `InjectionToken` in both the
|
|
519
|
-
* provider and the injection call. Creating a new instance of `InjectionToken` in different places,
|
|
520
|
-
* even with the same description, will be treated as different tokens by Angular's DI system,
|
|
521
|
-
* leading to a `NullInjectorError`.
|
|
522
|
-
*
|
|
523
|
-
* </div>
|
|
524
|
-
*
|
|
525
|
-
* {@example injection-token/src/main.ts region='InjectionToken'}
|
|
526
|
-
*
|
|
527
|
-
* When creating an `InjectionToken`, you can optionally specify a factory function which returns
|
|
528
|
-
* (possibly by creating) a default value of the parameterized type `T`. This sets up the
|
|
529
|
-
* `InjectionToken` using this factory as a provider as if it was defined explicitly in the
|
|
530
|
-
* application's root injector. If the factory function, which takes zero arguments, needs to inject
|
|
531
|
-
* dependencies, it can do so using the [`inject`](api/core/inject) function.
|
|
532
|
-
* As you can see in the Tree-shakable InjectionToken example below.
|
|
533
|
-
*
|
|
534
|
-
* Additionally, if a `factory` is specified you can also specify the `providedIn` option, which
|
|
535
|
-
* overrides the above behavior and marks the token as belonging to a particular `@NgModule` (note:
|
|
536
|
-
* this option is now deprecated). As mentioned above, `'root'` is the default value for
|
|
537
|
-
* `providedIn`.
|
|
538
|
-
*
|
|
539
|
-
* The `providedIn: NgModule` and `providedIn: 'any'` options are deprecated.
|
|
540
|
-
*
|
|
541
|
-
* @usageNotes
|
|
542
|
-
* ### Basic Examples
|
|
543
|
-
*
|
|
544
|
-
* ### Plain InjectionToken
|
|
545
|
-
*
|
|
546
|
-
* {@example core/di/ts/injector_spec.ts region='InjectionToken'}
|
|
547
|
-
*
|
|
548
|
-
* ### Tree-shakable InjectionToken
|
|
549
|
-
*
|
|
550
|
-
* {@example core/di/ts/injector_spec.ts region='ShakableInjectionToken'}
|
|
551
|
-
*
|
|
552
|
-
* @publicApi
|
|
553
|
-
*/
|
|
554
|
-
class InjectionToken {
|
|
555
|
-
_desc;
|
|
556
|
-
/** @internal */
|
|
557
|
-
ngMetadataName = 'InjectionToken';
|
|
558
|
-
ɵprov;
|
|
559
|
-
/**
|
|
560
|
-
* @param _desc Description for the token,
|
|
561
|
-
* used only for debugging purposes,
|
|
562
|
-
* it should but does not need to be unique
|
|
563
|
-
* @param options Options for the token's usage, as described above
|
|
564
|
-
*/
|
|
565
|
-
constructor(_desc, options) {
|
|
566
|
-
this._desc = _desc;
|
|
567
|
-
this.ɵprov = undefined;
|
|
568
|
-
if (typeof options == 'number') {
|
|
569
|
-
(typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
570
|
-
assertLessThan(options, 0, 'Only negative numbers are supported here');
|
|
571
|
-
// This is a special hack to assign __NG_ELEMENT_ID__ to this instance.
|
|
572
|
-
// See `InjectorMarkers`
|
|
573
|
-
this.__NG_ELEMENT_ID__ = options;
|
|
574
|
-
}
|
|
575
|
-
else if (options !== undefined) {
|
|
576
|
-
this.ɵprov = ɵɵdefineInjectable({
|
|
577
|
-
token: this,
|
|
578
|
-
providedIn: options.providedIn || 'root',
|
|
579
|
-
factory: options.factory,
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
/**
|
|
584
|
-
* @internal
|
|
585
|
-
*/
|
|
586
|
-
get multi() {
|
|
587
|
-
return this;
|
|
588
|
-
}
|
|
589
|
-
toString() {
|
|
590
|
-
return `InjectionToken ${this._desc}`;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
let _injectorProfilerContext;
|
|
595
|
-
function getInjectorProfilerContext() {
|
|
596
|
-
!ngDevMode && throwError('getInjectorProfilerContext should never be called in production mode');
|
|
597
|
-
return _injectorProfilerContext;
|
|
598
|
-
}
|
|
599
|
-
function setInjectorProfilerContext(context) {
|
|
600
|
-
!ngDevMode && throwError('setInjectorProfilerContext should never be called in production mode');
|
|
601
|
-
const previous = _injectorProfilerContext;
|
|
602
|
-
_injectorProfilerContext = context;
|
|
603
|
-
return previous;
|
|
604
|
-
}
|
|
605
|
-
const injectorProfilerCallbacks = [];
|
|
606
|
-
const NOOP_PROFILER_REMOVAL = () => { };
|
|
607
|
-
function removeProfiler(profiler) {
|
|
608
|
-
const profilerIdx = injectorProfilerCallbacks.indexOf(profiler);
|
|
609
|
-
if (profilerIdx !== -1) {
|
|
610
|
-
injectorProfilerCallbacks.splice(profilerIdx, 1);
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
/**
|
|
614
|
-
* Adds a callback function which will be invoked during certain DI events within the
|
|
615
|
-
* runtime (for example: injecting services, creating injectable instances, configuring providers).
|
|
616
|
-
* Multiple profiler callbacks can be set: in this case profiling events are
|
|
617
|
-
* reported to every registered callback.
|
|
618
|
-
*
|
|
619
|
-
* Warning: this function is *INTERNAL* and should not be relied upon in application's code.
|
|
620
|
-
* The contract of the function might be changed in any release and/or the function can be removed
|
|
621
|
-
* completely.
|
|
622
|
-
*
|
|
623
|
-
* @param profiler function provided by the caller or null value to disable profiling.
|
|
624
|
-
* @returns a cleanup function that, when invoked, removes a given profiler callback.
|
|
625
|
-
*/
|
|
626
|
-
function setInjectorProfiler(injectorProfiler) {
|
|
627
|
-
!ngDevMode && throwError('setInjectorProfiler should never be called in production mode');
|
|
628
|
-
if (injectorProfiler !== null) {
|
|
629
|
-
if (!injectorProfilerCallbacks.includes(injectorProfiler)) {
|
|
630
|
-
injectorProfilerCallbacks.push(injectorProfiler);
|
|
631
|
-
}
|
|
632
|
-
return () => removeProfiler(injectorProfiler);
|
|
633
|
-
}
|
|
634
|
-
else {
|
|
635
|
-
injectorProfilerCallbacks.length = 0;
|
|
636
|
-
return NOOP_PROFILER_REMOVAL;
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
/**
|
|
640
|
-
* Injector profiler function which emits on DI events executed by the runtime.
|
|
641
|
-
*
|
|
642
|
-
* @param event InjectorProfilerEvent corresponding to the DI event being emitted
|
|
643
|
-
*/
|
|
644
|
-
function injectorProfiler(event) {
|
|
645
|
-
!ngDevMode && throwError('Injector profiler should never be called in production mode');
|
|
646
|
-
for (let i = 0; i < injectorProfilerCallbacks.length; i++) {
|
|
647
|
-
const injectorProfilerCallback = injectorProfilerCallbacks[i];
|
|
648
|
-
injectorProfilerCallback(event);
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
/**
|
|
652
|
-
* Emits an InjectorProfilerEventType.ProviderConfigured to the injector profiler. The data in the
|
|
653
|
-
* emitted event includes the raw provider, as well as the token that provider is providing.
|
|
654
|
-
*
|
|
655
|
-
* @param eventProvider A provider object
|
|
656
|
-
*/
|
|
657
|
-
function emitProviderConfiguredEvent(eventProvider, isViewProvider = false) {
|
|
658
|
-
!ngDevMode && throwError('Injector profiler should never be called in production mode');
|
|
659
|
-
let token;
|
|
660
|
-
// if the provider is a TypeProvider (typeof provider is function) then the token is the
|
|
661
|
-
// provider itself
|
|
662
|
-
if (typeof eventProvider === 'function') {
|
|
663
|
-
token = eventProvider;
|
|
664
|
-
}
|
|
665
|
-
// if the provider is an injection token, then the token is the injection token.
|
|
666
|
-
else if (eventProvider instanceof InjectionToken) {
|
|
667
|
-
token = eventProvider;
|
|
668
|
-
}
|
|
669
|
-
// in all other cases we can access the token via the `provide` property of the provider
|
|
670
|
-
else {
|
|
671
|
-
token = resolveForwardRef(eventProvider.provide);
|
|
672
|
-
}
|
|
673
|
-
let provider = eventProvider;
|
|
674
|
-
// Injection tokens may define their own default provider which gets attached to the token itself
|
|
675
|
-
// as `ɵprov`. In this case, we want to emit the provider that is attached to the token, not the
|
|
676
|
-
// token itself.
|
|
677
|
-
if (eventProvider instanceof InjectionToken) {
|
|
678
|
-
provider = eventProvider.ɵprov || eventProvider;
|
|
679
|
-
}
|
|
680
|
-
injectorProfiler({
|
|
681
|
-
type: 2 /* InjectorProfilerEventType.ProviderConfigured */,
|
|
682
|
-
context: getInjectorProfilerContext(),
|
|
683
|
-
providerRecord: { token, provider, isViewProvider },
|
|
684
|
-
});
|
|
685
|
-
}
|
|
686
|
-
/**
|
|
687
|
-
* Emits an event to the injector profiler when an instance corresponding to a given token is about to be created be an injector. Note that
|
|
688
|
-
* the injector associated with this emission can be accessed by using getDebugInjectContext()
|
|
689
|
-
*
|
|
690
|
-
* @param instance an object created by an injector
|
|
691
|
-
*/
|
|
692
|
-
function emitInjectorToCreateInstanceEvent(token) {
|
|
693
|
-
!ngDevMode && throwError('Injector profiler should never be called in production mode');
|
|
694
|
-
injectorProfiler({
|
|
695
|
-
type: 4 /* InjectorProfilerEventType.InjectorToCreateInstanceEvent */,
|
|
696
|
-
context: getInjectorProfilerContext(),
|
|
697
|
-
token: token,
|
|
698
|
-
});
|
|
699
|
-
}
|
|
700
|
-
/**
|
|
701
|
-
* Emits an event to the injector profiler with the instance that was created. Note that
|
|
702
|
-
* the injector associated with this emission can be accessed by using getDebugInjectContext()
|
|
703
|
-
*
|
|
704
|
-
* @param instance an object created by an injector
|
|
705
|
-
*/
|
|
706
|
-
function emitInstanceCreatedByInjectorEvent(instance) {
|
|
707
|
-
!ngDevMode && throwError('Injector profiler should never be called in production mode');
|
|
708
|
-
injectorProfiler({
|
|
709
|
-
type: 1 /* InjectorProfilerEventType.InstanceCreatedByInjector */,
|
|
710
|
-
context: getInjectorProfilerContext(),
|
|
711
|
-
instance: { value: instance },
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
/**
|
|
715
|
-
* @param token DI token associated with injected service
|
|
716
|
-
* @param value the instance of the injected service (i.e the result of `inject(token)`)
|
|
717
|
-
* @param flags the flags that the token was injected with
|
|
718
|
-
*/
|
|
719
|
-
function emitInjectEvent(token, value, flags) {
|
|
720
|
-
!ngDevMode && throwError('Injector profiler should never be called in production mode');
|
|
721
|
-
injectorProfiler({
|
|
722
|
-
type: 0 /* InjectorProfilerEventType.Inject */,
|
|
723
|
-
context: getInjectorProfilerContext(),
|
|
724
|
-
service: { token, value, flags },
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
function emitEffectCreatedEvent(effect) {
|
|
728
|
-
!ngDevMode && throwError('Injector profiler should never be called in production mode');
|
|
729
|
-
injectorProfiler({
|
|
730
|
-
type: 3 /* InjectorProfilerEventType.EffectCreated */,
|
|
731
|
-
context: getInjectorProfilerContext(),
|
|
732
|
-
effect,
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
function runInInjectorProfilerContext(injector, token, callback) {
|
|
736
|
-
!ngDevMode &&
|
|
737
|
-
throwError('runInInjectorProfilerContext should never be called in production mode');
|
|
738
|
-
const prevInjectContext = setInjectorProfilerContext({ injector, token });
|
|
739
|
-
try {
|
|
740
|
-
callback();
|
|
741
|
-
}
|
|
742
|
-
finally {
|
|
743
|
-
setInjectorProfilerContext(prevInjectContext);
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
function isEnvironmentProviders(value) {
|
|
748
|
-
return value && !!value.ɵproviders;
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
const NG_COMP_DEF = getClosureSafeProperty({ ɵcmp: getClosureSafeProperty });
|
|
752
|
-
const NG_DIR_DEF = getClosureSafeProperty({ ɵdir: getClosureSafeProperty });
|
|
753
|
-
const NG_PIPE_DEF = getClosureSafeProperty({ ɵpipe: getClosureSafeProperty });
|
|
754
|
-
const NG_MOD_DEF = getClosureSafeProperty({ ɵmod: getClosureSafeProperty });
|
|
755
|
-
const NG_FACTORY_DEF = getClosureSafeProperty({ ɵfac: getClosureSafeProperty });
|
|
756
|
-
/**
|
|
757
|
-
* If a directive is diPublic, bloomAdd sets a property on the type with this constant as
|
|
758
|
-
* the key and the directive's unique ID as the value. This allows us to map directives to their
|
|
759
|
-
* bloom filter bit for DI.
|
|
760
|
-
*/
|
|
761
|
-
// TODO(misko): This is wrong. The NG_ELEMENT_ID should never be minified.
|
|
762
|
-
const NG_ELEMENT_ID = getClosureSafeProperty({
|
|
763
|
-
__NG_ELEMENT_ID__: getClosureSafeProperty,
|
|
764
|
-
});
|
|
765
|
-
/**
|
|
766
|
-
* The `NG_ENV_ID` field on a DI token indicates special processing in the `EnvironmentInjector`:
|
|
767
|
-
* getting such tokens from the `EnvironmentInjector` will bypass the standard DI resolution
|
|
768
|
-
* strategy and instead will return implementation produced by the `NG_ENV_ID` factory function.
|
|
769
|
-
*
|
|
770
|
-
* This particular retrieval of DI tokens is mostly done to eliminate circular dependencies and
|
|
771
|
-
* improve tree-shaking.
|
|
772
|
-
*/
|
|
773
|
-
const NG_ENV_ID = getClosureSafeProperty({ __NG_ENV_ID__: getClosureSafeProperty });
|
|
774
|
-
|
|
775
|
-
/**
|
|
776
|
-
* Used for stringify render output in Ivy.
|
|
777
|
-
* Important! This function is very performance-sensitive and we should
|
|
778
|
-
* be extra careful not to introduce megamorphic reads in it.
|
|
779
|
-
* Check `core/test/render3/perf/render_stringify` for benchmarks and alternate implementations.
|
|
780
|
-
*/
|
|
781
|
-
function renderStringify(value) {
|
|
782
|
-
if (typeof value === 'string')
|
|
783
|
-
return value;
|
|
784
|
-
if (value == null)
|
|
785
|
-
return '';
|
|
786
|
-
// Use `String` so that it invokes the `toString` method of the value. Note that this
|
|
787
|
-
// appears to be faster than calling `value.toString` (see `render_stringify` benchmark).
|
|
788
|
-
return String(value);
|
|
789
|
-
}
|
|
790
|
-
/**
|
|
791
|
-
* Used to stringify a value so that it can be displayed in an error message.
|
|
792
|
-
*
|
|
793
|
-
* Important! This function contains a megamorphic read and should only be
|
|
794
|
-
* used for error messages.
|
|
795
|
-
*/
|
|
796
|
-
function stringifyForError(value) {
|
|
797
|
-
if (typeof value === 'function')
|
|
798
|
-
return value.name || value.toString();
|
|
799
|
-
if (typeof value === 'object' && value != null && typeof value.type === 'function') {
|
|
800
|
-
return value.type.name || value.type.toString();
|
|
801
|
-
}
|
|
802
|
-
return renderStringify(value);
|
|
803
|
-
}
|
|
804
|
-
/**
|
|
805
|
-
* Used to stringify a `Type` and including the file path and line number in which it is defined, if
|
|
806
|
-
* possible, for better debugging experience.
|
|
807
|
-
*
|
|
808
|
-
* Important! This function contains a megamorphic read and should only be used for error messages.
|
|
809
|
-
*/
|
|
810
|
-
function debugStringifyTypeForError(type) {
|
|
811
|
-
// TODO(pmvald): Do some refactoring so that we can use getComponentDef here without creating
|
|
812
|
-
// circular deps.
|
|
813
|
-
let componentDef = type[NG_COMP_DEF] || null;
|
|
814
|
-
if (componentDef !== null && componentDef.debugInfo) {
|
|
815
|
-
return stringifyTypeFromDebugInfo(componentDef.debugInfo);
|
|
816
|
-
}
|
|
817
|
-
return stringifyForError(type);
|
|
818
|
-
}
|
|
819
|
-
// TODO(pmvald): Do some refactoring so that we can use the type ClassDebugInfo for the param
|
|
820
|
-
// debugInfo here without creating circular deps.
|
|
821
|
-
function stringifyTypeFromDebugInfo(debugInfo) {
|
|
822
|
-
if (!debugInfo.filePath || !debugInfo.lineNumber) {
|
|
823
|
-
return debugInfo.className;
|
|
824
|
-
}
|
|
825
|
-
else {
|
|
826
|
-
return `${debugInfo.className} (at ${debugInfo.filePath}:${debugInfo.lineNumber})`;
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
const NG_RUNTIME_ERROR_CODE = getClosureSafeProperty({ 'ngErrorCode': getClosureSafeProperty });
|
|
831
|
-
const NG_RUNTIME_ERROR_MESSAGE = getClosureSafeProperty({ 'ngErrorMessage': getClosureSafeProperty });
|
|
832
|
-
const NG_TOKEN_PATH = getClosureSafeProperty({ 'ngTokenPath': getClosureSafeProperty });
|
|
833
|
-
/** Creates a circular dependency runtime error. */
|
|
834
|
-
function cyclicDependencyError(token, path) {
|
|
835
|
-
const message = ngDevMode ? `Circular dependency detected for \`${token}\`.` : '';
|
|
836
|
-
return createRuntimeError(message, -200 /* RuntimeErrorCode.CYCLIC_DI_DEPENDENCY */, path);
|
|
837
|
-
}
|
|
838
|
-
/** Creates a circular dependency runtime error including a dependency path in the error message. */
|
|
839
|
-
function cyclicDependencyErrorWithDetails(token, path) {
|
|
840
|
-
return augmentRuntimeError(cyclicDependencyError(token, path), null);
|
|
841
|
-
}
|
|
842
|
-
function throwMixedMultiProviderError() {
|
|
843
|
-
throw new Error(`Cannot mix multi providers and regular providers`);
|
|
844
|
-
}
|
|
845
|
-
function throwInvalidProviderError(ngModuleType, providers, provider) {
|
|
846
|
-
if (ngModuleType && providers) {
|
|
847
|
-
const providerDetail = providers.map((v) => (v == provider ? '?' + provider + '?' : '...'));
|
|
848
|
-
throw new Error(`Invalid provider for the NgModule '${stringify(ngModuleType)}' - only instances of Provider and Type are allowed, got: [${providerDetail.join(', ')}]`);
|
|
849
|
-
}
|
|
850
|
-
else if (isEnvironmentProviders(provider)) {
|
|
851
|
-
if (provider.ɵfromNgModule) {
|
|
852
|
-
throw new RuntimeError(207 /* RuntimeErrorCode.PROVIDER_IN_WRONG_CONTEXT */, `Invalid providers from 'importProvidersFrom' present in a non-environment injector. 'importProvidersFrom' can't be used for component providers.`);
|
|
853
|
-
}
|
|
854
|
-
else {
|
|
855
|
-
throw new RuntimeError(207 /* RuntimeErrorCode.PROVIDER_IN_WRONG_CONTEXT */, `Invalid providers present in a non-environment injector. 'EnvironmentProviders' can't be used for component providers.`);
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
else {
|
|
859
|
-
throw new Error('Invalid provider');
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
/** Throws an error when a token is not found in DI. */
|
|
863
|
-
function throwProviderNotFoundError(token, injectorName) {
|
|
864
|
-
const errorMessage = ngDevMode &&
|
|
865
|
-
`No provider for ${stringifyForError(token)} found${injectorName ? ` in ${injectorName}` : ''}`;
|
|
866
|
-
throw new RuntimeError(-201 /* RuntimeErrorCode.PROVIDER_NOT_FOUND */, errorMessage);
|
|
867
|
-
}
|
|
868
|
-
/**
|
|
869
|
-
* Given an Error instance and the current token - update the monkey-patched
|
|
870
|
-
* dependency path info to include that token.
|
|
871
|
-
*
|
|
872
|
-
* @param error Current instance of the Error class.
|
|
873
|
-
* @param token Extra token that should be appended.
|
|
874
|
-
*/
|
|
875
|
-
function prependTokenToDependencyPath(error, token) {
|
|
876
|
-
error[NG_TOKEN_PATH] ??= [];
|
|
877
|
-
// Append current token to the current token path. Since the error
|
|
878
|
-
// is bubbling up, add the token in front of other tokens.
|
|
879
|
-
const currentPath = error[NG_TOKEN_PATH];
|
|
880
|
-
// Do not append the same token multiple times.
|
|
881
|
-
let pathStr;
|
|
882
|
-
if (typeof token === 'object' && 'multi' in token && token?.multi === true) {
|
|
883
|
-
assertDefined(token.provide, 'Token with multi: true should have a provide property');
|
|
884
|
-
pathStr = stringifyForError(token.provide);
|
|
885
|
-
}
|
|
886
|
-
else {
|
|
887
|
-
pathStr = stringifyForError(token);
|
|
888
|
-
}
|
|
889
|
-
if (currentPath[0] !== pathStr) {
|
|
890
|
-
error[NG_TOKEN_PATH].unshift(pathStr);
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
/**
|
|
894
|
-
* Modifies an Error instance with an updated error message
|
|
895
|
-
* based on the accumulated dependency path.
|
|
896
|
-
*
|
|
897
|
-
* @param error Current instance of the Error class.
|
|
898
|
-
* @param source Extra info about the injector which started
|
|
899
|
-
* the resolution process, which eventually failed.
|
|
900
|
-
*/
|
|
901
|
-
function augmentRuntimeError(error, source) {
|
|
902
|
-
const tokenPath = error[NG_TOKEN_PATH];
|
|
903
|
-
const errorCode = error[NG_RUNTIME_ERROR_CODE];
|
|
904
|
-
const message = error[NG_RUNTIME_ERROR_MESSAGE] || error.message;
|
|
905
|
-
error.message = formatErrorMessage(message, errorCode, tokenPath, source);
|
|
906
|
-
return error;
|
|
907
|
-
}
|
|
908
|
-
/**
|
|
909
|
-
* Creates an initial RuntimeError instance when a problem is detected.
|
|
910
|
-
* Monkey-patches extra info in the RuntimeError instance, so that it can
|
|
911
|
-
* be reused later, before throwing the final error.
|
|
912
|
-
*/
|
|
913
|
-
function createRuntimeError(message, code, path) {
|
|
914
|
-
// Cast to `any`, so that extra info can be monkey-patched onto this instance.
|
|
915
|
-
const error = new RuntimeError(code, message);
|
|
916
|
-
// Monkey-patch a runtime error code and a path onto an Error instance.
|
|
917
|
-
error[NG_RUNTIME_ERROR_CODE] = code;
|
|
918
|
-
error[NG_RUNTIME_ERROR_MESSAGE] = message;
|
|
919
|
-
if (path) {
|
|
920
|
-
error[NG_TOKEN_PATH] = path;
|
|
921
|
-
}
|
|
922
|
-
return error;
|
|
923
|
-
}
|
|
924
|
-
/**
|
|
925
|
-
* Reads monkey-patched error code from the given Error instance.
|
|
926
|
-
*/
|
|
927
|
-
function getRuntimeErrorCode(error) {
|
|
928
|
-
return error[NG_RUNTIME_ERROR_CODE];
|
|
929
|
-
}
|
|
930
|
-
function formatErrorMessage(text, code, path = [], source = null) {
|
|
931
|
-
let pathDetails = '';
|
|
932
|
-
// If the path is empty or contains only one element (self) -
|
|
933
|
-
// do not append additional info the error message.
|
|
934
|
-
if (path && path.length > 1) {
|
|
935
|
-
pathDetails = ` Path: ${path.join(' -> ')}.`;
|
|
936
|
-
}
|
|
937
|
-
const sourceDetails = source ? ` Source: ${source}.` : '';
|
|
938
|
-
return formatRuntimeError(code, `${text}${sourceDetails}${pathDetails}`);
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
/**
|
|
942
|
-
* Current implementation of inject.
|
|
943
|
-
*
|
|
944
|
-
* By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed
|
|
945
|
-
* to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this
|
|
946
|
-
* way for two reasons:
|
|
947
|
-
* 1. `Injector` should not depend on ivy logic.
|
|
948
|
-
* 2. To maintain tree shake-ability we don't want to bring in unnecessary code.
|
|
949
|
-
*/
|
|
950
|
-
let _injectImplementation;
|
|
951
|
-
function getInjectImplementation() {
|
|
952
|
-
return _injectImplementation;
|
|
953
|
-
}
|
|
954
|
-
/**
|
|
955
|
-
* Sets the current inject implementation.
|
|
956
|
-
*/
|
|
957
|
-
function setInjectImplementation(impl) {
|
|
958
|
-
const previous = _injectImplementation;
|
|
959
|
-
_injectImplementation = impl;
|
|
960
|
-
return previous;
|
|
961
|
-
}
|
|
962
|
-
/**
|
|
963
|
-
* Injects `root` tokens in limp mode.
|
|
964
|
-
*
|
|
965
|
-
* If no injector exists, we can still inject tree-shakable providers which have `providedIn` set to
|
|
966
|
-
* `"root"`. This is known as the limp mode injection. In such case the value is stored in the
|
|
967
|
-
* injectable definition.
|
|
968
|
-
*/
|
|
969
|
-
function injectRootLimpMode(token, notFoundValue, flags) {
|
|
970
|
-
const injectableDef = getInjectableDef(token);
|
|
971
|
-
if (injectableDef && injectableDef.providedIn == 'root') {
|
|
972
|
-
return injectableDef.value === undefined
|
|
973
|
-
? (injectableDef.value = injectableDef.factory())
|
|
974
|
-
: injectableDef.value;
|
|
975
|
-
}
|
|
976
|
-
if (flags & 8 /* InternalInjectFlags.Optional */)
|
|
977
|
-
return null;
|
|
978
|
-
if (notFoundValue !== undefined)
|
|
979
|
-
return notFoundValue;
|
|
980
|
-
throwProviderNotFoundError(token, 'Injector');
|
|
981
|
-
}
|
|
982
|
-
/**
|
|
983
|
-
* Assert that `_injectImplementation` is not `fn`.
|
|
984
|
-
*
|
|
985
|
-
* This is useful, to prevent infinite recursion.
|
|
986
|
-
*
|
|
987
|
-
* @param fn Function which it should not equal to
|
|
988
|
-
*/
|
|
989
|
-
function assertInjectImplementationNotEqual(fn) {
|
|
990
|
-
ngDevMode &&
|
|
991
|
-
assertNotEqual(_injectImplementation, fn, 'Calling ɵɵinject would cause infinite recursion');
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
const _THROW_IF_NOT_FOUND = {};
|
|
995
|
-
const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
|
|
996
|
-
/*
|
|
997
|
-
* Name of a property (that we patch onto DI decorator), which is used as an annotation of which
|
|
998
|
-
* InjectFlag this decorator represents. This allows to avoid direct references to the DI decorators
|
|
999
|
-
* in the code, thus making them tree-shakable.
|
|
1000
|
-
*/
|
|
1001
|
-
const DI_DECORATOR_FLAG = '__NG_DI_FLAG__';
|
|
1002
|
-
/**
|
|
1003
|
-
* A wrapper around an `Injector` that implements the `PrimitivesInjector` interface.
|
|
1004
|
-
*
|
|
1005
|
-
* This is used to allow the `inject` function to be used with the new primitives-based DI system.
|
|
1006
|
-
*/
|
|
1007
|
-
class RetrievingInjector {
|
|
1008
|
-
injector;
|
|
1009
|
-
constructor(injector) {
|
|
1010
|
-
this.injector = injector;
|
|
1011
|
-
}
|
|
1012
|
-
retrieve(token, options) {
|
|
1013
|
-
const flags = convertToBitFlags(options) || 0 /* InternalInjectFlags.Default */;
|
|
1014
|
-
try {
|
|
1015
|
-
return this.injector.get(token,
|
|
1016
|
-
// When a dependency is requested with an optional flag, DI returns null as the default value.
|
|
1017
|
-
(flags & 8 /* InternalInjectFlags.Optional */ ? null : THROW_IF_NOT_FOUND), flags);
|
|
1018
|
-
}
|
|
1019
|
-
catch (e) {
|
|
1020
|
-
if (isNotFound(e)) {
|
|
1021
|
-
return e;
|
|
1022
|
-
}
|
|
1023
|
-
throw e;
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
function injectInjectorOnly(token, flags = 0 /* InternalInjectFlags.Default */) {
|
|
1028
|
-
const currentInjector = getCurrentInjector();
|
|
1029
|
-
if (currentInjector === undefined) {
|
|
1030
|
-
throw new RuntimeError(-203 /* RuntimeErrorCode.MISSING_INJECTION_CONTEXT */, ngDevMode &&
|
|
1031
|
-
`The \`${stringify(token)}\` token injection failed. \`inject()\` function must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with \`runInInjectionContext\`.`);
|
|
1032
|
-
}
|
|
1033
|
-
else if (currentInjector === null) {
|
|
1034
|
-
return injectRootLimpMode(token, undefined, flags);
|
|
1035
|
-
}
|
|
1036
|
-
else {
|
|
1037
|
-
const options = convertToInjectOptions(flags);
|
|
1038
|
-
// TODO: improve the typings here.
|
|
1039
|
-
// `token` can be a multi: true provider definition, which is considered as a Token but not represented in the typings
|
|
1040
|
-
const value = currentInjector.retrieve(token, options);
|
|
1041
|
-
ngDevMode && emitInjectEvent(token, value, flags);
|
|
1042
|
-
if (isNotFound(value)) {
|
|
1043
|
-
if (options.optional) {
|
|
1044
|
-
return null;
|
|
1045
|
-
}
|
|
1046
|
-
throw value;
|
|
1047
|
-
}
|
|
1048
|
-
return value;
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
function ɵɵinject(token, flags = 0 /* InternalInjectFlags.Default */) {
|
|
1052
|
-
return (getInjectImplementation() || injectInjectorOnly)(resolveForwardRef(token), flags);
|
|
1053
|
-
}
|
|
1054
|
-
/**
|
|
1055
|
-
* Throws an error indicating that a factory function could not be generated by the compiler for a
|
|
1056
|
-
* particular class.
|
|
1057
|
-
*
|
|
1058
|
-
* The name of the class is not mentioned here, but will be in the generated factory function name
|
|
1059
|
-
* and thus in the stack trace.
|
|
1060
|
-
*
|
|
1061
|
-
* @codeGenApi
|
|
1062
|
-
*/
|
|
1063
|
-
function ɵɵinvalidFactoryDep(index) {
|
|
1064
|
-
throw new RuntimeError(202 /* RuntimeErrorCode.INVALID_FACTORY_DEPENDENCY */, ngDevMode &&
|
|
1065
|
-
`This constructor is not compatible with Angular Dependency Injection because its dependency at index ${index} of the parameter list is invalid.
|
|
1066
|
-
This can happen if the dependency type is a primitive like a string or if an ancestor of this class is missing an Angular decorator.
|
|
1067
|
-
|
|
1068
|
-
Please check that 1) the type for the parameter at index ${index} is correct and 2) the correct Angular decorators are defined for this class and its ancestors.`);
|
|
1069
|
-
}
|
|
1070
|
-
/**
|
|
1071
|
-
* Injects a token from the currently active injector.
|
|
1072
|
-
* `inject` is only supported in an [injection context](guide/di/dependency-injection-context). It
|
|
1073
|
-
* can be used during:
|
|
1074
|
-
* - Construction (via the `constructor`) of a class being instantiated by the DI system, such
|
|
1075
|
-
* as an `@Injectable` or `@Component`.
|
|
1076
|
-
* - In the initializer for fields of such classes.
|
|
1077
|
-
* - In the factory function specified for `useFactory` of a `Provider` or an `@Injectable`.
|
|
1078
|
-
* - In the `factory` function specified for an `InjectionToken`.
|
|
1079
|
-
* - In a stackframe of a function call in a DI context
|
|
1080
|
-
*
|
|
1081
|
-
* @param token A token that represents a dependency that should be injected.
|
|
1082
|
-
* @param flags Optional flags that control how injection is executed.
|
|
1083
|
-
* The flags correspond to injection strategies that can be specified with
|
|
1084
|
-
* parameter decorators `@Host`, `@Self`, `@SkipSelf`, and `@Optional`.
|
|
1085
|
-
* @returns the injected value if operation is successful, `null` otherwise.
|
|
1086
|
-
* @throws if called outside of a supported context.
|
|
1087
|
-
*
|
|
1088
|
-
* @usageNotes
|
|
1089
|
-
* In practice the `inject()` calls are allowed in a constructor, a constructor parameter and a
|
|
1090
|
-
* field initializer:
|
|
1091
|
-
*
|
|
1092
|
-
* ```ts
|
|
1093
|
-
* @Injectable({providedIn: 'root'})
|
|
1094
|
-
* export class Car {
|
|
1095
|
-
* radio: Radio|undefined;
|
|
1096
|
-
* // OK: field initializer
|
|
1097
|
-
* spareTyre = inject(Tyre);
|
|
1098
|
-
*
|
|
1099
|
-
* constructor() {
|
|
1100
|
-
* // OK: constructor body
|
|
1101
|
-
* this.radio = inject(Radio);
|
|
1102
|
-
* }
|
|
1103
|
-
* }
|
|
1104
|
-
* ```
|
|
1105
|
-
*
|
|
1106
|
-
* It is also legal to call `inject` from a provider's factory:
|
|
1107
|
-
*
|
|
1108
|
-
* ```ts
|
|
1109
|
-
* providers: [
|
|
1110
|
-
* {provide: Car, useFactory: () => {
|
|
1111
|
-
* // OK: a class factory
|
|
1112
|
-
* const engine = inject(Engine);
|
|
1113
|
-
* return new Car(engine);
|
|
1114
|
-
* }}
|
|
1115
|
-
* ]
|
|
1116
|
-
* ```
|
|
1117
|
-
*
|
|
1118
|
-
* Calls to the `inject()` function outside of the class creation context will result in error. Most
|
|
1119
|
-
* notably, calls to `inject()` are disallowed after a class instance was created, in methods
|
|
1120
|
-
* (including lifecycle hooks):
|
|
1121
|
-
*
|
|
1122
|
-
* ```ts
|
|
1123
|
-
* @Component({ ... })
|
|
1124
|
-
* export class CarComponent {
|
|
1125
|
-
* ngOnInit() {
|
|
1126
|
-
* // ERROR: too late, the component instance was already created
|
|
1127
|
-
* const engine = inject(Engine);
|
|
1128
|
-
* engine.start();
|
|
1129
|
-
* }
|
|
1130
|
-
* }
|
|
1131
|
-
* ```
|
|
1132
|
-
*
|
|
1133
|
-
* @publicApi
|
|
1134
|
-
*/
|
|
1135
|
-
function inject(token, options) {
|
|
1136
|
-
// The `as any` here _shouldn't_ be necessary, but without it JSCompiler
|
|
1137
|
-
// throws a disambiguation error due to the multiple signatures.
|
|
1138
|
-
return ɵɵinject(token, convertToBitFlags(options));
|
|
1139
|
-
}
|
|
1140
|
-
// Converts object-based DI flags (`InjectOptions`) to bit flags (`InjectFlags`).
|
|
1141
|
-
function convertToBitFlags(flags) {
|
|
1142
|
-
if (typeof flags === 'undefined' || typeof flags === 'number') {
|
|
1143
|
-
return flags;
|
|
1144
|
-
}
|
|
1145
|
-
// While TypeScript doesn't accept it without a cast, bitwise OR with false-y values in
|
|
1146
|
-
// JavaScript is a no-op. We can use that for a very codesize-efficient conversion from
|
|
1147
|
-
// `InjectOptions` to `InjectFlags`.
|
|
1148
|
-
return (0 /* InternalInjectFlags.Default */ | // comment to force a line break in the formatter
|
|
1149
|
-
(flags.optional && 8 /* InternalInjectFlags.Optional */) |
|
|
1150
|
-
(flags.host && 1 /* InternalInjectFlags.Host */) |
|
|
1151
|
-
(flags.self && 2 /* InternalInjectFlags.Self */) |
|
|
1152
|
-
(flags.skipSelf && 4 /* InternalInjectFlags.SkipSelf */));
|
|
1153
|
-
}
|
|
1154
|
-
// Converts bitflags to inject options
|
|
1155
|
-
function convertToInjectOptions(flags) {
|
|
1156
|
-
return {
|
|
1157
|
-
optional: !!(flags & 8 /* InternalInjectFlags.Optional */),
|
|
1158
|
-
host: !!(flags & 1 /* InternalInjectFlags.Host */),
|
|
1159
|
-
self: !!(flags & 2 /* InternalInjectFlags.Self */),
|
|
1160
|
-
skipSelf: !!(flags & 4 /* InternalInjectFlags.SkipSelf */),
|
|
1161
|
-
};
|
|
1162
|
-
}
|
|
1163
|
-
function injectArgs(types) {
|
|
1164
|
-
const args = [];
|
|
1165
|
-
for (let i = 0; i < types.length; i++) {
|
|
1166
|
-
const arg = resolveForwardRef(types[i]);
|
|
1167
|
-
if (Array.isArray(arg)) {
|
|
1168
|
-
if (arg.length === 0) {
|
|
1169
|
-
throw new RuntimeError(900 /* RuntimeErrorCode.INVALID_DIFFER_INPUT */, ngDevMode && 'Arguments array must have arguments.');
|
|
1170
|
-
}
|
|
1171
|
-
let type = undefined;
|
|
1172
|
-
let flags = 0 /* InternalInjectFlags.Default */;
|
|
1173
|
-
for (let j = 0; j < arg.length; j++) {
|
|
1174
|
-
const meta = arg[j];
|
|
1175
|
-
const flag = getInjectFlag(meta);
|
|
1176
|
-
if (typeof flag === 'number') {
|
|
1177
|
-
// Special case when we handle @Inject decorator.
|
|
1178
|
-
if (flag === -1 /* DecoratorFlags.Inject */) {
|
|
1179
|
-
type = meta.token;
|
|
1180
|
-
}
|
|
1181
|
-
else {
|
|
1182
|
-
flags |= flag;
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
else {
|
|
1186
|
-
type = meta;
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
args.push(ɵɵinject(type, flags));
|
|
1190
|
-
}
|
|
1191
|
-
else {
|
|
1192
|
-
args.push(ɵɵinject(arg));
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
return args;
|
|
1196
|
-
}
|
|
1197
|
-
/**
|
|
1198
|
-
* Attaches a given InjectFlag to a given decorator using monkey-patching.
|
|
1199
|
-
* Since DI decorators can be used in providers `deps` array (when provider is configured using
|
|
1200
|
-
* `useFactory`) without initialization (e.g. `Host`) and as an instance (e.g. `new Host()`), we
|
|
1201
|
-
* attach the flag to make it available both as a static property and as a field on decorator
|
|
1202
|
-
* instance.
|
|
1203
|
-
*
|
|
1204
|
-
* @param decorator Provided DI decorator.
|
|
1205
|
-
* @param flag InjectFlag that should be applied.
|
|
1206
|
-
*/
|
|
1207
|
-
function attachInjectFlag(decorator, flag) {
|
|
1208
|
-
decorator[DI_DECORATOR_FLAG] = flag;
|
|
1209
|
-
decorator.prototype[DI_DECORATOR_FLAG] = flag;
|
|
1210
|
-
return decorator;
|
|
1211
|
-
}
|
|
1212
|
-
/**
|
|
1213
|
-
* Reads monkey-patched property that contains InjectFlag attached to a decorator.
|
|
1214
|
-
*
|
|
1215
|
-
* @param token Token that may contain monkey-patched DI flags property.
|
|
1216
|
-
*/
|
|
1217
|
-
function getInjectFlag(token) {
|
|
1218
|
-
return token[DI_DECORATOR_FLAG];
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
function getFactoryDef(type, throwNotFound) {
|
|
1222
|
-
const hasFactoryDef = type.hasOwnProperty(NG_FACTORY_DEF);
|
|
1223
|
-
if (!hasFactoryDef && throwNotFound === true && ngDevMode) {
|
|
1224
|
-
throw new Error(`Type ${stringify(type)} does not have 'ɵfac' property.`);
|
|
1225
|
-
}
|
|
1226
|
-
return hasFactoryDef ? type[NG_FACTORY_DEF] : null;
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
/**
|
|
1230
|
-
* Determines if the contents of two arrays is identical
|
|
1231
|
-
*
|
|
1232
|
-
* @param a first array
|
|
1233
|
-
* @param b second array
|
|
1234
|
-
* @param identityAccessor Optional function for extracting stable object identity from a value in
|
|
1235
|
-
* the array.
|
|
1236
|
-
*/
|
|
1237
|
-
function arrayEquals(a, b, identityAccessor) {
|
|
1238
|
-
if (a.length !== b.length)
|
|
1239
|
-
return false;
|
|
1240
|
-
for (let i = 0; i < a.length; i++) {
|
|
1241
|
-
let valueA = a[i];
|
|
1242
|
-
let valueB = b[i];
|
|
1243
|
-
if (identityAccessor) {
|
|
1244
|
-
valueA = identityAccessor(valueA);
|
|
1245
|
-
valueB = identityAccessor(valueB);
|
|
1246
|
-
}
|
|
1247
|
-
if (valueB !== valueA) {
|
|
1248
|
-
return false;
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
return true;
|
|
1252
|
-
}
|
|
1253
|
-
/**
|
|
1254
|
-
* Flattens an array.
|
|
1255
|
-
*/
|
|
1256
|
-
function flatten(list) {
|
|
1257
|
-
return list.flat(Number.POSITIVE_INFINITY);
|
|
1258
|
-
}
|
|
1259
|
-
function deepForEach(input, fn) {
|
|
1260
|
-
input.forEach((value) => (Array.isArray(value) ? deepForEach(value, fn) : fn(value)));
|
|
1261
|
-
}
|
|
1262
|
-
function addToArray(arr, index, value) {
|
|
1263
|
-
// perf: array.push is faster than array.splice!
|
|
1264
|
-
if (index >= arr.length) {
|
|
1265
|
-
arr.push(value);
|
|
1266
|
-
}
|
|
1267
|
-
else {
|
|
1268
|
-
arr.splice(index, 0, value);
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
function removeFromArray(arr, index) {
|
|
1272
|
-
// perf: array.pop is faster than array.splice!
|
|
1273
|
-
if (index >= arr.length - 1) {
|
|
1274
|
-
return arr.pop();
|
|
1275
|
-
}
|
|
1276
|
-
else {
|
|
1277
|
-
return arr.splice(index, 1)[0];
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
function newArray(size, value) {
|
|
1281
|
-
const list = [];
|
|
1282
|
-
for (let i = 0; i < size; i++) {
|
|
1283
|
-
list.push(value);
|
|
1284
|
-
}
|
|
1285
|
-
return list;
|
|
1286
|
-
}
|
|
1287
|
-
/**
|
|
1288
|
-
* Remove item from array (Same as `Array.splice()` but faster.)
|
|
1289
|
-
*
|
|
1290
|
-
* `Array.splice()` is not as fast because it has to allocate an array for the elements which were
|
|
1291
|
-
* removed. This causes memory pressure and slows down code when most of the time we don't
|
|
1292
|
-
* care about the deleted items array.
|
|
1293
|
-
*
|
|
1294
|
-
* https://jsperf.com/fast-array-splice (About 20x faster)
|
|
1295
|
-
*
|
|
1296
|
-
* @param array Array to splice
|
|
1297
|
-
* @param index Index of element in array to remove.
|
|
1298
|
-
* @param count Number of items to remove.
|
|
1299
|
-
*/
|
|
1300
|
-
function arraySplice(array, index, count) {
|
|
1301
|
-
const length = array.length - count;
|
|
1302
|
-
while (index < length) {
|
|
1303
|
-
array[index] = array[index + count];
|
|
1304
|
-
index++;
|
|
1305
|
-
}
|
|
1306
|
-
while (count--) {
|
|
1307
|
-
array.pop(); // shrink the array
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
/**
|
|
1311
|
-
* Same as `Array.splice2(index, 0, value1, value2)` but faster.
|
|
1312
|
-
*
|
|
1313
|
-
* `Array.splice()` is not fast because it has to allocate an array for the elements which were
|
|
1314
|
-
* removed. This causes memory pressure and slows down code when most of the time we don't
|
|
1315
|
-
* care about the deleted items array.
|
|
1316
|
-
*
|
|
1317
|
-
* @param array Array to splice.
|
|
1318
|
-
* @param index Index in array where the `value` should be added.
|
|
1319
|
-
* @param value1 Value to add to array.
|
|
1320
|
-
* @param value2 Value to add to array.
|
|
1321
|
-
*/
|
|
1322
|
-
function arrayInsert2(array, index, value1, value2) {
|
|
1323
|
-
ngDevMode && assertLessThanOrEqual(index, array.length, "Can't insert past array end.");
|
|
1324
|
-
let end = array.length;
|
|
1325
|
-
if (end == index) {
|
|
1326
|
-
// inserting at the end.
|
|
1327
|
-
array.push(value1, value2);
|
|
1328
|
-
}
|
|
1329
|
-
else if (end === 1) {
|
|
1330
|
-
// corner case when we have less items in array than we have items to insert.
|
|
1331
|
-
array.push(value2, array[0]);
|
|
1332
|
-
array[0] = value1;
|
|
1333
|
-
}
|
|
1334
|
-
else {
|
|
1335
|
-
end--;
|
|
1336
|
-
array.push(array[end - 1], array[end]);
|
|
1337
|
-
while (end > index) {
|
|
1338
|
-
const previousEnd = end - 2;
|
|
1339
|
-
array[end] = array[previousEnd];
|
|
1340
|
-
end--;
|
|
1341
|
-
}
|
|
1342
|
-
array[index] = value1;
|
|
1343
|
-
array[index + 1] = value2;
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
/**
|
|
1347
|
-
* Set a `value` for a `key`.
|
|
1348
|
-
*
|
|
1349
|
-
* @param keyValueArray to modify.
|
|
1350
|
-
* @param key The key to locate or create.
|
|
1351
|
-
* @param value The value to set for a `key`.
|
|
1352
|
-
* @returns index (always even) of where the value vas set.
|
|
1353
|
-
*/
|
|
1354
|
-
function keyValueArraySet(keyValueArray, key, value) {
|
|
1355
|
-
let index = keyValueArrayIndexOf(keyValueArray, key);
|
|
1356
|
-
if (index >= 0) {
|
|
1357
|
-
// if we found it set it.
|
|
1358
|
-
keyValueArray[index | 1] = value;
|
|
1359
|
-
}
|
|
1360
|
-
else {
|
|
1361
|
-
index = ~index;
|
|
1362
|
-
arrayInsert2(keyValueArray, index, key, value);
|
|
1363
|
-
}
|
|
1364
|
-
return index;
|
|
1365
|
-
}
|
|
1366
|
-
/**
|
|
1367
|
-
* Retrieve a `value` for a `key` (on `undefined` if not found.)
|
|
1368
|
-
*
|
|
1369
|
-
* @param keyValueArray to search.
|
|
1370
|
-
* @param key The key to locate.
|
|
1371
|
-
* @return The `value` stored at the `key` location or `undefined if not found.
|
|
1372
|
-
*/
|
|
1373
|
-
function keyValueArrayGet(keyValueArray, key) {
|
|
1374
|
-
const index = keyValueArrayIndexOf(keyValueArray, key);
|
|
1375
|
-
if (index >= 0) {
|
|
1376
|
-
// if we found it retrieve it.
|
|
1377
|
-
return keyValueArray[index | 1];
|
|
1378
|
-
}
|
|
1379
|
-
return undefined;
|
|
1380
|
-
}
|
|
1381
|
-
/**
|
|
1382
|
-
* Retrieve a `key` index value in the array or `-1` if not found.
|
|
1383
|
-
*
|
|
1384
|
-
* @param keyValueArray to search.
|
|
1385
|
-
* @param key The key to locate.
|
|
1386
|
-
* @returns index of where the key is (or should have been.)
|
|
1387
|
-
* - positive (even) index if key found.
|
|
1388
|
-
* - negative index if key not found. (`~index` (even) to get the index where it should have
|
|
1389
|
-
* been inserted.)
|
|
1390
|
-
*/
|
|
1391
|
-
function keyValueArrayIndexOf(keyValueArray, key) {
|
|
1392
|
-
return _arrayIndexOfSorted(keyValueArray, key, 1);
|
|
1393
|
-
}
|
|
1394
|
-
/**
|
|
1395
|
-
* INTERNAL: Get an index of an `value` in a sorted `array` by grouping search by `shift`.
|
|
1396
|
-
*
|
|
1397
|
-
* NOTE:
|
|
1398
|
-
* - This uses binary search algorithm for fast removals.
|
|
1399
|
-
*
|
|
1400
|
-
* @param array A sorted array to binary search.
|
|
1401
|
-
* @param value The value to look for.
|
|
1402
|
-
* @param shift grouping shift.
|
|
1403
|
-
* - `0` means look at every location
|
|
1404
|
-
* - `1` means only look at every other (even) location (the odd locations are to be ignored as
|
|
1405
|
-
* they are values.)
|
|
1406
|
-
* @returns index of the value.
|
|
1407
|
-
* - positive index if value found.
|
|
1408
|
-
* - negative index if value not found. (`~index` to get the value where it should have been
|
|
1409
|
-
* inserted)
|
|
1410
|
-
*/
|
|
1411
|
-
function _arrayIndexOfSorted(array, value, shift) {
|
|
1412
|
-
ngDevMode && assertEqual(Array.isArray(array), true, 'Expecting an array');
|
|
1413
|
-
let start = 0;
|
|
1414
|
-
let end = array.length >> shift;
|
|
1415
|
-
while (end !== start) {
|
|
1416
|
-
const middle = start + ((end - start) >> 1); // find the middle.
|
|
1417
|
-
const current = array[middle << shift];
|
|
1418
|
-
if (value === current) {
|
|
1419
|
-
return middle << shift;
|
|
1420
|
-
}
|
|
1421
|
-
else if (current > value) {
|
|
1422
|
-
end = middle;
|
|
1423
|
-
}
|
|
1424
|
-
else {
|
|
1425
|
-
start = middle + 1; // We already searched middle so make it non-inclusive by adding 1
|
|
1426
|
-
}
|
|
1427
|
-
}
|
|
1428
|
-
return ~(end << shift);
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
/**
|
|
1432
|
-
* This file contains reuseable "empty" symbols that can be used as default return values
|
|
1433
|
-
* in different parts of the rendering code. Because the same symbols are returned, this
|
|
1434
|
-
* allows for identity checks against these values to be consistently used by the framework
|
|
1435
|
-
* code.
|
|
1436
|
-
*/
|
|
1437
|
-
const EMPTY_OBJ = {};
|
|
1438
|
-
const EMPTY_ARRAY = [];
|
|
1439
|
-
// freezing the values prevents any code from accidentally inserting new values in
|
|
1440
|
-
if ((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode()) {
|
|
1441
|
-
// These property accesses can be ignored because ngDevMode will be set to false
|
|
1442
|
-
// when optimizing code and the whole if statement will be dropped.
|
|
1443
|
-
// tslint:disable-next-line:no-toplevel-property-access
|
|
1444
|
-
Object.freeze(EMPTY_OBJ);
|
|
1445
|
-
// tslint:disable-next-line:no-toplevel-property-access
|
|
1446
|
-
Object.freeze(EMPTY_ARRAY);
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
/**
|
|
1450
|
-
* A multi-provider token for initialization functions that will run upon construction of an
|
|
1451
|
-
* environment injector.
|
|
1452
|
-
*
|
|
1453
|
-
* @deprecated from v19.0.0, use provideEnvironmentInitializer instead
|
|
1454
|
-
*
|
|
1455
|
-
* @see {@link provideEnvironmentInitializer}
|
|
1456
|
-
*
|
|
1457
|
-
* Note: As opposed to the `APP_INITIALIZER` token, the `ENVIRONMENT_INITIALIZER` functions are not awaited,
|
|
1458
|
-
* hence they should not be `async`.
|
|
1459
|
-
*
|
|
1460
|
-
* @publicApi
|
|
1461
|
-
*/
|
|
1462
|
-
const ENVIRONMENT_INITIALIZER = new InjectionToken(typeof ngDevMode !== undefined && ngDevMode ? 'ENVIRONMENT_INITIALIZER' : '');
|
|
1463
|
-
|
|
1464
|
-
/**
|
|
1465
|
-
* An InjectionToken that gets the current `Injector` for `createInjector()`-style injectors.
|
|
1466
|
-
*
|
|
1467
|
-
* Requesting this token instead of `Injector` allows `StaticInjector` to be tree-shaken from a
|
|
1468
|
-
* project.
|
|
1469
|
-
*
|
|
1470
|
-
* @publicApi
|
|
1471
|
-
*/
|
|
1472
|
-
const INJECTOR$1 = new InjectionToken(typeof ngDevMode !== undefined && ngDevMode ? 'INJECTOR' : '',
|
|
1473
|
-
// Disable tslint because this is const enum which gets inlined not top level prop access.
|
|
1474
|
-
// tslint:disable-next-line: no-toplevel-property-access
|
|
1475
|
-
-1 /* InjectorMarkers.Injector */);
|
|
1476
|
-
|
|
1477
|
-
const INJECTOR_DEF_TYPES = new InjectionToken(typeof ngDevMode !== undefined && ngDevMode ? 'INJECTOR_DEF_TYPES' : '');
|
|
1478
|
-
|
|
1479
|
-
class NullInjector {
|
|
1480
|
-
get(token, notFoundValue = THROW_IF_NOT_FOUND) {
|
|
1481
|
-
if (notFoundValue === THROW_IF_NOT_FOUND) {
|
|
1482
|
-
const message = ngDevMode ? `No provider found for \`${stringify(token)}\`.` : '';
|
|
1483
|
-
const error = createRuntimeError(message, -201 /* RuntimeErrorCode.PROVIDER_NOT_FOUND */);
|
|
1484
|
-
// Note: This is the name used by the primitives to identify a not found error.
|
|
1485
|
-
error.name = 'ɵNotFound';
|
|
1486
|
-
throw error;
|
|
1487
|
-
}
|
|
1488
|
-
return notFoundValue;
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
|
-
function getNgModuleDef(type) {
|
|
1493
|
-
return type[NG_MOD_DEF] || null;
|
|
1494
|
-
}
|
|
1495
|
-
function getNgModuleDefOrThrow(type) {
|
|
1496
|
-
const ngModuleDef = getNgModuleDef(type);
|
|
1497
|
-
if (!ngModuleDef) {
|
|
1498
|
-
throw new RuntimeError(915 /* RuntimeErrorCode.MISSING_NG_MODULE_DEFINITION */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
1499
|
-
`Type ${stringify(type)} does not have 'ɵmod' property.`);
|
|
1500
|
-
}
|
|
1501
|
-
return ngModuleDef;
|
|
1502
|
-
}
|
|
1503
|
-
/**
|
|
1504
|
-
* The following getter methods retrieve the definition from the type. Currently the retrieval
|
|
1505
|
-
* honors inheritance, but in the future we may change the rule to require that definitions are
|
|
1506
|
-
* explicit. This would require some sort of migration strategy.
|
|
1507
|
-
*/
|
|
1508
|
-
function getComponentDef(type) {
|
|
1509
|
-
return type[NG_COMP_DEF] || null;
|
|
1510
|
-
}
|
|
1511
|
-
function getDirectiveDefOrThrow(type) {
|
|
1512
|
-
const def = getDirectiveDef(type);
|
|
1513
|
-
if (!def) {
|
|
1514
|
-
throw new RuntimeError(916 /* RuntimeErrorCode.MISSING_DIRECTIVE_DEFINITION */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
|
|
1515
|
-
`Type ${stringify(type)} does not have 'ɵdir' property.`);
|
|
1516
|
-
}
|
|
1517
|
-
return def;
|
|
1518
|
-
}
|
|
1519
|
-
function getDirectiveDef(type) {
|
|
1520
|
-
return type[NG_DIR_DEF] || null;
|
|
1521
|
-
}
|
|
1522
|
-
function getPipeDef(type) {
|
|
1523
|
-
return type[NG_PIPE_DEF] || null;
|
|
1524
|
-
}
|
|
1525
|
-
/**
|
|
1526
|
-
* Checks whether a given Component, Directive or Pipe is marked as standalone.
|
|
1527
|
-
* This will return false if passed anything other than a Component, Directive, or Pipe class
|
|
1528
|
-
* See [this guide](guide/components/importing) for additional information:
|
|
1529
|
-
*
|
|
1530
|
-
* @param type A reference to a Component, Directive or Pipe.
|
|
1531
|
-
* @publicApi
|
|
1532
|
-
*/
|
|
1533
|
-
function isStandalone(type) {
|
|
1534
|
-
const def = getComponentDef(type) || getDirectiveDef(type) || getPipeDef(type);
|
|
1535
|
-
return def !== null && def.standalone;
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
/**
|
|
1539
|
-
* Wrap an array of `Provider`s into `EnvironmentProviders`, preventing them from being accidentally
|
|
1540
|
-
* referenced in `@Component` in a component injector.
|
|
1541
|
-
*
|
|
1542
|
-
* @publicApi
|
|
1543
|
-
*/
|
|
1544
|
-
function makeEnvironmentProviders(providers) {
|
|
1545
|
-
return {
|
|
1546
|
-
ɵproviders: providers,
|
|
1547
|
-
};
|
|
1548
|
-
}
|
|
1549
|
-
/**
|
|
1550
|
-
* @description
|
|
1551
|
-
* This function is used to provide initialization functions that will be executed upon construction
|
|
1552
|
-
* of an environment injector.
|
|
1553
|
-
*
|
|
1554
|
-
* Note that the provided initializer is run in the injection context.
|
|
1555
|
-
*
|
|
1556
|
-
* Previously, this was achieved using the `ENVIRONMENT_INITIALIZER` token which is now deprecated.
|
|
1557
|
-
*
|
|
1558
|
-
* @see {@link ENVIRONMENT_INITIALIZER}
|
|
1559
|
-
*
|
|
1560
|
-
* @usageNotes
|
|
1561
|
-
* The following example illustrates how to configure an initialization function using
|
|
1562
|
-
* `provideEnvironmentInitializer()`
|
|
1563
|
-
* ```ts
|
|
1564
|
-
* createEnvironmentInjector(
|
|
1565
|
-
* [
|
|
1566
|
-
* provideEnvironmentInitializer(() => {
|
|
1567
|
-
* console.log('environment initialized');
|
|
1568
|
-
* }),
|
|
1569
|
-
* ],
|
|
1570
|
-
* parentInjector
|
|
1571
|
-
* );
|
|
1572
|
-
* ```
|
|
1573
|
-
*
|
|
1574
|
-
* @publicApi
|
|
1575
|
-
*/
|
|
1576
|
-
function provideEnvironmentInitializer(initializerFn) {
|
|
1577
|
-
return makeEnvironmentProviders([
|
|
1578
|
-
{
|
|
1579
|
-
provide: ENVIRONMENT_INITIALIZER,
|
|
1580
|
-
multi: true,
|
|
1581
|
-
useValue: initializerFn,
|
|
1582
|
-
},
|
|
1583
|
-
]);
|
|
1584
|
-
}
|
|
1585
|
-
/**
|
|
1586
|
-
* Collects providers from all NgModules and standalone components, including transitively imported
|
|
1587
|
-
* ones.
|
|
1588
|
-
*
|
|
1589
|
-
* Providers extracted via `importProvidersFrom` are only usable in an application injector or
|
|
1590
|
-
* another environment injector (such as a route injector). They should not be used in component
|
|
1591
|
-
* providers.
|
|
1592
|
-
*
|
|
1593
|
-
* More information about standalone components can be found in [this
|
|
1594
|
-
* guide](guide/components/importing).
|
|
1595
|
-
*
|
|
1596
|
-
* @usageNotes
|
|
1597
|
-
* The results of the `importProvidersFrom` call can be used in the `bootstrapApplication` call:
|
|
1598
|
-
*
|
|
1599
|
-
* ```ts
|
|
1600
|
-
* await bootstrapApplication(RootComponent, {
|
|
1601
|
-
* providers: [
|
|
1602
|
-
* importProvidersFrom(NgModuleOne, NgModuleTwo)
|
|
1603
|
-
* ]
|
|
1604
|
-
* });
|
|
1605
|
-
* ```
|
|
1606
|
-
*
|
|
1607
|
-
* You can also use the `importProvidersFrom` results in the `providers` field of a route, when a
|
|
1608
|
-
* standalone component is used:
|
|
1609
|
-
*
|
|
1610
|
-
* ```ts
|
|
1611
|
-
* export const ROUTES: Route[] = [
|
|
1612
|
-
* {
|
|
1613
|
-
* path: 'foo',
|
|
1614
|
-
* providers: [
|
|
1615
|
-
* importProvidersFrom(NgModuleOne, NgModuleTwo)
|
|
1616
|
-
* ],
|
|
1617
|
-
* component: YourStandaloneComponent
|
|
1618
|
-
* }
|
|
1619
|
-
* ];
|
|
1620
|
-
* ```
|
|
1621
|
-
*
|
|
1622
|
-
* @returns Collected providers from the specified list of types.
|
|
1623
|
-
* @publicApi
|
|
1624
|
-
*/
|
|
1625
|
-
function importProvidersFrom(...sources) {
|
|
1626
|
-
return {
|
|
1627
|
-
ɵproviders: internalImportProvidersFrom(true, sources),
|
|
1628
|
-
ɵfromNgModule: true,
|
|
1629
|
-
};
|
|
1630
|
-
}
|
|
1631
|
-
function internalImportProvidersFrom(checkForStandaloneCmp, ...sources) {
|
|
1632
|
-
const providersOut = [];
|
|
1633
|
-
const dedup = new Set(); // already seen types
|
|
1634
|
-
let injectorTypesWithProviders;
|
|
1635
|
-
const collectProviders = (provider) => {
|
|
1636
|
-
providersOut.push(provider);
|
|
1637
|
-
};
|
|
1638
|
-
deepForEach(sources, (source) => {
|
|
1639
|
-
if ((typeof ngDevMode === 'undefined' || ngDevMode) && checkForStandaloneCmp) {
|
|
1640
|
-
const cmpDef = getComponentDef(source);
|
|
1641
|
-
if (cmpDef?.standalone) {
|
|
1642
|
-
throw new RuntimeError(800 /* RuntimeErrorCode.IMPORT_PROVIDERS_FROM_STANDALONE */, `Importing providers supports NgModule or ModuleWithProviders but got a standalone component "${stringifyForError(source)}"`);
|
|
1643
|
-
}
|
|
1644
|
-
}
|
|
1645
|
-
// Narrow `source` to access the internal type analogue for `ModuleWithProviders`.
|
|
1646
|
-
const internalSource = source;
|
|
1647
|
-
if (walkProviderTree(internalSource, collectProviders, [], dedup)) {
|
|
1648
|
-
injectorTypesWithProviders ||= [];
|
|
1649
|
-
injectorTypesWithProviders.push(internalSource);
|
|
1650
|
-
}
|
|
1651
|
-
});
|
|
1652
|
-
// Collect all providers from `ModuleWithProviders` types.
|
|
1653
|
-
if (injectorTypesWithProviders !== undefined) {
|
|
1654
|
-
processInjectorTypesWithProviders(injectorTypesWithProviders, collectProviders);
|
|
1655
|
-
}
|
|
1656
|
-
return providersOut;
|
|
1657
|
-
}
|
|
1658
|
-
/**
|
|
1659
|
-
* Collects all providers from the list of `ModuleWithProviders` and appends them to the provided
|
|
1660
|
-
* array.
|
|
1661
|
-
*/
|
|
1662
|
-
function processInjectorTypesWithProviders(typesWithProviders, visitor) {
|
|
1663
|
-
for (let i = 0; i < typesWithProviders.length; i++) {
|
|
1664
|
-
const { ngModule, providers } = typesWithProviders[i];
|
|
1665
|
-
deepForEachProvider(providers, (provider) => {
|
|
1666
|
-
ngDevMode && validateProvider(provider, providers || EMPTY_ARRAY, ngModule);
|
|
1667
|
-
visitor(provider, ngModule);
|
|
1668
|
-
});
|
|
1669
|
-
}
|
|
1670
|
-
}
|
|
1671
|
-
/**
|
|
1672
|
-
* The logic visits an `InjectorType`, an `InjectorTypeWithProviders`, or a standalone
|
|
1673
|
-
* `ComponentType`, and all of its transitive providers and collects providers.
|
|
1674
|
-
*
|
|
1675
|
-
* If an `InjectorTypeWithProviders` that declares providers besides the type is specified,
|
|
1676
|
-
* the function will return "true" to indicate that the providers of the type definition need
|
|
1677
|
-
* to be processed. This allows us to process providers of injector types after all imports of
|
|
1678
|
-
* an injector definition are processed. (following View Engine semantics: see FW-1349)
|
|
1679
|
-
*/
|
|
1680
|
-
function walkProviderTree(container, visitor, parents, dedup) {
|
|
1681
|
-
container = resolveForwardRef(container);
|
|
1682
|
-
if (!container)
|
|
1683
|
-
return false;
|
|
1684
|
-
// The actual type which had the definition. Usually `container`, but may be an unwrapped type
|
|
1685
|
-
// from `InjectorTypeWithProviders`.
|
|
1686
|
-
let defType = null;
|
|
1687
|
-
let injDef = getInjectorDef(container);
|
|
1688
|
-
const cmpDef = !injDef && getComponentDef(container);
|
|
1689
|
-
if (!injDef && !cmpDef) {
|
|
1690
|
-
// `container` is not an injector type or a component type. It might be:
|
|
1691
|
-
// * An `InjectorTypeWithProviders` that wraps an injector type.
|
|
1692
|
-
// * A standalone directive or pipe that got pulled in from a standalone component's
|
|
1693
|
-
// dependencies.
|
|
1694
|
-
// Try to unwrap it as an `InjectorTypeWithProviders` first.
|
|
1695
|
-
const ngModule = container
|
|
1696
|
-
.ngModule;
|
|
1697
|
-
injDef = getInjectorDef(ngModule);
|
|
1698
|
-
if (injDef) {
|
|
1699
|
-
defType = ngModule;
|
|
1700
|
-
}
|
|
1701
|
-
else {
|
|
1702
|
-
// Not a component or injector type, so ignore it.
|
|
1703
|
-
return false;
|
|
1704
|
-
}
|
|
1705
|
-
}
|
|
1706
|
-
else if (cmpDef && !cmpDef.standalone) {
|
|
1707
|
-
return false;
|
|
1708
|
-
}
|
|
1709
|
-
else {
|
|
1710
|
-
defType = container;
|
|
1711
|
-
}
|
|
1712
|
-
// Check for circular dependencies.
|
|
1713
|
-
if (ngDevMode && parents.indexOf(defType) !== -1) {
|
|
1714
|
-
const defName = stringify(defType);
|
|
1715
|
-
const path = parents.map(stringify).concat(defName);
|
|
1716
|
-
throw cyclicDependencyErrorWithDetails(defName, path);
|
|
1717
|
-
}
|
|
1718
|
-
// Check for multiple imports of the same module
|
|
1719
|
-
const isDuplicate = dedup.has(defType);
|
|
1720
|
-
if (cmpDef) {
|
|
1721
|
-
if (isDuplicate) {
|
|
1722
|
-
// This component definition has already been processed.
|
|
1723
|
-
return false;
|
|
1724
|
-
}
|
|
1725
|
-
dedup.add(defType);
|
|
1726
|
-
if (cmpDef.dependencies) {
|
|
1727
|
-
const deps = typeof cmpDef.dependencies === 'function' ? cmpDef.dependencies() : cmpDef.dependencies;
|
|
1728
|
-
for (const dep of deps) {
|
|
1729
|
-
walkProviderTree(dep, visitor, parents, dedup);
|
|
1730
|
-
}
|
|
1731
|
-
}
|
|
1732
|
-
}
|
|
1733
|
-
else if (injDef) {
|
|
1734
|
-
// First, include providers from any imports.
|
|
1735
|
-
if (injDef.imports != null && !isDuplicate) {
|
|
1736
|
-
// Before processing defType's imports, add it to the set of parents. This way, if it ends
|
|
1737
|
-
// up deeply importing itself, this can be detected.
|
|
1738
|
-
ngDevMode && parents.push(defType);
|
|
1739
|
-
// Add it to the set of dedups. This way we can detect multiple imports of the same module
|
|
1740
|
-
dedup.add(defType);
|
|
1741
|
-
let importTypesWithProviders;
|
|
1742
|
-
try {
|
|
1743
|
-
deepForEach(injDef.imports, (imported) => {
|
|
1744
|
-
if (walkProviderTree(imported, visitor, parents, dedup)) {
|
|
1745
|
-
importTypesWithProviders ||= [];
|
|
1746
|
-
// If the processed import is an injector type with providers, we store it in the
|
|
1747
|
-
// list of import types with providers, so that we can process those afterwards.
|
|
1748
|
-
importTypesWithProviders.push(imported);
|
|
1749
|
-
}
|
|
1750
|
-
});
|
|
1751
|
-
}
|
|
1752
|
-
finally {
|
|
1753
|
-
// Remove it from the parents set when finished.
|
|
1754
|
-
ngDevMode && parents.pop();
|
|
1755
|
-
}
|
|
1756
|
-
// Imports which are declared with providers (TypeWithProviders) need to be processed
|
|
1757
|
-
// after all imported modules are processed. This is similar to how View Engine
|
|
1758
|
-
// processes/merges module imports in the metadata resolver. See: FW-1349.
|
|
1759
|
-
if (importTypesWithProviders !== undefined) {
|
|
1760
|
-
processInjectorTypesWithProviders(importTypesWithProviders, visitor);
|
|
1761
|
-
}
|
|
1762
|
-
}
|
|
1763
|
-
if (!isDuplicate) {
|
|
1764
|
-
// Track the InjectorType and add a provider for it.
|
|
1765
|
-
// It's important that this is done after the def's imports.
|
|
1766
|
-
const factory = getFactoryDef(defType) || (() => new defType());
|
|
1767
|
-
// Append extra providers to make more info available for consumers (to retrieve an injector
|
|
1768
|
-
// type), as well as internally (to calculate an injection scope correctly and eagerly
|
|
1769
|
-
// instantiate a `defType` when an injector is created).
|
|
1770
|
-
// Provider to create `defType` using its factory.
|
|
1771
|
-
visitor({ provide: defType, useFactory: factory, deps: EMPTY_ARRAY }, defType);
|
|
1772
|
-
// Make this `defType` available to an internal logic that calculates injector scope.
|
|
1773
|
-
visitor({ provide: INJECTOR_DEF_TYPES, useValue: defType, multi: true }, defType);
|
|
1774
|
-
// Provider to eagerly instantiate `defType` via `INJECTOR_INITIALIZER`.
|
|
1775
|
-
visitor({ provide: ENVIRONMENT_INITIALIZER, useValue: () => ɵɵinject(defType), multi: true }, defType);
|
|
1776
|
-
}
|
|
1777
|
-
// Next, include providers listed on the definition itself.
|
|
1778
|
-
const defProviders = injDef.providers;
|
|
1779
|
-
if (defProviders != null && !isDuplicate) {
|
|
1780
|
-
const injectorType = container;
|
|
1781
|
-
deepForEachProvider(defProviders, (provider) => {
|
|
1782
|
-
ngDevMode && validateProvider(provider, defProviders, injectorType);
|
|
1783
|
-
visitor(provider, injectorType);
|
|
1784
|
-
});
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
else {
|
|
1788
|
-
// Should not happen, but just in case.
|
|
1789
|
-
return false;
|
|
1790
|
-
}
|
|
1791
|
-
return (defType !== container && container.providers !== undefined);
|
|
1792
|
-
}
|
|
1793
|
-
function validateProvider(provider, providers, containerType) {
|
|
1794
|
-
if (isTypeProvider(provider) ||
|
|
1795
|
-
isValueProvider(provider) ||
|
|
1796
|
-
isFactoryProvider(provider) ||
|
|
1797
|
-
isExistingProvider(provider)) {
|
|
1798
|
-
return;
|
|
1799
|
-
}
|
|
1800
|
-
// Here we expect the provider to be a `useClass` provider (by elimination).
|
|
1801
|
-
const classRef = resolveForwardRef(provider && (provider.useClass || provider.provide));
|
|
1802
|
-
if (!classRef) {
|
|
1803
|
-
throwInvalidProviderError(containerType, providers, provider);
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1806
|
-
function deepForEachProvider(providers, fn) {
|
|
1807
|
-
for (let provider of providers) {
|
|
1808
|
-
if (isEnvironmentProviders(provider)) {
|
|
1809
|
-
provider = provider.ɵproviders;
|
|
1810
|
-
}
|
|
1811
|
-
if (Array.isArray(provider)) {
|
|
1812
|
-
deepForEachProvider(provider, fn);
|
|
1813
|
-
}
|
|
1814
|
-
else {
|
|
1815
|
-
fn(provider);
|
|
1816
|
-
}
|
|
1817
|
-
}
|
|
1818
|
-
}
|
|
1819
|
-
const USE_VALUE = getClosureSafeProperty({
|
|
1820
|
-
provide: String,
|
|
1821
|
-
useValue: getClosureSafeProperty,
|
|
1822
|
-
});
|
|
1823
|
-
function isValueProvider(value) {
|
|
1824
|
-
return value !== null && typeof value == 'object' && USE_VALUE in value;
|
|
1825
|
-
}
|
|
1826
|
-
function isExistingProvider(value) {
|
|
1827
|
-
return !!(value && value.useExisting);
|
|
1828
|
-
}
|
|
1829
|
-
function isFactoryProvider(value) {
|
|
1830
|
-
return !!(value && value.useFactory);
|
|
1831
|
-
}
|
|
1832
|
-
function isTypeProvider(value) {
|
|
1833
|
-
return typeof value === 'function';
|
|
1834
|
-
}
|
|
1835
|
-
function isClassProvider(value) {
|
|
1836
|
-
return !!value.useClass;
|
|
1837
|
-
}
|
|
1838
|
-
|
|
1839
|
-
/**
|
|
1840
|
-
* An internal token whose presence in an injector indicates that the injector should treat itself
|
|
1841
|
-
* as a root scoped injector when processing requests for unknown tokens which may indicate
|
|
1842
|
-
* they are provided in the root scope.
|
|
1843
|
-
*/
|
|
1844
|
-
const INJECTOR_SCOPE = new InjectionToken(typeof ngDevMode !== undefined && ngDevMode ? 'Set Injector scope.' : '');
|
|
1845
|
-
|
|
1846
|
-
/**
|
|
1847
|
-
* Marker which indicates that a value has not yet been created from the factory function.
|
|
1848
|
-
*/
|
|
1849
|
-
const NOT_YET = {};
|
|
1850
|
-
/**
|
|
1851
|
-
* Marker which indicates that the factory function for a token is in the process of being called.
|
|
1852
|
-
*
|
|
1853
|
-
* If the injector is asked to inject a token with its value set to CIRCULAR, that indicates
|
|
1854
|
-
* injection of a dependency has recursively attempted to inject the original token, and there is
|
|
1855
|
-
* a circular dependency among the providers.
|
|
1856
|
-
*/
|
|
1857
|
-
const CIRCULAR = {};
|
|
1858
|
-
/**
|
|
1859
|
-
* A lazily initialized NullInjector.
|
|
1860
|
-
*/
|
|
1861
|
-
let NULL_INJECTOR = undefined;
|
|
1862
|
-
function getNullInjector() {
|
|
1863
|
-
if (NULL_INJECTOR === undefined) {
|
|
1864
|
-
NULL_INJECTOR = new NullInjector();
|
|
1865
|
-
}
|
|
1866
|
-
return NULL_INJECTOR;
|
|
1867
|
-
}
|
|
1868
|
-
/**
|
|
1869
|
-
* An `Injector` that's part of the environment injector hierarchy, which exists outside of the
|
|
1870
|
-
* component tree.
|
|
1871
|
-
*
|
|
1872
|
-
* @publicApi
|
|
1873
|
-
*/
|
|
1874
|
-
class EnvironmentInjector {
|
|
1875
|
-
}
|
|
1876
|
-
class R3Injector extends EnvironmentInjector {
|
|
1877
|
-
parent;
|
|
1878
|
-
source;
|
|
1879
|
-
scopes;
|
|
1880
|
-
/**
|
|
1881
|
-
* Map of tokens to records which contain the instances of those tokens.
|
|
1882
|
-
* - `null` value implies that we don't have the record. Used by tree-shakable injectors
|
|
1883
|
-
* to prevent further searches.
|
|
1884
|
-
*/
|
|
1885
|
-
records = new Map();
|
|
1886
|
-
/**
|
|
1887
|
-
* Set of values instantiated by this injector which contain `ngOnDestroy` lifecycle hooks.
|
|
1888
|
-
*/
|
|
1889
|
-
_ngOnDestroyHooks = new Set();
|
|
1890
|
-
_onDestroyHooks = [];
|
|
1891
|
-
/**
|
|
1892
|
-
* Flag indicating that this injector was previously destroyed.
|
|
1893
|
-
*/
|
|
1894
|
-
get destroyed() {
|
|
1895
|
-
return this._destroyed;
|
|
1896
|
-
}
|
|
1897
|
-
_destroyed = false;
|
|
1898
|
-
injectorDefTypes;
|
|
1899
|
-
constructor(providers, parent, source, scopes) {
|
|
1900
|
-
super();
|
|
1901
|
-
this.parent = parent;
|
|
1902
|
-
this.source = source;
|
|
1903
|
-
this.scopes = scopes;
|
|
1904
|
-
// Start off by creating Records for every provider.
|
|
1905
|
-
forEachSingleProvider(providers, (provider) => this.processProvider(provider));
|
|
1906
|
-
// Make sure the INJECTOR token provides this injector.
|
|
1907
|
-
this.records.set(INJECTOR$1, makeRecord(undefined, this));
|
|
1908
|
-
// And `EnvironmentInjector` if the current injector is supposed to be env-scoped.
|
|
1909
|
-
if (scopes.has('environment')) {
|
|
1910
|
-
this.records.set(EnvironmentInjector, makeRecord(undefined, this));
|
|
1911
|
-
}
|
|
1912
|
-
// Detect whether this injector has the APP_ROOT_SCOPE token and thus should provide
|
|
1913
|
-
// any injectable scoped to APP_ROOT_SCOPE.
|
|
1914
|
-
const record = this.records.get(INJECTOR_SCOPE);
|
|
1915
|
-
if (record != null && typeof record.value === 'string') {
|
|
1916
|
-
this.scopes.add(record.value);
|
|
1917
|
-
}
|
|
1918
|
-
this.injectorDefTypes = new Set(this.get(INJECTOR_DEF_TYPES, EMPTY_ARRAY, { self: true }));
|
|
1919
|
-
}
|
|
1920
|
-
retrieve(token, options) {
|
|
1921
|
-
const flags = convertToBitFlags(options) || 0 /* InternalInjectFlags.Default */;
|
|
1922
|
-
try {
|
|
1923
|
-
return this.get(token,
|
|
1924
|
-
// When a dependency is requested with an optional flag, DI returns null as the default value.
|
|
1925
|
-
THROW_IF_NOT_FOUND, flags);
|
|
1926
|
-
}
|
|
1927
|
-
catch (e) {
|
|
1928
|
-
if (isNotFound$1(e)) {
|
|
1929
|
-
return e;
|
|
1930
|
-
}
|
|
1931
|
-
throw e;
|
|
1932
|
-
}
|
|
1933
|
-
}
|
|
1934
|
-
/**
|
|
1935
|
-
* Destroy the injector and release references to every instance or provider associated with it.
|
|
1936
|
-
*
|
|
1937
|
-
* Also calls the `OnDestroy` lifecycle hooks of every instance that was created for which a
|
|
1938
|
-
* hook was found.
|
|
1939
|
-
*/
|
|
1940
|
-
destroy() {
|
|
1941
|
-
assertNotDestroyed(this);
|
|
1942
|
-
// Set destroyed = true first, in case lifecycle hooks re-enter destroy().
|
|
1943
|
-
this._destroyed = true;
|
|
1944
|
-
const prevConsumer = setActiveConsumer(null);
|
|
1945
|
-
try {
|
|
1946
|
-
// Call all the lifecycle hooks.
|
|
1947
|
-
for (const service of this._ngOnDestroyHooks) {
|
|
1948
|
-
service.ngOnDestroy();
|
|
1949
|
-
}
|
|
1950
|
-
const onDestroyHooks = this._onDestroyHooks;
|
|
1951
|
-
// Reset the _onDestroyHooks array before iterating over it to prevent hooks that unregister
|
|
1952
|
-
// themselves from mutating the array during iteration.
|
|
1953
|
-
this._onDestroyHooks = [];
|
|
1954
|
-
for (const hook of onDestroyHooks) {
|
|
1955
|
-
hook();
|
|
1956
|
-
}
|
|
1957
|
-
}
|
|
1958
|
-
finally {
|
|
1959
|
-
// Release all references.
|
|
1960
|
-
this.records.clear();
|
|
1961
|
-
this._ngOnDestroyHooks.clear();
|
|
1962
|
-
this.injectorDefTypes.clear();
|
|
1963
|
-
setActiveConsumer(prevConsumer);
|
|
1964
|
-
}
|
|
1965
|
-
}
|
|
1966
|
-
onDestroy(callback) {
|
|
1967
|
-
assertNotDestroyed(this);
|
|
1968
|
-
this._onDestroyHooks.push(callback);
|
|
1969
|
-
return () => this.removeOnDestroy(callback);
|
|
1970
|
-
}
|
|
1971
|
-
runInContext(fn) {
|
|
1972
|
-
assertNotDestroyed(this);
|
|
1973
|
-
const previousInjector = setCurrentInjector(this);
|
|
1974
|
-
const previousInjectImplementation = setInjectImplementation(undefined);
|
|
1975
|
-
let prevInjectContext;
|
|
1976
|
-
if (ngDevMode) {
|
|
1977
|
-
prevInjectContext = setInjectorProfilerContext({ injector: this, token: null });
|
|
1978
|
-
}
|
|
1979
|
-
try {
|
|
1980
|
-
return fn();
|
|
1981
|
-
}
|
|
1982
|
-
finally {
|
|
1983
|
-
setCurrentInjector(previousInjector);
|
|
1984
|
-
setInjectImplementation(previousInjectImplementation);
|
|
1985
|
-
ngDevMode && setInjectorProfilerContext(prevInjectContext);
|
|
1986
|
-
}
|
|
1987
|
-
}
|
|
1988
|
-
get(token, notFoundValue = THROW_IF_NOT_FOUND, options) {
|
|
1989
|
-
assertNotDestroyed(this);
|
|
1990
|
-
if (token.hasOwnProperty(NG_ENV_ID)) {
|
|
1991
|
-
return token[NG_ENV_ID](this);
|
|
1992
|
-
}
|
|
1993
|
-
const flags = convertToBitFlags(options);
|
|
1994
|
-
// Set the injection context.
|
|
1995
|
-
let prevInjectContext;
|
|
1996
|
-
if (ngDevMode) {
|
|
1997
|
-
prevInjectContext = setInjectorProfilerContext({ injector: this, token: token });
|
|
1998
|
-
}
|
|
1999
|
-
const previousInjector = setCurrentInjector(this);
|
|
2000
|
-
const previousInjectImplementation = setInjectImplementation(undefined);
|
|
2001
|
-
try {
|
|
2002
|
-
// Check for the SkipSelf flag.
|
|
2003
|
-
if (!(flags & 4 /* InternalInjectFlags.SkipSelf */)) {
|
|
2004
|
-
// SkipSelf isn't set, check if the record belongs to this injector.
|
|
2005
|
-
let record = this.records.get(token);
|
|
2006
|
-
if (record === undefined) {
|
|
2007
|
-
// No record, but maybe the token is scoped to this injector. Look for an injectable
|
|
2008
|
-
// def with a scope matching this injector.
|
|
2009
|
-
const def = couldBeInjectableType(token) && getInjectableDef(token);
|
|
2010
|
-
if (def && this.injectableDefInScope(def)) {
|
|
2011
|
-
// Found an injectable def and it's scoped to this injector. Pretend as if it was here
|
|
2012
|
-
// all along.
|
|
2013
|
-
if (ngDevMode) {
|
|
2014
|
-
runInInjectorProfilerContext(this, token, () => {
|
|
2015
|
-
emitProviderConfiguredEvent(token);
|
|
2016
|
-
});
|
|
2017
|
-
}
|
|
2018
|
-
record = makeRecord(injectableDefOrInjectorDefFactory(token), NOT_YET);
|
|
2019
|
-
}
|
|
2020
|
-
else {
|
|
2021
|
-
record = null;
|
|
2022
|
-
}
|
|
2023
|
-
this.records.set(token, record);
|
|
2024
|
-
}
|
|
2025
|
-
// If a record was found, get the instance for it and return it.
|
|
2026
|
-
if (record != null /* NOT null || undefined */) {
|
|
2027
|
-
return this.hydrate(token, record, flags);
|
|
2028
|
-
}
|
|
2029
|
-
}
|
|
2030
|
-
// Select the next injector based on the Self flag - if self is set, the next injector is
|
|
2031
|
-
// the NullInjector, otherwise it's the parent.
|
|
2032
|
-
const nextInjector = !(flags & 2 /* InternalInjectFlags.Self */) ? this.parent : getNullInjector();
|
|
2033
|
-
// Set the notFoundValue based on the Optional flag - if optional is set and notFoundValue
|
|
2034
|
-
// is undefined, the value is null, otherwise it's the notFoundValue.
|
|
2035
|
-
notFoundValue =
|
|
2036
|
-
flags & 8 /* InternalInjectFlags.Optional */ && notFoundValue === THROW_IF_NOT_FOUND
|
|
2037
|
-
? null
|
|
2038
|
-
: notFoundValue;
|
|
2039
|
-
return nextInjector.get(token, notFoundValue);
|
|
2040
|
-
}
|
|
2041
|
-
catch (error) {
|
|
2042
|
-
// If there was a cyclic dependency error or a token was not found,
|
|
2043
|
-
// an error is thrown at the level where the problem was detected.
|
|
2044
|
-
// The error propagates up the call stack and the code below appends
|
|
2045
|
-
// the current token into the path. As a result, the full path is assembled
|
|
2046
|
-
// at the very top of the call stack, so the final error message can be
|
|
2047
|
-
// formatted to include that path.
|
|
2048
|
-
const errorCode = getRuntimeErrorCode(error);
|
|
2049
|
-
if (errorCode === -200 /* RuntimeErrorCode.CYCLIC_DI_DEPENDENCY */ ||
|
|
2050
|
-
errorCode === -201 /* RuntimeErrorCode.PROVIDER_NOT_FOUND */) {
|
|
2051
|
-
// Note: we use `if (ngDevMode) { ... }` instead of an early return.
|
|
2052
|
-
// ESBuild is conservative about removing dead code that follows `return;`
|
|
2053
|
-
// inside a function body, so the block may remain in the bundle.
|
|
2054
|
-
// Using a conditional ensures the dev-only logic is reliably tree-shaken
|
|
2055
|
-
// in production builds.
|
|
2056
|
-
if (ngDevMode) {
|
|
2057
|
-
prependTokenToDependencyPath(error, token);
|
|
2058
|
-
if (previousInjector) {
|
|
2059
|
-
// We still have a parent injector, keep throwing
|
|
2060
|
-
throw error;
|
|
2061
|
-
}
|
|
2062
|
-
else {
|
|
2063
|
-
// Format & throw the final error message when we don't have any previous injector
|
|
2064
|
-
throw augmentRuntimeError(error, this.source);
|
|
2065
|
-
}
|
|
2066
|
-
}
|
|
2067
|
-
else {
|
|
2068
|
-
throw new RuntimeError(errorCode, null);
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
else {
|
|
2072
|
-
throw error;
|
|
2073
|
-
}
|
|
2074
|
-
}
|
|
2075
|
-
finally {
|
|
2076
|
-
// Lastly, restore the previous injection context.
|
|
2077
|
-
setInjectImplementation(previousInjectImplementation);
|
|
2078
|
-
setCurrentInjector(previousInjector);
|
|
2079
|
-
ngDevMode && setInjectorProfilerContext(prevInjectContext);
|
|
2080
|
-
}
|
|
2081
|
-
}
|
|
2082
|
-
/** @internal */
|
|
2083
|
-
resolveInjectorInitializers() {
|
|
2084
|
-
const prevConsumer = setActiveConsumer(null);
|
|
2085
|
-
const previousInjector = setCurrentInjector(this);
|
|
2086
|
-
const previousInjectImplementation = setInjectImplementation(undefined);
|
|
2087
|
-
let prevInjectContext;
|
|
2088
|
-
if (ngDevMode) {
|
|
2089
|
-
prevInjectContext = setInjectorProfilerContext({ injector: this, token: null });
|
|
2090
|
-
}
|
|
2091
|
-
try {
|
|
2092
|
-
const initializers = this.get(ENVIRONMENT_INITIALIZER, EMPTY_ARRAY, { self: true });
|
|
2093
|
-
if (ngDevMode && !Array.isArray(initializers)) {
|
|
2094
|
-
throw new RuntimeError(-209 /* RuntimeErrorCode.INVALID_MULTI_PROVIDER */, 'Unexpected type of the `ENVIRONMENT_INITIALIZER` token value ' +
|
|
2095
|
-
`(expected an array, but got ${typeof initializers}). ` +
|
|
2096
|
-
'Please check that the `ENVIRONMENT_INITIALIZER` token is configured as a ' +
|
|
2097
|
-
'`multi: true` provider.');
|
|
2098
|
-
}
|
|
2099
|
-
for (const initializer of initializers) {
|
|
2100
|
-
initializer();
|
|
2101
|
-
}
|
|
2102
|
-
}
|
|
2103
|
-
finally {
|
|
2104
|
-
setCurrentInjector(previousInjector);
|
|
2105
|
-
setInjectImplementation(previousInjectImplementation);
|
|
2106
|
-
ngDevMode && setInjectorProfilerContext(prevInjectContext);
|
|
2107
|
-
setActiveConsumer(prevConsumer);
|
|
2108
|
-
}
|
|
2109
|
-
}
|
|
2110
|
-
toString() {
|
|
2111
|
-
const tokens = [];
|
|
2112
|
-
const records = this.records;
|
|
2113
|
-
for (const token of records.keys()) {
|
|
2114
|
-
tokens.push(stringify(token));
|
|
2115
|
-
}
|
|
2116
|
-
return `R3Injector[${tokens.join(', ')}]`;
|
|
2117
|
-
}
|
|
2118
|
-
/**
|
|
2119
|
-
* Process a `SingleProvider` and add it.
|
|
2120
|
-
*/
|
|
2121
|
-
processProvider(provider) {
|
|
2122
|
-
// Determine the token from the provider. Either it's its own token, or has a {provide: ...}
|
|
2123
|
-
// property.
|
|
2124
|
-
provider = resolveForwardRef(provider);
|
|
2125
|
-
let token = isTypeProvider(provider)
|
|
2126
|
-
? provider
|
|
2127
|
-
: resolveForwardRef(provider && provider.provide);
|
|
2128
|
-
// Construct a `Record` for the provider.
|
|
2129
|
-
const record = providerToRecord(provider);
|
|
2130
|
-
if (ngDevMode) {
|
|
2131
|
-
runInInjectorProfilerContext(this, token, () => {
|
|
2132
|
-
// Emit InjectorProfilerEventType.Create if provider is a value provider because
|
|
2133
|
-
// these are the only providers that do not go through the value hydration logic
|
|
2134
|
-
// where this event would normally be emitted from.
|
|
2135
|
-
if (isValueProvider(provider)) {
|
|
2136
|
-
emitInjectorToCreateInstanceEvent(token);
|
|
2137
|
-
emitInstanceCreatedByInjectorEvent(provider.useValue);
|
|
2138
|
-
}
|
|
2139
|
-
emitProviderConfiguredEvent(provider);
|
|
2140
|
-
});
|
|
2141
|
-
}
|
|
2142
|
-
if (!isTypeProvider(provider) && provider.multi === true) {
|
|
2143
|
-
// If the provider indicates that it's a multi-provider, process it specially.
|
|
2144
|
-
// First check whether it's been defined already.
|
|
2145
|
-
let multiRecord = this.records.get(token);
|
|
2146
|
-
if (multiRecord) {
|
|
2147
|
-
// It has. Throw a nice error if
|
|
2148
|
-
if (ngDevMode && multiRecord.multi === undefined) {
|
|
2149
|
-
throwMixedMultiProviderError();
|
|
2150
|
-
}
|
|
2151
|
-
}
|
|
2152
|
-
else {
|
|
2153
|
-
multiRecord = makeRecord(undefined, NOT_YET, true);
|
|
2154
|
-
multiRecord.factory = () => injectArgs(multiRecord.multi);
|
|
2155
|
-
this.records.set(token, multiRecord);
|
|
2156
|
-
}
|
|
2157
|
-
token = provider;
|
|
2158
|
-
multiRecord.multi.push(provider);
|
|
2159
|
-
}
|
|
2160
|
-
else {
|
|
2161
|
-
if (ngDevMode) {
|
|
2162
|
-
const existing = this.records.get(token);
|
|
2163
|
-
if (existing && existing.multi !== undefined) {
|
|
2164
|
-
throwMixedMultiProviderError();
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
2167
|
-
}
|
|
2168
|
-
this.records.set(token, record);
|
|
2169
|
-
}
|
|
2170
|
-
hydrate(token, record, flags) {
|
|
2171
|
-
const prevConsumer = setActiveConsumer(null);
|
|
2172
|
-
try {
|
|
2173
|
-
if (record.value === CIRCULAR) {
|
|
2174
|
-
throw cyclicDependencyError(stringify(token));
|
|
2175
|
-
}
|
|
2176
|
-
else if (record.value === NOT_YET) {
|
|
2177
|
-
record.value = CIRCULAR;
|
|
2178
|
-
if (ngDevMode) {
|
|
2179
|
-
runInInjectorProfilerContext(this, token, () => {
|
|
2180
|
-
emitInjectorToCreateInstanceEvent(token);
|
|
2181
|
-
record.value = record.factory(undefined, flags);
|
|
2182
|
-
emitInstanceCreatedByInjectorEvent(record.value);
|
|
2183
|
-
});
|
|
2184
|
-
}
|
|
2185
|
-
else {
|
|
2186
|
-
record.value = record.factory(undefined, flags);
|
|
2187
|
-
}
|
|
2188
|
-
}
|
|
2189
|
-
if (typeof record.value === 'object' && record.value && hasOnDestroy(record.value)) {
|
|
2190
|
-
this._ngOnDestroyHooks.add(record.value);
|
|
2191
|
-
}
|
|
2192
|
-
return record.value;
|
|
2193
|
-
}
|
|
2194
|
-
finally {
|
|
2195
|
-
setActiveConsumer(prevConsumer);
|
|
2196
|
-
}
|
|
2197
|
-
}
|
|
2198
|
-
injectableDefInScope(def) {
|
|
2199
|
-
if (!def.providedIn) {
|
|
2200
|
-
return false;
|
|
2201
|
-
}
|
|
2202
|
-
const providedIn = resolveForwardRef(def.providedIn);
|
|
2203
|
-
if (typeof providedIn === 'string') {
|
|
2204
|
-
return providedIn === 'any' || this.scopes.has(providedIn);
|
|
2205
|
-
}
|
|
2206
|
-
else {
|
|
2207
|
-
return this.injectorDefTypes.has(providedIn);
|
|
2208
|
-
}
|
|
2209
|
-
}
|
|
2210
|
-
removeOnDestroy(callback) {
|
|
2211
|
-
const destroyCBIdx = this._onDestroyHooks.indexOf(callback);
|
|
2212
|
-
if (destroyCBIdx !== -1) {
|
|
2213
|
-
this._onDestroyHooks.splice(destroyCBIdx, 1);
|
|
2214
|
-
}
|
|
2215
|
-
}
|
|
2216
|
-
}
|
|
2217
|
-
function injectableDefOrInjectorDefFactory(token) {
|
|
2218
|
-
// Most tokens will have an injectable def directly on them, which specifies a factory directly.
|
|
2219
|
-
const injectableDef = getInjectableDef(token);
|
|
2220
|
-
const factory = injectableDef !== null ? injectableDef.factory : getFactoryDef(token);
|
|
2221
|
-
if (factory !== null) {
|
|
2222
|
-
return factory;
|
|
2223
|
-
}
|
|
2224
|
-
// InjectionTokens should have an injectable def (ɵprov) and thus should be handled above.
|
|
2225
|
-
// If it's missing that, it's an error.
|
|
2226
|
-
if (token instanceof InjectionToken) {
|
|
2227
|
-
throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Token ${stringify(token)} is missing a ɵprov definition.`);
|
|
2228
|
-
}
|
|
2229
|
-
// Undecorated types can sometimes be created if they have no constructor arguments.
|
|
2230
|
-
if (token instanceof Function) {
|
|
2231
|
-
return getUndecoratedInjectableFactory(token);
|
|
2232
|
-
}
|
|
2233
|
-
// There was no way to resolve a factory for this token.
|
|
2234
|
-
throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && 'unreachable');
|
|
2235
|
-
}
|
|
2236
|
-
function getUndecoratedInjectableFactory(token) {
|
|
2237
|
-
// If the token has parameters then it has dependencies that we cannot resolve implicitly.
|
|
2238
|
-
const paramLength = token.length;
|
|
2239
|
-
if (paramLength > 0) {
|
|
2240
|
-
throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode &&
|
|
2241
|
-
`Can't resolve all parameters for ${stringify(token)}: (${newArray(paramLength, '?').join(', ')}).`);
|
|
2242
|
-
}
|
|
2243
|
-
// The constructor function appears to have no parameters.
|
|
2244
|
-
// This might be because it inherits from a super-class. In which case, use an injectable
|
|
2245
|
-
// def from an ancestor if there is one.
|
|
2246
|
-
// Otherwise this really is a simple class with no dependencies, so return a factory that
|
|
2247
|
-
// just instantiates the zero-arg constructor.
|
|
2248
|
-
const inheritedInjectableDef = getInheritedInjectableDef(token);
|
|
2249
|
-
if (inheritedInjectableDef !== null) {
|
|
2250
|
-
return () => inheritedInjectableDef.factory(token);
|
|
2251
|
-
}
|
|
2252
|
-
else {
|
|
2253
|
-
return () => new token();
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
|
-
function providerToRecord(provider) {
|
|
2257
|
-
if (isValueProvider(provider)) {
|
|
2258
|
-
return makeRecord(undefined, provider.useValue);
|
|
2259
|
-
}
|
|
2260
|
-
else {
|
|
2261
|
-
const factory = providerToFactory(provider);
|
|
2262
|
-
return makeRecord(factory, NOT_YET);
|
|
2263
|
-
}
|
|
2264
|
-
}
|
|
2265
|
-
/**
|
|
2266
|
-
* Converts a `SingleProvider` into a factory function.
|
|
2267
|
-
*
|
|
2268
|
-
* @param provider provider to convert to factory
|
|
2269
|
-
*/
|
|
2270
|
-
function providerToFactory(provider, ngModuleType, providers) {
|
|
2271
|
-
let factory = undefined;
|
|
2272
|
-
if (ngDevMode && isEnvironmentProviders(provider)) {
|
|
2273
|
-
throwInvalidProviderError(undefined, providers, provider);
|
|
2274
|
-
}
|
|
2275
|
-
if (isTypeProvider(provider)) {
|
|
2276
|
-
const unwrappedProvider = resolveForwardRef(provider);
|
|
2277
|
-
return getFactoryDef(unwrappedProvider) || injectableDefOrInjectorDefFactory(unwrappedProvider);
|
|
2278
|
-
}
|
|
2279
|
-
else {
|
|
2280
|
-
if (isValueProvider(provider)) {
|
|
2281
|
-
factory = () => resolveForwardRef(provider.useValue);
|
|
2282
|
-
}
|
|
2283
|
-
else if (isFactoryProvider(provider)) {
|
|
2284
|
-
factory = () => provider.useFactory(...injectArgs(provider.deps || []));
|
|
2285
|
-
}
|
|
2286
|
-
else if (isExistingProvider(provider)) {
|
|
2287
|
-
factory = (_, flags) => ɵɵinject(resolveForwardRef(provider.useExisting), flags !== undefined && flags & 8 /* InternalInjectFlags.Optional */
|
|
2288
|
-
? 8 /* InternalInjectFlags.Optional */
|
|
2289
|
-
: undefined);
|
|
2290
|
-
}
|
|
2291
|
-
else {
|
|
2292
|
-
const classRef = resolveForwardRef(provider &&
|
|
2293
|
-
(provider.useClass || provider.provide));
|
|
2294
|
-
if (ngDevMode && !classRef) {
|
|
2295
|
-
throwInvalidProviderError(ngModuleType, providers, provider);
|
|
2296
|
-
}
|
|
2297
|
-
if (hasDeps(provider)) {
|
|
2298
|
-
factory = () => new classRef(...injectArgs(provider.deps));
|
|
2299
|
-
}
|
|
2300
|
-
else {
|
|
2301
|
-
return getFactoryDef(classRef) || injectableDefOrInjectorDefFactory(classRef);
|
|
2302
|
-
}
|
|
2303
|
-
}
|
|
2304
|
-
}
|
|
2305
|
-
return factory;
|
|
2306
|
-
}
|
|
2307
|
-
function assertNotDestroyed(injector) {
|
|
2308
|
-
if (injector.destroyed) {
|
|
2309
|
-
throw new RuntimeError(205 /* RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED */, ngDevMode && 'Injector has already been destroyed.');
|
|
2310
|
-
}
|
|
2311
|
-
}
|
|
2312
|
-
function makeRecord(factory, value, multi = false) {
|
|
2313
|
-
return {
|
|
2314
|
-
factory: factory,
|
|
2315
|
-
value: value,
|
|
2316
|
-
multi: multi ? [] : undefined,
|
|
2317
|
-
};
|
|
2318
|
-
}
|
|
2319
|
-
function hasDeps(value) {
|
|
2320
|
-
return !!value.deps;
|
|
2321
|
-
}
|
|
2322
|
-
function hasOnDestroy(value) {
|
|
2323
|
-
return (value !== null &&
|
|
2324
|
-
typeof value === 'object' &&
|
|
2325
|
-
typeof value.ngOnDestroy === 'function');
|
|
2326
|
-
}
|
|
2327
|
-
function couldBeInjectableType(value) {
|
|
2328
|
-
return (typeof value === 'function' ||
|
|
2329
|
-
(typeof value === 'object' && value.ngMetadataName === 'InjectionToken'));
|
|
2330
|
-
}
|
|
2331
|
-
function forEachSingleProvider(providers, fn) {
|
|
2332
|
-
for (const provider of providers) {
|
|
2333
|
-
if (Array.isArray(provider)) {
|
|
2334
|
-
forEachSingleProvider(provider, fn);
|
|
2335
|
-
}
|
|
2336
|
-
else if (provider && isEnvironmentProviders(provider)) {
|
|
2337
|
-
forEachSingleProvider(provider.ɵproviders, fn);
|
|
2338
|
-
}
|
|
2339
|
-
else {
|
|
2340
|
-
fn(provider);
|
|
2341
|
-
}
|
|
2342
|
-
}
|
|
2343
|
-
}
|
|
2344
|
-
|
|
2345
|
-
/**
|
|
2346
|
-
* Runs the given function in the [context](guide/di/dependency-injection-context) of the given
|
|
2347
|
-
* `Injector`.
|
|
2348
|
-
*
|
|
2349
|
-
* Within the function's stack frame, [`inject`](api/core/inject) can be used to inject dependencies
|
|
2350
|
-
* from the given `Injector`. Note that `inject` is only usable synchronously, and cannot be used in
|
|
2351
|
-
* any asynchronous callbacks or after any `await` points.
|
|
2352
|
-
*
|
|
2353
|
-
* @param injector the injector which will satisfy calls to [`inject`](api/core/inject) while `fn`
|
|
2354
|
-
* is executing
|
|
2355
|
-
* @param fn the closure to be run in the context of `injector`
|
|
2356
|
-
* @returns the return value of the function, if any
|
|
2357
|
-
* @publicApi
|
|
2358
|
-
*/
|
|
2359
|
-
function runInInjectionContext(injector, fn) {
|
|
2360
|
-
let internalInjector;
|
|
2361
|
-
if (injector instanceof R3Injector) {
|
|
2362
|
-
assertNotDestroyed(injector);
|
|
2363
|
-
internalInjector = injector;
|
|
2364
|
-
}
|
|
2365
|
-
else {
|
|
2366
|
-
internalInjector = new RetrievingInjector(injector);
|
|
2367
|
-
}
|
|
2368
|
-
let prevInjectorProfilerContext;
|
|
2369
|
-
if (ngDevMode) {
|
|
2370
|
-
prevInjectorProfilerContext = setInjectorProfilerContext({ injector, token: null });
|
|
2371
|
-
}
|
|
2372
|
-
const prevInjector = setCurrentInjector(internalInjector);
|
|
2373
|
-
const previousInjectImplementation = setInjectImplementation(undefined);
|
|
2374
|
-
try {
|
|
2375
|
-
return fn();
|
|
2376
|
-
}
|
|
2377
|
-
finally {
|
|
2378
|
-
setCurrentInjector(prevInjector);
|
|
2379
|
-
ngDevMode && setInjectorProfilerContext(prevInjectorProfilerContext);
|
|
2380
|
-
setInjectImplementation(previousInjectImplementation);
|
|
2381
|
-
}
|
|
2382
|
-
}
|
|
2383
|
-
/**
|
|
2384
|
-
* Whether the current stack frame is inside an injection context.
|
|
2385
|
-
*/
|
|
2386
|
-
function isInInjectionContext() {
|
|
2387
|
-
return getInjectImplementation() !== undefined || getCurrentInjector() != null;
|
|
2388
|
-
}
|
|
2389
|
-
/**
|
|
2390
|
-
* Asserts that the current stack frame is within an [injection
|
|
2391
|
-
* context](guide/di/dependency-injection-context) and has access to `inject`.
|
|
2392
|
-
*
|
|
2393
|
-
* @param debugFn a reference to the function making the assertion (used for the error message).
|
|
2394
|
-
*
|
|
2395
|
-
* @see [Asserts the context](guide/di/dependency-injection-context#asserts-the-context)
|
|
2396
|
-
*
|
|
2397
|
-
* @publicApi
|
|
2398
|
-
*/
|
|
2399
|
-
function assertInInjectionContext(debugFn) {
|
|
2400
|
-
// Taking a `Function` instead of a string name here prevents the unminified name of the function
|
|
2401
|
-
// from being retained in the bundle regardless of minification.
|
|
2402
|
-
if (!isInInjectionContext()) {
|
|
2403
|
-
throw new RuntimeError(-203 /* RuntimeErrorCode.MISSING_INJECTION_CONTEXT */, ngDevMode &&
|
|
2404
|
-
debugFn.name +
|
|
2405
|
-
'() can only be used within an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext`');
|
|
2406
|
-
}
|
|
2407
|
-
}
|
|
2408
|
-
|
|
2409
|
-
// Below are constants for LView indices to help us look up LView members
|
|
2410
|
-
// without having to remember the specific indices.
|
|
2411
|
-
// Uglify will inline these when minifying so there shouldn't be a cost.
|
|
2412
|
-
const HOST = 0;
|
|
2413
|
-
const TVIEW = 1;
|
|
2414
|
-
// Shared with LContainer
|
|
2415
|
-
const FLAGS = 2;
|
|
2416
|
-
const PARENT = 3;
|
|
2417
|
-
const NEXT = 4;
|
|
2418
|
-
const T_HOST = 5;
|
|
2419
|
-
// End shared with LContainer
|
|
2420
|
-
const HYDRATION = 6;
|
|
2421
|
-
const CLEANUP = 7;
|
|
2422
|
-
const CONTEXT = 8;
|
|
2423
|
-
const INJECTOR = 9;
|
|
2424
|
-
const ENVIRONMENT = 10;
|
|
2425
|
-
const RENDERER = 11;
|
|
2426
|
-
const CHILD_HEAD = 12;
|
|
2427
|
-
const CHILD_TAIL = 13;
|
|
2428
|
-
// FIXME(misko): Investigate if the three declarations aren't all same thing.
|
|
2429
|
-
const DECLARATION_VIEW = 14;
|
|
2430
|
-
const DECLARATION_COMPONENT_VIEW = 15;
|
|
2431
|
-
const DECLARATION_LCONTAINER = 16;
|
|
2432
|
-
const PREORDER_HOOK_FLAGS = 17;
|
|
2433
|
-
const QUERIES = 18;
|
|
2434
|
-
const ID = 19;
|
|
2435
|
-
const EMBEDDED_VIEW_INJECTOR = 20;
|
|
2436
|
-
const ON_DESTROY_HOOKS = 21;
|
|
2437
|
-
const EFFECTS_TO_SCHEDULE = 22;
|
|
2438
|
-
const EFFECTS = 23;
|
|
2439
|
-
const REACTIVE_TEMPLATE_CONSUMER = 24;
|
|
2440
|
-
const AFTER_RENDER_SEQUENCES_TO_ADD = 25;
|
|
2441
|
-
const ANIMATIONS = 26;
|
|
2442
|
-
/**
|
|
2443
|
-
* Size of LView's header. Necessary to adjust for it when setting slots.
|
|
2444
|
-
*
|
|
2445
|
-
* IMPORTANT: `HEADER_OFFSET` should only be referred to the in the `ɵɵ*` instructions to translate
|
|
2446
|
-
* instruction index into `LView` index. All other indexes should be in the `LView` index space and
|
|
2447
|
-
* there should be no need to refer to `HEADER_OFFSET` anywhere else.
|
|
2448
|
-
*/
|
|
2449
|
-
const HEADER_OFFSET = 27;
|
|
2450
|
-
|
|
2451
|
-
/**
|
|
2452
|
-
* Special location which allows easy identification of type. If we have an array which was
|
|
2453
|
-
* retrieved from the `LView` and that array has `true` at `TYPE` location, we know it is
|
|
2454
|
-
* `LContainer`.
|
|
2455
|
-
*/
|
|
2456
|
-
const TYPE = 1;
|
|
2457
|
-
/**
|
|
2458
|
-
* Below are constants for LContainer indices to help us look up LContainer members
|
|
2459
|
-
* without having to remember the specific indices.
|
|
2460
|
-
* Uglify will inline these when minifying so there shouldn't be a cost.
|
|
2461
|
-
*/
|
|
2462
|
-
// FLAGS, PARENT, NEXT, and T_HOST are indices 2, 3, 4, and 5
|
|
2463
|
-
// As we already have these constants in LView, we don't need to re-create them.
|
|
2464
|
-
const DEHYDRATED_VIEWS = 6;
|
|
2465
|
-
const NATIVE = 7;
|
|
2466
|
-
const VIEW_REFS = 8;
|
|
2467
|
-
const MOVED_VIEWS = 9;
|
|
2468
|
-
/**
|
|
2469
|
-
* Size of LContainer's header. Represents the index after which all views in the
|
|
2470
|
-
* container will be inserted. We need to keep a record of current views so we know
|
|
2471
|
-
* which views are already in the DOM (and don't need to be re-added) and so we can
|
|
2472
|
-
* remove views from the DOM when they are no longer required.
|
|
2473
|
-
*/
|
|
2474
|
-
const CONTAINER_HEADER_OFFSET = 10;
|
|
2475
|
-
|
|
2476
|
-
/**
|
|
2477
|
-
* True if `value` is `LView`.
|
|
2478
|
-
* @param value wrapped value of `RNode`, `LView`, `LContainer`
|
|
2479
|
-
*/
|
|
2480
|
-
function isLView(value) {
|
|
2481
|
-
return Array.isArray(value) && typeof value[TYPE] === 'object';
|
|
2482
|
-
}
|
|
2483
|
-
/**
|
|
2484
|
-
* True if `value` is `LContainer`.
|
|
2485
|
-
* @param value wrapped value of `RNode`, `LView`, `LContainer`
|
|
2486
|
-
*/
|
|
2487
|
-
function isLContainer(value) {
|
|
2488
|
-
return Array.isArray(value) && value[TYPE] === true;
|
|
2489
|
-
}
|
|
2490
|
-
function isContentQueryHost(tNode) {
|
|
2491
|
-
return (tNode.flags & 4 /* TNodeFlags.hasContentQuery */) !== 0;
|
|
2492
|
-
}
|
|
2493
|
-
function isComponentHost(tNode) {
|
|
2494
|
-
return tNode.componentOffset > -1;
|
|
2495
|
-
}
|
|
2496
|
-
function isDirectiveHost(tNode) {
|
|
2497
|
-
return (tNode.flags & 1 /* TNodeFlags.isDirectiveHost */) === 1 /* TNodeFlags.isDirectiveHost */;
|
|
2498
|
-
}
|
|
2499
|
-
function isComponentDef(def) {
|
|
2500
|
-
return !!def.template;
|
|
2501
|
-
}
|
|
2502
|
-
function isRootView(target) {
|
|
2503
|
-
// Determines whether a given LView is marked as a root view.
|
|
2504
|
-
return (target[FLAGS] & 512 /* LViewFlags.IsRoot */) !== 0;
|
|
2505
|
-
}
|
|
2506
|
-
function isProjectionTNode(tNode) {
|
|
2507
|
-
return (tNode.type & 16 /* TNodeType.Projection */) === 16 /* TNodeType.Projection */;
|
|
2508
|
-
}
|
|
2509
|
-
function hasI18n(lView) {
|
|
2510
|
-
return (lView[FLAGS] & 32 /* LViewFlags.HasI18n */) === 32 /* LViewFlags.HasI18n */;
|
|
2511
|
-
}
|
|
2512
|
-
function isDestroyed(lView) {
|
|
2513
|
-
// Determines whether a given LView is marked as destroyed.
|
|
2514
|
-
return (lView[FLAGS] & 256 /* LViewFlags.Destroyed */) === 256 /* LViewFlags.Destroyed */;
|
|
2515
|
-
}
|
|
2516
|
-
|
|
2517
|
-
// [Assert functions do not constraint type when they are guarded by a truthy
|
|
2518
|
-
// expression.](https://github.com/microsoft/TypeScript/issues/37295)
|
|
2519
|
-
function assertTNodeForLView(tNode, lView) {
|
|
2520
|
-
assertTNodeForTView(tNode, lView[TVIEW]);
|
|
2521
|
-
}
|
|
2522
|
-
function assertTNodeCreationIndex(lView, index) {
|
|
2523
|
-
const adjustedIndex = index + HEADER_OFFSET;
|
|
2524
|
-
assertIndexInRange(lView, adjustedIndex);
|
|
2525
|
-
assertLessThan(adjustedIndex, lView[TVIEW].bindingStartIndex, 'TNodes should be created before any bindings');
|
|
2526
|
-
}
|
|
2527
|
-
function assertTNodeForTView(tNode, tView) {
|
|
2528
|
-
assertTNode(tNode);
|
|
2529
|
-
const tData = tView.data;
|
|
2530
|
-
for (let i = HEADER_OFFSET; i < tData.length; i++) {
|
|
2531
|
-
if (tData[i] === tNode) {
|
|
2532
|
-
return;
|
|
2533
|
-
}
|
|
2534
|
-
}
|
|
2535
|
-
throwError('This TNode does not belong to this TView.');
|
|
2536
|
-
}
|
|
2537
|
-
function assertTNode(tNode) {
|
|
2538
|
-
assertDefined(tNode, 'TNode must be defined');
|
|
2539
|
-
if (!(tNode && typeof tNode === 'object' && tNode.hasOwnProperty('directiveStylingLast'))) {
|
|
2540
|
-
throwError('Not of type TNode, got: ' + tNode);
|
|
2541
|
-
}
|
|
2542
|
-
}
|
|
2543
|
-
function assertTIcu(tIcu) {
|
|
2544
|
-
assertDefined(tIcu, 'Expected TIcu to be defined');
|
|
2545
|
-
if (!(typeof tIcu.currentCaseLViewIndex === 'number')) {
|
|
2546
|
-
throwError('Object is not of TIcu type.');
|
|
2547
|
-
}
|
|
2548
|
-
}
|
|
2549
|
-
function assertComponentType(actual, msg = "Type passed in is not ComponentType, it does not have 'ɵcmp' property.") {
|
|
2550
|
-
if (!getComponentDef(actual)) {
|
|
2551
|
-
throwError(msg);
|
|
2552
|
-
}
|
|
2553
|
-
}
|
|
2554
|
-
function assertNgModuleType(actual, msg = "Type passed in is not NgModuleType, it does not have 'ɵmod' property.") {
|
|
2555
|
-
if (!getNgModuleDef(actual)) {
|
|
2556
|
-
throwError(msg);
|
|
2557
|
-
}
|
|
2558
|
-
}
|
|
2559
|
-
function assertHasParent(tNode) {
|
|
2560
|
-
assertDefined(tNode, 'currentTNode should exist!');
|
|
2561
|
-
assertDefined(tNode.parent, 'currentTNode should have a parent');
|
|
2562
|
-
}
|
|
2563
|
-
function assertLContainer(value) {
|
|
2564
|
-
assertDefined(value, 'LContainer must be defined');
|
|
2565
|
-
assertEqual(isLContainer(value), true, 'Expecting LContainer');
|
|
2566
|
-
}
|
|
2567
|
-
function assertLViewOrUndefined(value) {
|
|
2568
|
-
value && assertEqual(isLView(value), true, 'Expecting LView or undefined or null');
|
|
2569
|
-
}
|
|
2570
|
-
function assertLView(value) {
|
|
2571
|
-
assertDefined(value, 'LView must be defined');
|
|
2572
|
-
assertEqual(isLView(value), true, 'Expecting LView');
|
|
2573
|
-
}
|
|
2574
|
-
function assertFirstCreatePass(tView, errMessage) {
|
|
2575
|
-
assertEqual(tView.firstCreatePass, true, errMessage || 'Should only be called in first create pass.');
|
|
2576
|
-
}
|
|
2577
|
-
function assertFirstUpdatePass(tView, errMessage) {
|
|
2578
|
-
assertEqual(tView.firstUpdatePass, true, 'Should only be called in first update pass.');
|
|
2579
|
-
}
|
|
2580
|
-
/**
|
|
2581
|
-
* This is a basic sanity check that an object is probably a directive def. DirectiveDef is
|
|
2582
|
-
* an interface, so we can't do a direct instanceof check.
|
|
2583
|
-
*/
|
|
2584
|
-
function assertDirectiveDef(obj) {
|
|
2585
|
-
if (obj.type === undefined || obj.selectors == undefined || obj.inputs === undefined) {
|
|
2586
|
-
throwError(`Expected a DirectiveDef/ComponentDef and this object does not seem to have the expected shape.`);
|
|
2587
|
-
}
|
|
2588
|
-
}
|
|
2589
|
-
function assertIndexInDeclRange(tView, index) {
|
|
2590
|
-
assertBetween(HEADER_OFFSET, tView.bindingStartIndex, index);
|
|
2591
|
-
}
|
|
2592
|
-
function assertIndexInExpandoRange(lView, index) {
|
|
2593
|
-
const tView = lView[1];
|
|
2594
|
-
assertBetween(tView.expandoStartIndex, lView.length, index);
|
|
2595
|
-
}
|
|
2596
|
-
function assertBetween(lower, upper, index) {
|
|
2597
|
-
if (!(lower <= index && index < upper)) {
|
|
2598
|
-
throwError(`Index out of range (expecting ${lower} <= ${index} < ${upper})`);
|
|
2599
|
-
}
|
|
2600
|
-
}
|
|
2601
|
-
function assertProjectionSlots(lView, errMessage) {
|
|
2602
|
-
assertDefined(lView[DECLARATION_COMPONENT_VIEW], 'Component views should exist.');
|
|
2603
|
-
assertDefined(lView[DECLARATION_COMPONENT_VIEW][T_HOST].projection, 'Components with projection nodes (<ng-content>) must have projection slots defined.');
|
|
2604
|
-
}
|
|
2605
|
-
function assertParentView(lView, errMessage) {
|
|
2606
|
-
assertDefined(lView, "Component views should always have a parent view (component's host view)");
|
|
2607
|
-
}
|
|
2608
|
-
/**
|
|
2609
|
-
* This is a basic sanity check that the `injectorIndex` seems to point to what looks like a
|
|
2610
|
-
* NodeInjector data structure.
|
|
2611
|
-
*
|
|
2612
|
-
* @param lView `LView` which should be checked.
|
|
2613
|
-
* @param injectorIndex index into the `LView` where the `NodeInjector` is expected.
|
|
2614
|
-
*/
|
|
2615
|
-
function assertNodeInjector(lView, injectorIndex) {
|
|
2616
|
-
assertIndexInExpandoRange(lView, injectorIndex);
|
|
2617
|
-
assertIndexInExpandoRange(lView, injectorIndex + 8 /* NodeInjectorOffset.PARENT */);
|
|
2618
|
-
assertNumber(lView[injectorIndex + 0], 'injectorIndex should point to a bloom filter');
|
|
2619
|
-
assertNumber(lView[injectorIndex + 1], 'injectorIndex should point to a bloom filter');
|
|
2620
|
-
assertNumber(lView[injectorIndex + 2], 'injectorIndex should point to a bloom filter');
|
|
2621
|
-
assertNumber(lView[injectorIndex + 3], 'injectorIndex should point to a bloom filter');
|
|
2622
|
-
assertNumber(lView[injectorIndex + 4], 'injectorIndex should point to a bloom filter');
|
|
2623
|
-
assertNumber(lView[injectorIndex + 5], 'injectorIndex should point to a bloom filter');
|
|
2624
|
-
assertNumber(lView[injectorIndex + 6], 'injectorIndex should point to a bloom filter');
|
|
2625
|
-
assertNumber(lView[injectorIndex + 7], 'injectorIndex should point to a bloom filter');
|
|
2626
|
-
assertNumber(lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */], 'injectorIndex should point to parent injector');
|
|
2627
|
-
}
|
|
2628
|
-
|
|
2629
|
-
const SVG_NAMESPACE = 'svg';
|
|
2630
|
-
const MATH_ML_NAMESPACE = 'math';
|
|
2631
|
-
|
|
2632
|
-
/**
|
|
2633
|
-
* For efficiency reasons we often put several different data types (`RNode`, `LView`, `LContainer`)
|
|
2634
|
-
* in same location in `LView`. This is because we don't want to pre-allocate space for it
|
|
2635
|
-
* because the storage is sparse. This file contains utilities for dealing with such data types.
|
|
2636
|
-
*
|
|
2637
|
-
* How do we know what is stored at a given location in `LView`.
|
|
2638
|
-
* - `Array.isArray(value) === false` => `RNode` (The normal storage value)
|
|
2639
|
-
* - `Array.isArray(value) === true` => then the `value[0]` represents the wrapped value.
|
|
2640
|
-
* - `typeof value[TYPE] === 'object'` => `LView`
|
|
2641
|
-
* - This happens when we have a component at a given location
|
|
2642
|
-
* - `typeof value[TYPE] === true` => `LContainer`
|
|
2643
|
-
* - This happens when we have `LContainer` binding at a given location.
|
|
2644
|
-
*
|
|
2645
|
-
*
|
|
2646
|
-
* NOTE: it is assumed that `Array.isArray` and `typeof` operations are very efficient.
|
|
2647
|
-
*/
|
|
2648
|
-
/**
|
|
2649
|
-
* Returns `RNode`.
|
|
2650
|
-
* @param value wrapped value of `RNode`, `LView`, `LContainer`
|
|
2651
|
-
*/
|
|
2652
|
-
function unwrapRNode(value) {
|
|
2653
|
-
while (Array.isArray(value)) {
|
|
2654
|
-
value = value[HOST];
|
|
2655
|
-
}
|
|
2656
|
-
return value;
|
|
2657
|
-
}
|
|
2658
|
-
/**
|
|
2659
|
-
* Returns `LView` or `null` if not found.
|
|
2660
|
-
* @param value wrapped value of `RNode`, `LView`, `LContainer`
|
|
2661
|
-
*/
|
|
2662
|
-
function unwrapLView(value) {
|
|
2663
|
-
while (Array.isArray(value)) {
|
|
2664
|
-
// This check is same as `isLView()` but we don't call at as we don't want to call
|
|
2665
|
-
// `Array.isArray()` twice and give JITer more work for inlining.
|
|
2666
|
-
if (typeof value[TYPE] === 'object')
|
|
2667
|
-
return value;
|
|
2668
|
-
value = value[HOST];
|
|
2669
|
-
}
|
|
2670
|
-
return null;
|
|
2671
|
-
}
|
|
2672
|
-
/**
|
|
2673
|
-
* Retrieves an element value from the provided `viewData`, by unwrapping
|
|
2674
|
-
* from any containers, component views, or style contexts.
|
|
2675
|
-
*/
|
|
2676
|
-
function getNativeByIndex(index, lView) {
|
|
2677
|
-
ngDevMode && assertIndexInRange(lView, index);
|
|
2678
|
-
ngDevMode && assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Expected to be past HEADER_OFFSET');
|
|
2679
|
-
return unwrapRNode(lView[index]);
|
|
2680
|
-
}
|
|
2681
|
-
/**
|
|
2682
|
-
* Retrieve an `RNode` for a given `TNode` and `LView`.
|
|
2683
|
-
*
|
|
2684
|
-
* This function guarantees in dev mode to retrieve a non-null `RNode`.
|
|
2685
|
-
*
|
|
2686
|
-
* @param tNode
|
|
2687
|
-
* @param lView
|
|
2688
|
-
*/
|
|
2689
|
-
function getNativeByTNode(tNode, lView) {
|
|
2690
|
-
ngDevMode && assertTNodeForLView(tNode, lView);
|
|
2691
|
-
ngDevMode && assertIndexInRange(lView, tNode.index);
|
|
2692
|
-
const node = unwrapRNode(lView[tNode.index]);
|
|
2693
|
-
return node;
|
|
2694
|
-
}
|
|
2695
|
-
/**
|
|
2696
|
-
* Retrieve an `RNode` or `null` for a given `TNode` and `LView`.
|
|
2697
|
-
*
|
|
2698
|
-
* Some `TNode`s don't have associated `RNode`s. For example `Projection`
|
|
2699
|
-
*
|
|
2700
|
-
* @param tNode
|
|
2701
|
-
* @param lView
|
|
2702
|
-
*/
|
|
2703
|
-
function getNativeByTNodeOrNull(tNode, lView) {
|
|
2704
|
-
const index = tNode === null ? -1 : tNode.index;
|
|
2705
|
-
if (index !== -1) {
|
|
2706
|
-
ngDevMode && assertTNodeForLView(tNode, lView);
|
|
2707
|
-
const node = unwrapRNode(lView[index]);
|
|
2708
|
-
return node;
|
|
2709
|
-
}
|
|
2710
|
-
return null;
|
|
2711
|
-
}
|
|
2712
|
-
// fixme(misko): The return Type should be `TNode|null`
|
|
2713
|
-
function getTNode(tView, index) {
|
|
2714
|
-
ngDevMode && assertGreaterThan(index, -1, 'wrong index for TNode');
|
|
2715
|
-
ngDevMode && assertLessThan(index, tView.data.length, 'wrong index for TNode');
|
|
2716
|
-
const tNode = tView.data[index];
|
|
2717
|
-
ngDevMode && tNode !== null && assertTNode(tNode);
|
|
2718
|
-
return tNode;
|
|
2719
|
-
}
|
|
2720
|
-
/** Retrieves a value from any `LView` or `TData`. */
|
|
2721
|
-
function load(view, index) {
|
|
2722
|
-
ngDevMode && assertIndexInRange(view, index);
|
|
2723
|
-
return view[index];
|
|
2724
|
-
}
|
|
2725
|
-
/** Store a value in the `data` at a given `index`. */
|
|
2726
|
-
function store(tView, lView, index, value) {
|
|
2727
|
-
// We don't store any static data for local variables, so the first time
|
|
2728
|
-
// we see the template, we should store as null to avoid a sparse array
|
|
2729
|
-
if (index >= tView.data.length) {
|
|
2730
|
-
tView.data[index] = null;
|
|
2731
|
-
tView.blueprint[index] = null;
|
|
2732
|
-
}
|
|
2733
|
-
lView[index] = value;
|
|
2734
|
-
}
|
|
2735
|
-
function getComponentLViewByIndex(nodeIndex, hostView) {
|
|
2736
|
-
// Could be an LView or an LContainer. If LContainer, unwrap to find LView.
|
|
2737
|
-
ngDevMode && assertIndexInRange(hostView, nodeIndex);
|
|
2738
|
-
const slotValue = hostView[nodeIndex];
|
|
2739
|
-
const lView = isLView(slotValue) ? slotValue : slotValue[HOST];
|
|
2740
|
-
return lView;
|
|
2741
|
-
}
|
|
2742
|
-
/** Checks whether a given view is in creation mode */
|
|
2743
|
-
function isCreationMode(view) {
|
|
2744
|
-
return (view[FLAGS] & 4 /* LViewFlags.CreationMode */) === 4 /* LViewFlags.CreationMode */;
|
|
2745
|
-
}
|
|
2746
|
-
/**
|
|
2747
|
-
* Returns a boolean for whether the view is attached to the change detection tree.
|
|
2748
|
-
*
|
|
2749
|
-
* Note: This determines whether a view should be checked, not whether it's inserted
|
|
2750
|
-
* into a container. For that, you'll want `viewAttachedToContainer` below.
|
|
2751
|
-
*/
|
|
2752
|
-
function viewAttachedToChangeDetector(view) {
|
|
2753
|
-
return (view[FLAGS] & 128 /* LViewFlags.Attached */) === 128 /* LViewFlags.Attached */;
|
|
2754
|
-
}
|
|
2755
|
-
/** Returns a boolean for whether the view is attached to a container. */
|
|
2756
|
-
function viewAttachedToContainer(view) {
|
|
2757
|
-
return isLContainer(view[PARENT]);
|
|
2758
|
-
}
|
|
2759
|
-
function getConstant(consts, index) {
|
|
2760
|
-
if (index === null || index === undefined)
|
|
2761
|
-
return null;
|
|
2762
|
-
ngDevMode && assertIndexInRange(consts, index);
|
|
2763
|
-
return consts[index];
|
|
2764
|
-
}
|
|
2765
|
-
/**
|
|
2766
|
-
* Resets the pre-order hook flags of the view.
|
|
2767
|
-
* @param lView the LView on which the flags are reset
|
|
2768
|
-
*/
|
|
2769
|
-
function resetPreOrderHookFlags(lView) {
|
|
2770
|
-
lView[PREORDER_HOOK_FLAGS] = 0;
|
|
2771
|
-
}
|
|
2772
|
-
/**
|
|
2773
|
-
* Adds the `RefreshView` flag from the lView and updates HAS_CHILD_VIEWS_TO_REFRESH flag of
|
|
2774
|
-
* parents.
|
|
2775
|
-
*/
|
|
2776
|
-
function markViewForRefresh(lView) {
|
|
2777
|
-
if (lView[FLAGS] & 1024 /* LViewFlags.RefreshView */) {
|
|
2778
|
-
return;
|
|
2779
|
-
}
|
|
2780
|
-
lView[FLAGS] |= 1024 /* LViewFlags.RefreshView */;
|
|
2781
|
-
if (viewAttachedToChangeDetector(lView)) {
|
|
2782
|
-
markAncestorsForTraversal(lView);
|
|
2783
|
-
}
|
|
2784
|
-
}
|
|
2785
|
-
/**
|
|
2786
|
-
* Walks up the LView hierarchy.
|
|
2787
|
-
* @param nestingLevel Number of times to walk up in hierarchy.
|
|
2788
|
-
* @param currentView View from which to start the lookup.
|
|
2789
|
-
*/
|
|
2790
|
-
function walkUpViews(nestingLevel, currentView) {
|
|
2791
|
-
while (nestingLevel > 0) {
|
|
2792
|
-
ngDevMode &&
|
|
2793
|
-
assertDefined(currentView[DECLARATION_VIEW], 'Declaration view should be defined if nesting level is greater than 0.');
|
|
2794
|
-
currentView = currentView[DECLARATION_VIEW];
|
|
2795
|
-
nestingLevel--;
|
|
2796
|
-
}
|
|
2797
|
-
return currentView;
|
|
2798
|
-
}
|
|
2799
|
-
function requiresRefreshOrTraversal(lView) {
|
|
2800
|
-
return !!(lView[FLAGS] & (1024 /* LViewFlags.RefreshView */ | 8192 /* LViewFlags.HasChildViewsToRefresh */) ||
|
|
2801
|
-
lView[REACTIVE_TEMPLATE_CONSUMER]?.dirty);
|
|
2802
|
-
}
|
|
2803
|
-
/**
|
|
2804
|
-
* Updates the `HasChildViewsToRefresh` flag on the parents of the `LView` as well as the
|
|
2805
|
-
* parents above.
|
|
2806
|
-
*/
|
|
2807
|
-
function updateAncestorTraversalFlagsOnAttach(lView) {
|
|
2808
|
-
lView[ENVIRONMENT].changeDetectionScheduler?.notify(8 /* NotificationSource.ViewAttached */);
|
|
2809
|
-
if (lView[FLAGS] & 64 /* LViewFlags.Dirty */) {
|
|
2810
|
-
lView[FLAGS] |= 1024 /* LViewFlags.RefreshView */;
|
|
2811
|
-
}
|
|
2812
|
-
if (requiresRefreshOrTraversal(lView)) {
|
|
2813
|
-
markAncestorsForTraversal(lView);
|
|
2814
|
-
}
|
|
2815
|
-
}
|
|
2816
|
-
/**
|
|
2817
|
-
* Ensures views above the given `lView` are traversed during change detection even when they are
|
|
2818
|
-
* not dirty.
|
|
2819
|
-
*
|
|
2820
|
-
* This is done by setting the `HAS_CHILD_VIEWS_TO_REFRESH` flag up to the root, stopping when the
|
|
2821
|
-
* flag is already `true` or the `lView` is detached.
|
|
2822
|
-
*/
|
|
2823
|
-
function markAncestorsForTraversal(lView) {
|
|
2824
|
-
lView[ENVIRONMENT].changeDetectionScheduler?.notify(0 /* NotificationSource.MarkAncestorsForTraversal */);
|
|
2825
|
-
let parent = getLViewParent(lView);
|
|
2826
|
-
while (parent !== null) {
|
|
2827
|
-
// We stop adding markers to the ancestors once we reach one that already has the marker. This
|
|
2828
|
-
// is to avoid needlessly traversing all the way to the root when the marker already exists.
|
|
2829
|
-
if (parent[FLAGS] & 8192 /* LViewFlags.HasChildViewsToRefresh */) {
|
|
2830
|
-
break;
|
|
2831
|
-
}
|
|
2832
|
-
parent[FLAGS] |= 8192 /* LViewFlags.HasChildViewsToRefresh */;
|
|
2833
|
-
if (!viewAttachedToChangeDetector(parent)) {
|
|
2834
|
-
break;
|
|
2835
|
-
}
|
|
2836
|
-
parent = getLViewParent(parent);
|
|
2837
|
-
}
|
|
2838
|
-
}
|
|
2839
|
-
/**
|
|
2840
|
-
* Stores a LView-specific destroy callback.
|
|
2841
|
-
*/
|
|
2842
|
-
function storeLViewOnDestroy(lView, onDestroyCallback) {
|
|
2843
|
-
if (isDestroyed(lView)) {
|
|
2844
|
-
throw new RuntimeError(911 /* RuntimeErrorCode.VIEW_ALREADY_DESTROYED */, ngDevMode && 'View has already been destroyed.');
|
|
2845
|
-
}
|
|
2846
|
-
if (lView[ON_DESTROY_HOOKS] === null) {
|
|
2847
|
-
lView[ON_DESTROY_HOOKS] = [];
|
|
2848
|
-
}
|
|
2849
|
-
lView[ON_DESTROY_HOOKS].push(onDestroyCallback);
|
|
2850
|
-
}
|
|
2851
|
-
/**
|
|
2852
|
-
* Removes previously registered LView-specific destroy callback.
|
|
2853
|
-
*/
|
|
2854
|
-
function removeLViewOnDestroy(lView, onDestroyCallback) {
|
|
2855
|
-
if (lView[ON_DESTROY_HOOKS] === null)
|
|
2856
|
-
return;
|
|
2857
|
-
const destroyCBIdx = lView[ON_DESTROY_HOOKS].indexOf(onDestroyCallback);
|
|
2858
|
-
if (destroyCBIdx !== -1) {
|
|
2859
|
-
lView[ON_DESTROY_HOOKS].splice(destroyCBIdx, 1);
|
|
2860
|
-
}
|
|
2861
|
-
}
|
|
2862
|
-
/**
|
|
2863
|
-
* Gets the parent LView of the passed LView, if the PARENT is an LContainer, will get the parent of
|
|
2864
|
-
* that LContainer, which is an LView
|
|
2865
|
-
* @param lView the lView whose parent to get
|
|
2866
|
-
*/
|
|
2867
|
-
function getLViewParent(lView) {
|
|
2868
|
-
ngDevMode && assertLView(lView);
|
|
2869
|
-
const parent = lView[PARENT];
|
|
2870
|
-
return isLContainer(parent) ? parent[PARENT] : parent;
|
|
2871
|
-
}
|
|
2872
|
-
function getOrCreateLViewCleanup(view) {
|
|
2873
|
-
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
|
|
2874
|
-
return (view[CLEANUP] ??= []);
|
|
2875
|
-
}
|
|
2876
|
-
function getOrCreateTViewCleanup(tView) {
|
|
2877
|
-
return (tView.cleanup ??= []);
|
|
2878
|
-
}
|
|
2879
|
-
/**
|
|
2880
|
-
* Saves context for this cleanup function in LView.cleanupInstances.
|
|
2881
|
-
*
|
|
2882
|
-
* On the first template pass, saves in TView:
|
|
2883
|
-
* - Cleanup function
|
|
2884
|
-
* - Index of context we just saved in LView.cleanupInstances
|
|
2885
|
-
*/
|
|
2886
|
-
function storeCleanupWithContext(tView, lView, context, cleanupFn) {
|
|
2887
|
-
const lCleanup = getOrCreateLViewCleanup(lView);
|
|
2888
|
-
// Historically the `storeCleanupWithContext` was used to register both framework-level and
|
|
2889
|
-
// user-defined cleanup callbacks, but over time those two types of cleanups were separated.
|
|
2890
|
-
// This dev mode checks assures that user-level cleanup callbacks are _not_ stored in data
|
|
2891
|
-
// structures reserved for framework-specific hooks.
|
|
2892
|
-
ngDevMode &&
|
|
2893
|
-
assertDefined(context, 'Cleanup context is mandatory when registering framework-level destroy hooks');
|
|
2894
|
-
lCleanup.push(context);
|
|
2895
|
-
if (tView.firstCreatePass) {
|
|
2896
|
-
getOrCreateTViewCleanup(tView).push(cleanupFn, lCleanup.length - 1);
|
|
2897
|
-
}
|
|
2898
|
-
else {
|
|
2899
|
-
// Make sure that no new framework-level cleanup functions are registered after the first
|
|
2900
|
-
// template pass is done (and TView data structures are meant to fully constructed).
|
|
2901
|
-
if (ngDevMode) {
|
|
2902
|
-
Object.freeze(getOrCreateTViewCleanup(tView));
|
|
2903
|
-
}
|
|
2904
|
-
}
|
|
2905
|
-
}
|
|
2906
|
-
|
|
2907
|
-
const instructionState = {
|
|
2908
|
-
lFrame: createLFrame(null),
|
|
2909
|
-
bindingsEnabled: true,
|
|
2910
|
-
skipHydrationRootTNode: null,
|
|
2911
|
-
};
|
|
2912
|
-
var CheckNoChangesMode;
|
|
2913
|
-
(function (CheckNoChangesMode) {
|
|
2914
|
-
CheckNoChangesMode[CheckNoChangesMode["Off"] = 0] = "Off";
|
|
2915
|
-
CheckNoChangesMode[CheckNoChangesMode["Exhaustive"] = 1] = "Exhaustive";
|
|
2916
|
-
CheckNoChangesMode[CheckNoChangesMode["OnlyDirtyViews"] = 2] = "OnlyDirtyViews";
|
|
2917
|
-
})(CheckNoChangesMode || (CheckNoChangesMode = {}));
|
|
2918
|
-
/**
|
|
2919
|
-
* In this mode, any changes in bindings will throw an ExpressionChangedAfterChecked error.
|
|
2920
|
-
*
|
|
2921
|
-
* Necessary to support ChangeDetectorRef.checkNoChanges().
|
|
2922
|
-
*
|
|
2923
|
-
* The `checkNoChanges` function is invoked only in ngDevMode=true and verifies that no unintended
|
|
2924
|
-
* changes exist in the change detector or its children.
|
|
2925
|
-
*/
|
|
2926
|
-
let _checkNoChangesMode = 0; /* CheckNoChangesMode.Off */
|
|
2927
|
-
/**
|
|
2928
|
-
* Flag used to indicate that we are in the middle running change detection on a view
|
|
2929
|
-
*
|
|
2930
|
-
* @see detectChangesInViewWhileDirty
|
|
2931
|
-
*/
|
|
2932
|
-
let _isRefreshingViews = false;
|
|
2933
|
-
function getElementDepthCount() {
|
|
2934
|
-
return instructionState.lFrame.elementDepthCount;
|
|
2935
|
-
}
|
|
2936
|
-
function increaseElementDepthCount() {
|
|
2937
|
-
instructionState.lFrame.elementDepthCount++;
|
|
2938
|
-
}
|
|
2939
|
-
function decreaseElementDepthCount() {
|
|
2940
|
-
instructionState.lFrame.elementDepthCount--;
|
|
2941
|
-
}
|
|
2942
|
-
function getBindingsEnabled() {
|
|
2943
|
-
return instructionState.bindingsEnabled;
|
|
2944
|
-
}
|
|
2945
|
-
/**
|
|
2946
|
-
* Returns true if currently inside a skip hydration block.
|
|
2947
|
-
* @returns boolean
|
|
2948
|
-
*/
|
|
2949
|
-
function isInSkipHydrationBlock() {
|
|
2950
|
-
return instructionState.skipHydrationRootTNode !== null;
|
|
2951
|
-
}
|
|
2952
|
-
/**
|
|
2953
|
-
* Returns true if this is the root TNode of the skip hydration block.
|
|
2954
|
-
* @param tNode the current TNode
|
|
2955
|
-
* @returns boolean
|
|
2956
|
-
*/
|
|
2957
|
-
function isSkipHydrationRootTNode(tNode) {
|
|
2958
|
-
return instructionState.skipHydrationRootTNode === tNode;
|
|
2959
|
-
}
|
|
2960
|
-
/**
|
|
2961
|
-
* Enables directive matching on elements.
|
|
2962
|
-
*
|
|
2963
|
-
* * Example:
|
|
2964
|
-
* ```html
|
|
2965
|
-
* <my-comp my-directive>
|
|
2966
|
-
* Should match component / directive.
|
|
2967
|
-
* </my-comp>
|
|
2968
|
-
* <div ngNonBindable>
|
|
2969
|
-
* <!-- ɵɵdisableBindings() -->
|
|
2970
|
-
* <my-comp my-directive>
|
|
2971
|
-
* Should not match component / directive because we are in ngNonBindable.
|
|
2972
|
-
* </my-comp>
|
|
2973
|
-
* <!-- ɵɵenableBindings() -->
|
|
2974
|
-
* </div>
|
|
2975
|
-
* ```
|
|
2976
|
-
*
|
|
2977
|
-
* @codeGenApi
|
|
2978
|
-
*/
|
|
2979
|
-
function ɵɵenableBindings() {
|
|
2980
|
-
instructionState.bindingsEnabled = true;
|
|
2981
|
-
}
|
|
2982
|
-
/**
|
|
2983
|
-
* Sets a flag to specify that the TNode is in a skip hydration block.
|
|
2984
|
-
* @param tNode the current TNode
|
|
2985
|
-
*/
|
|
2986
|
-
function enterSkipHydrationBlock(tNode) {
|
|
2987
|
-
instructionState.skipHydrationRootTNode = tNode;
|
|
2988
|
-
}
|
|
2989
|
-
/**
|
|
2990
|
-
* Disables directive matching on element.
|
|
2991
|
-
*
|
|
2992
|
-
* * Example:
|
|
2993
|
-
* ```html
|
|
2994
|
-
* <my-comp my-directive>
|
|
2995
|
-
* Should match component / directive.
|
|
2996
|
-
* </my-comp>
|
|
2997
|
-
* <div ngNonBindable>
|
|
2998
|
-
* <!-- ɵɵdisableBindings() -->
|
|
2999
|
-
* <my-comp my-directive>
|
|
3000
|
-
* Should not match component / directive because we are in ngNonBindable.
|
|
3001
|
-
* </my-comp>
|
|
3002
|
-
* <!-- ɵɵenableBindings() -->
|
|
3003
|
-
* </div>
|
|
3004
|
-
* ```
|
|
3005
|
-
*
|
|
3006
|
-
* @codeGenApi
|
|
3007
|
-
*/
|
|
3008
|
-
function ɵɵdisableBindings() {
|
|
3009
|
-
instructionState.bindingsEnabled = false;
|
|
3010
|
-
}
|
|
3011
|
-
/**
|
|
3012
|
-
* Clears the root skip hydration node when leaving a skip hydration block.
|
|
3013
|
-
*/
|
|
3014
|
-
function leaveSkipHydrationBlock() {
|
|
3015
|
-
instructionState.skipHydrationRootTNode = null;
|
|
3016
|
-
}
|
|
3017
|
-
/**
|
|
3018
|
-
* Return the current `LView`.
|
|
3019
|
-
*/
|
|
3020
|
-
function getLView() {
|
|
3021
|
-
return instructionState.lFrame.lView;
|
|
3022
|
-
}
|
|
3023
|
-
/**
|
|
3024
|
-
* Return the current `TView`.
|
|
3025
|
-
*/
|
|
3026
|
-
function getTView() {
|
|
3027
|
-
return instructionState.lFrame.tView;
|
|
3028
|
-
}
|
|
3029
|
-
/**
|
|
3030
|
-
* Restores `contextViewData` to the given OpaqueViewState instance.
|
|
3031
|
-
*
|
|
3032
|
-
* Used in conjunction with the getCurrentView() instruction to save a snapshot
|
|
3033
|
-
* of the current view and restore it when listeners are invoked. This allows
|
|
3034
|
-
* walking the declaration view tree in listeners to get vars from parent views.
|
|
3035
|
-
*
|
|
3036
|
-
* @param viewToRestore The OpaqueViewState instance to restore.
|
|
3037
|
-
* @returns Context of the restored OpaqueViewState instance.
|
|
3038
|
-
*
|
|
3039
|
-
* @codeGenApi
|
|
3040
|
-
*/
|
|
3041
|
-
function ɵɵrestoreView(viewToRestore) {
|
|
3042
|
-
instructionState.lFrame.contextLView = viewToRestore;
|
|
3043
|
-
return viewToRestore[CONTEXT];
|
|
3044
|
-
}
|
|
3045
|
-
/**
|
|
3046
|
-
* Clears the view set in `ɵɵrestoreView` from memory. Returns the passed in
|
|
3047
|
-
* value so that it can be used as a return value of an instruction.
|
|
3048
|
-
*
|
|
3049
|
-
* @codeGenApi
|
|
3050
|
-
*/
|
|
3051
|
-
function ɵɵresetView(value) {
|
|
3052
|
-
instructionState.lFrame.contextLView = null;
|
|
3053
|
-
return value;
|
|
3054
|
-
}
|
|
3055
|
-
function getCurrentTNode() {
|
|
3056
|
-
let currentTNode = getCurrentTNodePlaceholderOk();
|
|
3057
|
-
while (currentTNode !== null && currentTNode.type === 64 /* TNodeType.Placeholder */) {
|
|
3058
|
-
currentTNode = currentTNode.parent;
|
|
3059
|
-
}
|
|
3060
|
-
return currentTNode;
|
|
3061
|
-
}
|
|
3062
|
-
function getCurrentTNodePlaceholderOk() {
|
|
3063
|
-
return instructionState.lFrame.currentTNode;
|
|
3064
|
-
}
|
|
3065
|
-
function getCurrentParentTNode() {
|
|
3066
|
-
const lFrame = instructionState.lFrame;
|
|
3067
|
-
const currentTNode = lFrame.currentTNode;
|
|
3068
|
-
return lFrame.isParent ? currentTNode : currentTNode.parent;
|
|
3069
|
-
}
|
|
3070
|
-
function setCurrentTNode(tNode, isParent) {
|
|
3071
|
-
ngDevMode && tNode && assertTNodeForTView(tNode, instructionState.lFrame.tView);
|
|
3072
|
-
const lFrame = instructionState.lFrame;
|
|
3073
|
-
lFrame.currentTNode = tNode;
|
|
3074
|
-
lFrame.isParent = isParent;
|
|
3075
|
-
}
|
|
3076
|
-
function isCurrentTNodeParent() {
|
|
3077
|
-
return instructionState.lFrame.isParent;
|
|
3078
|
-
}
|
|
3079
|
-
function setCurrentTNodeAsNotParent() {
|
|
3080
|
-
instructionState.lFrame.isParent = false;
|
|
3081
|
-
}
|
|
3082
|
-
function getContextLView() {
|
|
3083
|
-
const contextLView = instructionState.lFrame.contextLView;
|
|
3084
|
-
ngDevMode && assertDefined(contextLView, 'contextLView must be defined.');
|
|
3085
|
-
return contextLView;
|
|
3086
|
-
}
|
|
3087
|
-
function isInCheckNoChangesMode() {
|
|
3088
|
-
!ngDevMode && throwError('Must never be called in production mode');
|
|
3089
|
-
return _checkNoChangesMode !== CheckNoChangesMode.Off;
|
|
3090
|
-
}
|
|
3091
|
-
function isExhaustiveCheckNoChanges() {
|
|
3092
|
-
!ngDevMode && throwError('Must never be called in production mode');
|
|
3093
|
-
return _checkNoChangesMode === CheckNoChangesMode.Exhaustive;
|
|
3094
|
-
}
|
|
3095
|
-
function setIsInCheckNoChangesMode(mode) {
|
|
3096
|
-
!ngDevMode && throwError('Must never be called in production mode');
|
|
3097
|
-
_checkNoChangesMode = mode;
|
|
3098
|
-
}
|
|
3099
|
-
function isRefreshingViews() {
|
|
3100
|
-
return _isRefreshingViews;
|
|
3101
|
-
}
|
|
3102
|
-
function setIsRefreshingViews(mode) {
|
|
3103
|
-
const prev = _isRefreshingViews;
|
|
3104
|
-
_isRefreshingViews = mode;
|
|
3105
|
-
return prev;
|
|
3106
|
-
}
|
|
3107
|
-
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
|
|
3108
|
-
function getBindingRoot() {
|
|
3109
|
-
const lFrame = instructionState.lFrame;
|
|
3110
|
-
let index = lFrame.bindingRootIndex;
|
|
3111
|
-
if (index === -1) {
|
|
3112
|
-
index = lFrame.bindingRootIndex = lFrame.tView.bindingStartIndex;
|
|
3113
|
-
}
|
|
3114
|
-
return index;
|
|
3115
|
-
}
|
|
3116
|
-
function getBindingIndex() {
|
|
3117
|
-
return instructionState.lFrame.bindingIndex;
|
|
3118
|
-
}
|
|
3119
|
-
function setBindingIndex(value) {
|
|
3120
|
-
return (instructionState.lFrame.bindingIndex = value);
|
|
3121
|
-
}
|
|
3122
|
-
function nextBindingIndex() {
|
|
3123
|
-
return instructionState.lFrame.bindingIndex++;
|
|
3124
|
-
}
|
|
3125
|
-
function incrementBindingIndex(count) {
|
|
3126
|
-
const lFrame = instructionState.lFrame;
|
|
3127
|
-
const index = lFrame.bindingIndex;
|
|
3128
|
-
lFrame.bindingIndex = lFrame.bindingIndex + count;
|
|
3129
|
-
return index;
|
|
3130
|
-
}
|
|
3131
|
-
function isInI18nBlock() {
|
|
3132
|
-
return instructionState.lFrame.inI18n;
|
|
3133
|
-
}
|
|
3134
|
-
function setInI18nBlock(isInI18nBlock) {
|
|
3135
|
-
instructionState.lFrame.inI18n = isInI18nBlock;
|
|
3136
|
-
}
|
|
3137
|
-
/**
|
|
3138
|
-
* Set a new binding root index so that host template functions can execute.
|
|
3139
|
-
*
|
|
3140
|
-
* Bindings inside the host template are 0 index. But because we don't know ahead of time
|
|
3141
|
-
* how many host bindings we have we can't pre-compute them. For this reason they are all
|
|
3142
|
-
* 0 index and we just shift the root so that they match next available location in the LView.
|
|
3143
|
-
*
|
|
3144
|
-
* @param bindingRootIndex Root index for `hostBindings`
|
|
3145
|
-
* @param currentDirectiveIndex `TData[currentDirectiveIndex]` will point to the current directive
|
|
3146
|
-
* whose `hostBindings` are being processed.
|
|
3147
|
-
*/
|
|
3148
|
-
function setBindingRootForHostBindings(bindingRootIndex, currentDirectiveIndex) {
|
|
3149
|
-
const lFrame = instructionState.lFrame;
|
|
3150
|
-
lFrame.bindingIndex = lFrame.bindingRootIndex = bindingRootIndex;
|
|
3151
|
-
setCurrentDirectiveIndex(currentDirectiveIndex);
|
|
3152
|
-
}
|
|
3153
|
-
/**
|
|
3154
|
-
* When host binding is executing this points to the directive index.
|
|
3155
|
-
* `TView.data[getCurrentDirectiveIndex()]` is `DirectiveDef`
|
|
3156
|
-
* `LView[getCurrentDirectiveIndex()]` is directive instance.
|
|
3157
|
-
*/
|
|
3158
|
-
function getCurrentDirectiveIndex() {
|
|
3159
|
-
return instructionState.lFrame.currentDirectiveIndex;
|
|
3160
|
-
}
|
|
3161
|
-
/**
|
|
3162
|
-
* Sets an index of a directive whose `hostBindings` are being processed.
|
|
3163
|
-
*
|
|
3164
|
-
* @param currentDirectiveIndex `TData` index where current directive instance can be found.
|
|
3165
|
-
*/
|
|
3166
|
-
function setCurrentDirectiveIndex(currentDirectiveIndex) {
|
|
3167
|
-
instructionState.lFrame.currentDirectiveIndex = currentDirectiveIndex;
|
|
3168
|
-
}
|
|
3169
|
-
/**
|
|
3170
|
-
* Retrieve the current `DirectiveDef` which is active when `hostBindings` instruction is being
|
|
3171
|
-
* executed.
|
|
3172
|
-
*
|
|
3173
|
-
* @param tData Current `TData` where the `DirectiveDef` will be looked up at.
|
|
3174
|
-
*/
|
|
3175
|
-
function getCurrentDirectiveDef(tData) {
|
|
3176
|
-
const currentDirectiveIndex = instructionState.lFrame.currentDirectiveIndex;
|
|
3177
|
-
return currentDirectiveIndex === -1 ? null : tData[currentDirectiveIndex];
|
|
3178
|
-
}
|
|
3179
|
-
function getCurrentQueryIndex() {
|
|
3180
|
-
return instructionState.lFrame.currentQueryIndex;
|
|
3181
|
-
}
|
|
3182
|
-
function setCurrentQueryIndex(value) {
|
|
3183
|
-
instructionState.lFrame.currentQueryIndex = value;
|
|
3184
|
-
}
|
|
3185
|
-
/**
|
|
3186
|
-
* Returns a `TNode` of the location where the current `LView` is declared at.
|
|
3187
|
-
*
|
|
3188
|
-
* @param lView an `LView` that we want to find parent `TNode` for.
|
|
3189
|
-
*/
|
|
3190
|
-
function getDeclarationTNode(lView) {
|
|
3191
|
-
const tView = lView[TVIEW];
|
|
3192
|
-
// Return the declaration parent for embedded views
|
|
3193
|
-
if (tView.type === 2 /* TViewType.Embedded */) {
|
|
3194
|
-
ngDevMode && assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.');
|
|
3195
|
-
return tView.declTNode;
|
|
3196
|
-
}
|
|
3197
|
-
// Components don't have `TView.declTNode` because each instance of component could be
|
|
3198
|
-
// inserted in different location, hence `TView.declTNode` is meaningless.
|
|
3199
|
-
// Falling back to `T_HOST` in case we cross component boundary.
|
|
3200
|
-
if (tView.type === 1 /* TViewType.Component */) {
|
|
3201
|
-
return lView[T_HOST];
|
|
3202
|
-
}
|
|
3203
|
-
// Remaining TNode type is `TViewType.Root` which doesn't have a parent TNode.
|
|
3204
|
-
return null;
|
|
3205
|
-
}
|
|
3206
|
-
/**
|
|
3207
|
-
* This is a light weight version of the `enterView` which is needed by the DI system.
|
|
3208
|
-
*
|
|
3209
|
-
* @param lView `LView` location of the DI context.
|
|
3210
|
-
* @param tNode `TNode` for DI context
|
|
3211
|
-
* @param flags DI context flags. if `SkipSelf` flag is set than we walk up the declaration
|
|
3212
|
-
* tree from `tNode` until we find parent declared `TElementNode`.
|
|
3213
|
-
* @returns `true` if we have successfully entered DI associated with `tNode` (or with declared
|
|
3214
|
-
* `TNode` if `flags` has `SkipSelf`). Failing to enter DI implies that no associated
|
|
3215
|
-
* `NodeInjector` can be found and we should instead use `ModuleInjector`.
|
|
3216
|
-
* - If `true` than this call must be fallowed by `leaveDI`
|
|
3217
|
-
* - If `false` than this call failed and we should NOT call `leaveDI`
|
|
3218
|
-
*/
|
|
3219
|
-
function enterDI(lView, tNode, flags) {
|
|
3220
|
-
ngDevMode && assertLViewOrUndefined(lView);
|
|
3221
|
-
if (flags & 4 /* InternalInjectFlags.SkipSelf */) {
|
|
3222
|
-
ngDevMode && assertTNodeForTView(tNode, lView[TVIEW]);
|
|
3223
|
-
let parentTNode = tNode;
|
|
3224
|
-
let parentLView = lView;
|
|
3225
|
-
while (true) {
|
|
3226
|
-
ngDevMode && assertDefined(parentTNode, 'Parent TNode should be defined');
|
|
3227
|
-
parentTNode = parentTNode.parent;
|
|
3228
|
-
if (parentTNode === null && !(flags & 1 /* InternalInjectFlags.Host */)) {
|
|
3229
|
-
parentTNode = getDeclarationTNode(parentLView);
|
|
3230
|
-
if (parentTNode === null)
|
|
3231
|
-
break;
|
|
3232
|
-
// In this case, a parent exists and is definitely an element. So it will definitely
|
|
3233
|
-
// have an existing lView as the declaration view, which is why we can assume it's defined.
|
|
3234
|
-
ngDevMode && assertDefined(parentLView, 'Parent LView should be defined');
|
|
3235
|
-
parentLView = parentLView[DECLARATION_VIEW];
|
|
3236
|
-
// In Ivy there are Comment nodes that correspond to ngIf and NgFor embedded directives
|
|
3237
|
-
// We want to skip those and look only at Elements and ElementContainers to ensure
|
|
3238
|
-
// we're looking at true parent nodes, and not content or other types.
|
|
3239
|
-
if (parentTNode.type & (2 /* TNodeType.Element */ | 8 /* TNodeType.ElementContainer */)) {
|
|
3240
|
-
break;
|
|
3241
|
-
}
|
|
3242
|
-
}
|
|
3243
|
-
else {
|
|
3244
|
-
break;
|
|
3245
|
-
}
|
|
3246
|
-
}
|
|
3247
|
-
if (parentTNode === null) {
|
|
3248
|
-
// If we failed to find a parent TNode this means that we should use module injector.
|
|
3249
|
-
return false;
|
|
3250
|
-
}
|
|
3251
|
-
else {
|
|
3252
|
-
tNode = parentTNode;
|
|
3253
|
-
lView = parentLView;
|
|
3254
|
-
}
|
|
3255
|
-
}
|
|
3256
|
-
ngDevMode && assertTNodeForLView(tNode, lView);
|
|
3257
|
-
const lFrame = (instructionState.lFrame = allocLFrame());
|
|
3258
|
-
lFrame.currentTNode = tNode;
|
|
3259
|
-
lFrame.lView = lView;
|
|
3260
|
-
return true;
|
|
3261
|
-
}
|
|
3262
|
-
/**
|
|
3263
|
-
* Swap the current lView with a new lView.
|
|
3264
|
-
*
|
|
3265
|
-
* For performance reasons we store the lView in the top level of the module.
|
|
3266
|
-
* This way we minimize the number of properties to read. Whenever a new view
|
|
3267
|
-
* is entered we have to store the lView for later, and when the view is
|
|
3268
|
-
* exited the state has to be restored
|
|
3269
|
-
*
|
|
3270
|
-
* @param newView New lView to become active
|
|
3271
|
-
* @returns the previously active lView;
|
|
3272
|
-
*/
|
|
3273
|
-
function enterView(newView) {
|
|
3274
|
-
ngDevMode && assertNotEqual(newView[0], newView[1], '????');
|
|
3275
|
-
ngDevMode && assertLViewOrUndefined(newView);
|
|
3276
|
-
const newLFrame = allocLFrame();
|
|
3277
|
-
if (ngDevMode) {
|
|
3278
|
-
assertEqual(newLFrame.isParent, true, 'Expected clean LFrame');
|
|
3279
|
-
assertEqual(newLFrame.lView, null, 'Expected clean LFrame');
|
|
3280
|
-
assertEqual(newLFrame.tView, null, 'Expected clean LFrame');
|
|
3281
|
-
assertEqual(newLFrame.selectedIndex, -1, 'Expected clean LFrame');
|
|
3282
|
-
assertEqual(newLFrame.elementDepthCount, 0, 'Expected clean LFrame');
|
|
3283
|
-
assertEqual(newLFrame.currentDirectiveIndex, -1, 'Expected clean LFrame');
|
|
3284
|
-
assertEqual(newLFrame.currentNamespace, null, 'Expected clean LFrame');
|
|
3285
|
-
assertEqual(newLFrame.bindingRootIndex, -1, 'Expected clean LFrame');
|
|
3286
|
-
assertEqual(newLFrame.currentQueryIndex, 0, 'Expected clean LFrame');
|
|
3287
|
-
}
|
|
3288
|
-
const tView = newView[TVIEW];
|
|
3289
|
-
instructionState.lFrame = newLFrame;
|
|
3290
|
-
ngDevMode && tView.firstChild && assertTNodeForTView(tView.firstChild, tView);
|
|
3291
|
-
newLFrame.currentTNode = tView.firstChild;
|
|
3292
|
-
newLFrame.lView = newView;
|
|
3293
|
-
newLFrame.tView = tView;
|
|
3294
|
-
newLFrame.contextLView = newView;
|
|
3295
|
-
newLFrame.bindingIndex = tView.bindingStartIndex;
|
|
3296
|
-
newLFrame.inI18n = false;
|
|
3297
|
-
}
|
|
3298
|
-
/**
|
|
3299
|
-
* Allocates next free LFrame. This function tries to reuse the `LFrame`s to lower memory pressure.
|
|
3300
|
-
*/
|
|
3301
|
-
function allocLFrame() {
|
|
3302
|
-
const currentLFrame = instructionState.lFrame;
|
|
3303
|
-
const childLFrame = currentLFrame === null ? null : currentLFrame.child;
|
|
3304
|
-
const newLFrame = childLFrame === null ? createLFrame(currentLFrame) : childLFrame;
|
|
3305
|
-
return newLFrame;
|
|
3306
|
-
}
|
|
3307
|
-
function createLFrame(parent) {
|
|
3308
|
-
const lFrame = {
|
|
3309
|
-
currentTNode: null,
|
|
3310
|
-
isParent: true,
|
|
3311
|
-
lView: null,
|
|
3312
|
-
tView: null,
|
|
3313
|
-
selectedIndex: -1,
|
|
3314
|
-
contextLView: null,
|
|
3315
|
-
elementDepthCount: 0,
|
|
3316
|
-
currentNamespace: null,
|
|
3317
|
-
currentDirectiveIndex: -1,
|
|
3318
|
-
bindingRootIndex: -1,
|
|
3319
|
-
bindingIndex: -1,
|
|
3320
|
-
currentQueryIndex: 0,
|
|
3321
|
-
parent: parent,
|
|
3322
|
-
child: null,
|
|
3323
|
-
inI18n: false,
|
|
3324
|
-
};
|
|
3325
|
-
parent !== null && (parent.child = lFrame); // link the new LFrame for reuse.
|
|
3326
|
-
return lFrame;
|
|
3327
|
-
}
|
|
3328
|
-
/**
|
|
3329
|
-
* A lightweight version of leave which is used with DI.
|
|
3330
|
-
*
|
|
3331
|
-
* This function only resets `currentTNode` and `LView` as those are the only properties
|
|
3332
|
-
* used with DI (`enterDI()`).
|
|
3333
|
-
*
|
|
3334
|
-
* NOTE: This function is reexported as `leaveDI`. However `leaveDI` has return type of `void` where
|
|
3335
|
-
* as `leaveViewLight` has `LFrame`. This is so that `leaveViewLight` can be used in `leaveView`.
|
|
3336
|
-
*/
|
|
3337
|
-
function leaveViewLight() {
|
|
3338
|
-
const oldLFrame = instructionState.lFrame;
|
|
3339
|
-
instructionState.lFrame = oldLFrame.parent;
|
|
3340
|
-
oldLFrame.currentTNode = null;
|
|
3341
|
-
oldLFrame.lView = null;
|
|
3342
|
-
return oldLFrame;
|
|
3343
|
-
}
|
|
3344
|
-
/**
|
|
3345
|
-
* This is a lightweight version of the `leaveView` which is needed by the DI system.
|
|
3346
|
-
*
|
|
3347
|
-
* NOTE: this function is an alias so that we can change the type of the function to have `void`
|
|
3348
|
-
* return type.
|
|
3349
|
-
*/
|
|
3350
|
-
const leaveDI = leaveViewLight;
|
|
3351
|
-
/**
|
|
3352
|
-
* Leave the current `LView`
|
|
3353
|
-
*
|
|
3354
|
-
* This pops the `LFrame` with the associated `LView` from the stack.
|
|
3355
|
-
*
|
|
3356
|
-
* IMPORTANT: We must zero out the `LFrame` values here otherwise they will be retained. This is
|
|
3357
|
-
* because for performance reasons we don't release `LFrame` but rather keep it for next use.
|
|
3358
|
-
*/
|
|
3359
|
-
function leaveView() {
|
|
3360
|
-
const oldLFrame = leaveViewLight();
|
|
3361
|
-
oldLFrame.isParent = true;
|
|
3362
|
-
oldLFrame.tView = null;
|
|
3363
|
-
oldLFrame.selectedIndex = -1;
|
|
3364
|
-
oldLFrame.contextLView = null;
|
|
3365
|
-
oldLFrame.elementDepthCount = 0;
|
|
3366
|
-
oldLFrame.currentDirectiveIndex = -1;
|
|
3367
|
-
oldLFrame.currentNamespace = null;
|
|
3368
|
-
oldLFrame.bindingRootIndex = -1;
|
|
3369
|
-
oldLFrame.bindingIndex = -1;
|
|
3370
|
-
oldLFrame.currentQueryIndex = 0;
|
|
3371
|
-
}
|
|
3372
|
-
function nextContextImpl(level) {
|
|
3373
|
-
const contextLView = (instructionState.lFrame.contextLView = walkUpViews(level, instructionState.lFrame.contextLView));
|
|
3374
|
-
return contextLView[CONTEXT];
|
|
3375
|
-
}
|
|
3376
|
-
/**
|
|
3377
|
-
* Gets the currently selected element index.
|
|
3378
|
-
*
|
|
3379
|
-
* Used with {@link property} instruction (and more in the future) to identify the index in the
|
|
3380
|
-
* current `LView` to act on.
|
|
3381
|
-
*/
|
|
3382
|
-
function getSelectedIndex() {
|
|
3383
|
-
return instructionState.lFrame.selectedIndex;
|
|
3384
|
-
}
|
|
3385
|
-
/**
|
|
3386
|
-
* Sets the most recent index passed to {@link select}
|
|
3387
|
-
*
|
|
3388
|
-
* Used with {@link property} instruction (and more in the future) to identify the index in the
|
|
3389
|
-
* current `LView` to act on.
|
|
3390
|
-
*
|
|
3391
|
-
* (Note that if an "exit function" was set earlier (via `setElementExitFn()`) then that will be
|
|
3392
|
-
* run if and when the provided `index` value is different from the current selected index value.)
|
|
3393
|
-
*/
|
|
3394
|
-
function setSelectedIndex(index) {
|
|
3395
|
-
ngDevMode &&
|
|
3396
|
-
index !== -1 &&
|
|
3397
|
-
assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Index must be past HEADER_OFFSET (or -1).');
|
|
3398
|
-
ngDevMode &&
|
|
3399
|
-
assertLessThan(index, instructionState.lFrame.lView.length, "Can't set index passed end of LView");
|
|
3400
|
-
instructionState.lFrame.selectedIndex = index;
|
|
3401
|
-
}
|
|
3402
|
-
/**
|
|
3403
|
-
* Gets the `tNode` that represents currently selected element.
|
|
3404
|
-
*/
|
|
3405
|
-
function getSelectedTNode() {
|
|
3406
|
-
const lFrame = instructionState.lFrame;
|
|
3407
|
-
return getTNode(lFrame.tView, lFrame.selectedIndex);
|
|
3408
|
-
}
|
|
3409
|
-
/**
|
|
3410
|
-
* Sets the namespace used to create elements to `'http://www.w3.org/2000/svg'` in global state.
|
|
3411
|
-
*
|
|
3412
|
-
* @codeGenApi
|
|
3413
|
-
*/
|
|
3414
|
-
function ɵɵnamespaceSVG() {
|
|
3415
|
-
instructionState.lFrame.currentNamespace = SVG_NAMESPACE;
|
|
3416
|
-
}
|
|
3417
|
-
/**
|
|
3418
|
-
* Sets the namespace used to create elements to `'http://www.w3.org/1998/MathML/'` in global state.
|
|
3419
|
-
*
|
|
3420
|
-
* @codeGenApi
|
|
3421
|
-
*/
|
|
3422
|
-
function ɵɵnamespaceMathML() {
|
|
3423
|
-
instructionState.lFrame.currentNamespace = MATH_ML_NAMESPACE;
|
|
3424
|
-
}
|
|
3425
|
-
/**
|
|
3426
|
-
* Sets the namespace used to create elements to `null`, which forces element creation to use
|
|
3427
|
-
* `createElement` rather than `createElementNS`.
|
|
3428
|
-
*
|
|
3429
|
-
* @codeGenApi
|
|
3430
|
-
*/
|
|
3431
|
-
function ɵɵnamespaceHTML() {
|
|
3432
|
-
namespaceHTMLInternal();
|
|
3433
|
-
}
|
|
3434
|
-
/**
|
|
3435
|
-
* Sets the namespace used to create elements to `null`, which forces element creation to use
|
|
3436
|
-
* `createElement` rather than `createElementNS`.
|
|
3437
|
-
*/
|
|
3438
|
-
function namespaceHTMLInternal() {
|
|
3439
|
-
instructionState.lFrame.currentNamespace = null;
|
|
3440
|
-
}
|
|
3441
|
-
function getNamespace() {
|
|
3442
|
-
return instructionState.lFrame.currentNamespace;
|
|
3443
|
-
}
|
|
3444
|
-
let _wasLastNodeCreated = true;
|
|
3445
|
-
/**
|
|
3446
|
-
* Retrieves a global flag that indicates whether the most recent DOM node
|
|
3447
|
-
* was created or hydrated.
|
|
3448
|
-
*/
|
|
3449
|
-
function wasLastNodeCreated() {
|
|
3450
|
-
return _wasLastNodeCreated;
|
|
3451
|
-
}
|
|
3452
|
-
/**
|
|
3453
|
-
* Sets a global flag to indicate whether the most recent DOM node
|
|
3454
|
-
* was created or hydrated.
|
|
3455
|
-
*/
|
|
3456
|
-
function lastNodeWasCreated(flag) {
|
|
3457
|
-
_wasLastNodeCreated = flag;
|
|
3458
|
-
}
|
|
3459
|
-
|
|
3460
|
-
/**
|
|
3461
|
-
* Create a new `Injector` which is configured using a `defType` of `InjectorType<any>`s.
|
|
3462
|
-
*/
|
|
3463
|
-
function createInjector(defType, parent = null, additionalProviders = null, name) {
|
|
3464
|
-
const injector = createInjectorWithoutInjectorInstances(defType, parent, additionalProviders, name);
|
|
3465
|
-
injector.resolveInjectorInitializers();
|
|
3466
|
-
return injector;
|
|
3467
|
-
}
|
|
3468
|
-
/**
|
|
3469
|
-
* Creates a new injector without eagerly resolving its injector types. Can be used in places
|
|
3470
|
-
* where resolving the injector types immediately can lead to an infinite loop. The injector types
|
|
3471
|
-
* should be resolved at a later point by calling `_resolveInjectorDefTypes`.
|
|
3472
|
-
*/
|
|
3473
|
-
function createInjectorWithoutInjectorInstances(defType, parent = null, additionalProviders = null, name, scopes = new Set()) {
|
|
3474
|
-
const providers = [additionalProviders || EMPTY_ARRAY, importProvidersFrom(defType)];
|
|
3475
|
-
name = name || (typeof defType === 'object' ? undefined : stringify(defType));
|
|
3476
|
-
return new R3Injector(providers, parent || getNullInjector(), name || null, scopes);
|
|
3477
|
-
}
|
|
3478
|
-
|
|
3479
|
-
/**
|
|
3480
|
-
* Concrete injectors implement this interface. Injectors are configured
|
|
3481
|
-
* with [providers](guide/di/dependency-injection-providers) that associate
|
|
3482
|
-
* dependencies of various types with [injection tokens](guide/di/dependency-injection-providers).
|
|
3483
|
-
*
|
|
3484
|
-
* @see [DI Providers](guide/di/dependency-injection-providers).
|
|
3485
|
-
* @see {@link StaticProvider}
|
|
3486
|
-
*
|
|
3487
|
-
* @usageNotes
|
|
3488
|
-
*
|
|
3489
|
-
* The following example creates a service injector instance.
|
|
3490
|
-
*
|
|
3491
|
-
* {@example core/di/ts/provider_spec.ts region='ConstructorProvider'}
|
|
3492
|
-
*
|
|
3493
|
-
* ### Usage example
|
|
3494
|
-
*
|
|
3495
|
-
* {@example core/di/ts/injector_spec.ts region='Injector'}
|
|
3496
|
-
*
|
|
3497
|
-
* `Injector` returns itself when given `Injector` as a token:
|
|
3498
|
-
*
|
|
3499
|
-
* {@example core/di/ts/injector_spec.ts region='injectInjector'}
|
|
3500
|
-
*
|
|
3501
|
-
* @publicApi
|
|
3502
|
-
*/
|
|
3503
|
-
class Injector {
|
|
3504
|
-
static THROW_IF_NOT_FOUND = THROW_IF_NOT_FOUND;
|
|
3505
|
-
static NULL = new NullInjector();
|
|
3506
|
-
static create(options, parent) {
|
|
3507
|
-
if (Array.isArray(options)) {
|
|
3508
|
-
return createInjector({ name: '' }, parent, options, '');
|
|
3509
|
-
}
|
|
3510
|
-
else {
|
|
3511
|
-
const name = options.name ?? '';
|
|
3512
|
-
return createInjector({ name }, options.parent, options.providers, name);
|
|
3513
|
-
}
|
|
3514
|
-
}
|
|
3515
|
-
/** @nocollapse */
|
|
3516
|
-
static ɵprov = /** @pureOrBreakMyCode */ /* @__PURE__ */ ɵɵdefineInjectable({
|
|
3517
|
-
token: Injector,
|
|
3518
|
-
providedIn: 'any',
|
|
3519
|
-
factory: () => ɵɵinject(INJECTOR$1),
|
|
3520
|
-
});
|
|
3521
|
-
/**
|
|
3522
|
-
* @internal
|
|
3523
|
-
* @nocollapse
|
|
3524
|
-
*/
|
|
3525
|
-
static __NG_ELEMENT_ID__ = -1 /* InjectorMarkers.Injector */;
|
|
3526
|
-
}
|
|
3527
|
-
|
|
3528
|
-
/**
|
|
3529
|
-
* A DI Token representing the main rendering context.
|
|
3530
|
-
* In a browser and SSR this is the DOM Document.
|
|
3531
|
-
* When using SSR, that document is created by [Domino](https://github.com/angular/domino).
|
|
3532
|
-
*
|
|
3533
|
-
* @publicApi
|
|
3534
|
-
*/
|
|
3535
|
-
const DOCUMENT = new InjectionToken(typeof ngDevMode !== undefined && ngDevMode ? 'DocumentToken' : '');
|
|
3536
|
-
|
|
3537
|
-
/**
|
|
3538
|
-
* `DestroyRef` lets you set callbacks to run for any cleanup or destruction behavior.
|
|
3539
|
-
* The scope of this destruction depends on where `DestroyRef` is injected. If `DestroyRef`
|
|
3540
|
-
* is injected in a component or directive, the callbacks run when that component or
|
|
3541
|
-
* directive is destroyed. Otherwise the callbacks run when a corresponding injector is destroyed.
|
|
3542
|
-
*
|
|
3543
|
-
* @publicApi
|
|
3544
|
-
*/
|
|
3545
|
-
class DestroyRef {
|
|
3546
|
-
/**
|
|
3547
|
-
* @internal
|
|
3548
|
-
* @nocollapse
|
|
3549
|
-
*/
|
|
3550
|
-
static __NG_ELEMENT_ID__ = injectDestroyRef;
|
|
3551
|
-
/**
|
|
3552
|
-
* @internal
|
|
3553
|
-
* @nocollapse
|
|
3554
|
-
*/
|
|
3555
|
-
static __NG_ENV_ID__ = (injector) => injector;
|
|
3556
|
-
}
|
|
3557
|
-
class NodeInjectorDestroyRef extends DestroyRef {
|
|
3558
|
-
_lView;
|
|
3559
|
-
constructor(_lView) {
|
|
3560
|
-
super();
|
|
3561
|
-
this._lView = _lView;
|
|
3562
|
-
}
|
|
3563
|
-
get destroyed() {
|
|
3564
|
-
return isDestroyed(this._lView);
|
|
3565
|
-
}
|
|
3566
|
-
onDestroy(callback) {
|
|
3567
|
-
const lView = this._lView;
|
|
3568
|
-
storeLViewOnDestroy(lView, callback);
|
|
3569
|
-
return () => removeLViewOnDestroy(lView, callback);
|
|
3570
|
-
}
|
|
3571
|
-
}
|
|
3572
|
-
function injectDestroyRef() {
|
|
3573
|
-
return new NodeInjectorDestroyRef(getLView());
|
|
3574
|
-
}
|
|
3575
|
-
|
|
3576
|
-
const SCHEDULE_IN_ROOT_ZONE_DEFAULT = false;
|
|
3577
|
-
|
|
3578
|
-
/**
|
|
3579
|
-
* Internal implementation of the pending tasks service.
|
|
3580
|
-
*/
|
|
3581
|
-
class PendingTasksInternal {
|
|
3582
|
-
taskId = 0;
|
|
3583
|
-
pendingTasks = new Set();
|
|
3584
|
-
destroyed = false;
|
|
3585
|
-
pendingTask = new BehaviorSubject(false);
|
|
3586
|
-
get hasPendingTasks() {
|
|
3587
|
-
// Accessing the value of a closed `BehaviorSubject` throws an error.
|
|
3588
|
-
return this.destroyed ? false : this.pendingTask.value;
|
|
3589
|
-
}
|
|
3590
|
-
/**
|
|
3591
|
-
* In case the service is about to be destroyed, return a self-completing observable.
|
|
3592
|
-
* Otherwise, return the observable that emits the current state of pending tasks.
|
|
3593
|
-
*/
|
|
3594
|
-
get hasPendingTasksObservable() {
|
|
3595
|
-
if (this.destroyed) {
|
|
3596
|
-
// Manually creating the observable pulls less symbols from RxJS than `of(false)`.
|
|
3597
|
-
return new Observable((subscriber) => {
|
|
3598
|
-
subscriber.next(false);
|
|
3599
|
-
subscriber.complete();
|
|
3600
|
-
});
|
|
3601
|
-
}
|
|
3602
|
-
return this.pendingTask;
|
|
3603
|
-
}
|
|
3604
|
-
add() {
|
|
3605
|
-
// Emitting a value to a closed subject throws an error.
|
|
3606
|
-
if (!this.hasPendingTasks && !this.destroyed) {
|
|
3607
|
-
this.pendingTask.next(true);
|
|
3608
|
-
}
|
|
3609
|
-
const taskId = this.taskId++;
|
|
3610
|
-
this.pendingTasks.add(taskId);
|
|
3611
|
-
return taskId;
|
|
3612
|
-
}
|
|
3613
|
-
has(taskId) {
|
|
3614
|
-
return this.pendingTasks.has(taskId);
|
|
3615
|
-
}
|
|
3616
|
-
remove(taskId) {
|
|
3617
|
-
this.pendingTasks.delete(taskId);
|
|
3618
|
-
if (this.pendingTasks.size === 0 && this.hasPendingTasks) {
|
|
3619
|
-
this.pendingTask.next(false);
|
|
3620
|
-
}
|
|
3621
|
-
}
|
|
3622
|
-
ngOnDestroy() {
|
|
3623
|
-
this.pendingTasks.clear();
|
|
3624
|
-
if (this.hasPendingTasks) {
|
|
3625
|
-
this.pendingTask.next(false);
|
|
3626
|
-
}
|
|
3627
|
-
// We call `unsubscribe()` to release observers, as users may forget to
|
|
3628
|
-
// unsubscribe manually when subscribing to `isStable`. We do not call
|
|
3629
|
-
// `complete()` because it is unsafe; if someone subscribes using the `first`
|
|
3630
|
-
// operator and the observable completes before emitting a value,
|
|
3631
|
-
// RxJS will throw an error.
|
|
3632
|
-
this.destroyed = true;
|
|
3633
|
-
this.pendingTask.unsubscribe();
|
|
3634
|
-
}
|
|
3635
|
-
/** @nocollapse */
|
|
3636
|
-
static ɵprov = /** @pureOrBreakMyCode */ /* @__PURE__ */ ɵɵdefineInjectable({
|
|
3637
|
-
token: PendingTasksInternal,
|
|
3638
|
-
providedIn: 'root',
|
|
3639
|
-
factory: () => new PendingTasksInternal(),
|
|
3640
|
-
});
|
|
3641
|
-
}
|
|
3642
|
-
|
|
3643
|
-
class EventEmitter_ extends Subject {
|
|
3644
|
-
// tslint:disable-next-line:require-internal-with-underscore
|
|
3645
|
-
__isAsync;
|
|
3646
|
-
destroyRef = undefined;
|
|
3647
|
-
pendingTasks = undefined;
|
|
3648
|
-
constructor(isAsync = false) {
|
|
3649
|
-
super();
|
|
3650
|
-
this.__isAsync = isAsync;
|
|
3651
|
-
// Attempt to retrieve a `DestroyRef` and `PendingTasks` optionally.
|
|
3652
|
-
// For backwards compatibility reasons, this cannot be required.
|
|
3653
|
-
if (isInInjectionContext()) {
|
|
3654
|
-
// `DestroyRef` is optional because it is not available in all contexts.
|
|
3655
|
-
// But it is useful to properly complete the `EventEmitter` if used with `outputToObservable`
|
|
3656
|
-
// when the component/directive is destroyed. (See `outputToObservable` for more details.)
|
|
3657
|
-
this.destroyRef = inject(DestroyRef, { optional: true }) ?? undefined;
|
|
3658
|
-
this.pendingTasks = inject(PendingTasksInternal, { optional: true }) ?? undefined;
|
|
3659
|
-
}
|
|
3660
|
-
}
|
|
3661
|
-
emit(value) {
|
|
3662
|
-
const prevConsumer = setActiveConsumer$1(null);
|
|
3663
|
-
try {
|
|
3664
|
-
super.next(value);
|
|
3665
|
-
}
|
|
3666
|
-
finally {
|
|
3667
|
-
setActiveConsumer$1(prevConsumer);
|
|
3668
|
-
}
|
|
3669
|
-
}
|
|
3670
|
-
subscribe(observerOrNext, error, complete) {
|
|
3671
|
-
let nextFn = observerOrNext;
|
|
3672
|
-
let errorFn = error || (() => null);
|
|
3673
|
-
let completeFn = complete;
|
|
3674
|
-
if (observerOrNext && typeof observerOrNext === 'object') {
|
|
3675
|
-
const observer = observerOrNext;
|
|
3676
|
-
nextFn = observer.next?.bind(observer);
|
|
3677
|
-
errorFn = observer.error?.bind(observer);
|
|
3678
|
-
completeFn = observer.complete?.bind(observer);
|
|
3679
|
-
}
|
|
3680
|
-
if (this.__isAsync) {
|
|
3681
|
-
errorFn = this.wrapInTimeout(errorFn);
|
|
3682
|
-
if (nextFn) {
|
|
3683
|
-
nextFn = this.wrapInTimeout(nextFn);
|
|
3684
|
-
}
|
|
3685
|
-
if (completeFn) {
|
|
3686
|
-
completeFn = this.wrapInTimeout(completeFn);
|
|
3687
|
-
}
|
|
3688
|
-
}
|
|
3689
|
-
const sink = super.subscribe({ next: nextFn, error: errorFn, complete: completeFn });
|
|
3690
|
-
if (observerOrNext instanceof Subscription) {
|
|
3691
|
-
observerOrNext.add(sink);
|
|
3692
|
-
}
|
|
3693
|
-
return sink;
|
|
3694
|
-
}
|
|
3695
|
-
wrapInTimeout(fn) {
|
|
3696
|
-
return (value) => {
|
|
3697
|
-
const taskId = this.pendingTasks?.add();
|
|
3698
|
-
setTimeout(() => {
|
|
3699
|
-
try {
|
|
3700
|
-
fn(value);
|
|
3701
|
-
}
|
|
3702
|
-
finally {
|
|
3703
|
-
if (taskId !== undefined) {
|
|
3704
|
-
this.pendingTasks?.remove(taskId);
|
|
3705
|
-
}
|
|
3706
|
-
}
|
|
3707
|
-
});
|
|
3708
|
-
};
|
|
3709
|
-
}
|
|
3710
|
-
}
|
|
3711
|
-
/**
|
|
3712
|
-
* @publicApi
|
|
3713
|
-
*/
|
|
3714
|
-
const EventEmitter = EventEmitter_;
|
|
3715
|
-
|
|
3716
|
-
function noop(...args) {
|
|
3717
|
-
// Do nothing.
|
|
3718
|
-
}
|
|
3719
|
-
|
|
3720
|
-
/**
|
|
3721
|
-
* Gets a scheduling function that runs the callback after the first of setTimeout and
|
|
3722
|
-
* requestAnimationFrame resolves.
|
|
3723
|
-
*
|
|
3724
|
-
* - `requestAnimationFrame` ensures that change detection runs ahead of a browser repaint.
|
|
3725
|
-
* This ensures that the create and update passes of a change detection always happen
|
|
3726
|
-
* in the same frame.
|
|
3727
|
-
* - When the browser is resource-starved, `rAF` can execute _before_ a `setTimeout` because
|
|
3728
|
-
* rendering is a very high priority process. This means that `setTimeout` cannot guarantee
|
|
3729
|
-
* same-frame create and update pass, when `setTimeout` is used to schedule the update phase.
|
|
3730
|
-
* - While `rAF` gives us the desirable same-frame updates, it has two limitations that
|
|
3731
|
-
* prevent it from being used alone. First, it does not run in background tabs, which would
|
|
3732
|
-
* prevent Angular from initializing an application when opened in a new tab (for example).
|
|
3733
|
-
* Second, repeated calls to requestAnimationFrame will execute at the refresh rate of the
|
|
3734
|
-
* hardware (~16ms for a 60Hz display). This would cause significant slowdown of tests that
|
|
3735
|
-
* are written with several updates and asserts in the form of "update; await stable; assert;".
|
|
3736
|
-
* - Both `setTimeout` and `rAF` are able to "coalesce" several events from a single user
|
|
3737
|
-
* interaction into a single change detection. Importantly, this reduces view tree traversals when
|
|
3738
|
-
* compared to an alternative timing mechanism like `queueMicrotask`, where change detection would
|
|
3739
|
-
* then be interleaves between each event.
|
|
3740
|
-
*
|
|
3741
|
-
* By running change detection after the first of `setTimeout` and `rAF` to execute, we get the
|
|
3742
|
-
* best of both worlds.
|
|
3743
|
-
*
|
|
3744
|
-
* @returns a function to cancel the scheduled callback
|
|
3745
|
-
*/
|
|
3746
|
-
function scheduleCallbackWithRafRace(callback) {
|
|
3747
|
-
let timeoutId;
|
|
3748
|
-
let animationFrameId;
|
|
3749
|
-
function cleanup() {
|
|
3750
|
-
callback = noop;
|
|
3751
|
-
try {
|
|
3752
|
-
if (animationFrameId !== undefined && typeof cancelAnimationFrame === 'function') {
|
|
3753
|
-
cancelAnimationFrame(animationFrameId);
|
|
3754
|
-
}
|
|
3755
|
-
if (timeoutId !== undefined) {
|
|
3756
|
-
clearTimeout(timeoutId);
|
|
3757
|
-
}
|
|
3758
|
-
}
|
|
3759
|
-
catch {
|
|
3760
|
-
// Clearing/canceling can fail in tests due to the timing of functions being patched and unpatched
|
|
3761
|
-
// Just ignore the errors - we protect ourselves from this issue by also making the callback a no-op.
|
|
3762
|
-
}
|
|
3763
|
-
}
|
|
3764
|
-
timeoutId = setTimeout(() => {
|
|
3765
|
-
callback();
|
|
3766
|
-
cleanup();
|
|
3767
|
-
});
|
|
3768
|
-
if (typeof requestAnimationFrame === 'function') {
|
|
3769
|
-
animationFrameId = requestAnimationFrame(() => {
|
|
3770
|
-
callback();
|
|
3771
|
-
cleanup();
|
|
3772
|
-
});
|
|
3773
|
-
}
|
|
3774
|
-
return () => cleanup();
|
|
3775
|
-
}
|
|
3776
|
-
function scheduleCallbackWithMicrotask(callback) {
|
|
3777
|
-
queueMicrotask(() => callback());
|
|
3778
|
-
return () => {
|
|
3779
|
-
callback = noop;
|
|
3780
|
-
};
|
|
3781
|
-
}
|
|
3782
|
-
|
|
3783
|
-
class AsyncStackTaggingZoneSpec {
|
|
3784
|
-
createTask;
|
|
3785
|
-
constructor(namePrefix, consoleAsyncStackTaggingImpl = console) {
|
|
3786
|
-
this.name = 'asyncStackTagging for ' + namePrefix;
|
|
3787
|
-
this.createTask = consoleAsyncStackTaggingImpl?.createTask ?? (() => null);
|
|
3788
|
-
}
|
|
3789
|
-
// ZoneSpec implementation below.
|
|
3790
|
-
name;
|
|
3791
|
-
onScheduleTask(delegate, _current, target, task) {
|
|
3792
|
-
task.consoleTask = this.createTask(`Zone - ${task.source || task.type}`);
|
|
3793
|
-
return delegate.scheduleTask(target, task);
|
|
3794
|
-
}
|
|
3795
|
-
onInvokeTask(delegate, _currentZone, targetZone, task, applyThis, applyArgs) {
|
|
3796
|
-
let ret;
|
|
3797
|
-
if (task.consoleTask) {
|
|
3798
|
-
ret = task.consoleTask.run(() => delegate.invokeTask(targetZone, task, applyThis, applyArgs));
|
|
3799
|
-
}
|
|
3800
|
-
else {
|
|
3801
|
-
ret = delegate.invokeTask(targetZone, task, applyThis, applyArgs);
|
|
3802
|
-
}
|
|
3803
|
-
return ret;
|
|
3804
|
-
}
|
|
3805
|
-
}
|
|
3806
|
-
|
|
3807
|
-
const isAngularZoneProperty = 'isAngularZone';
|
|
3808
|
-
const angularZoneInstanceIdProperty = isAngularZoneProperty + '_ID';
|
|
3809
|
-
let ngZoneInstanceId = 0;
|
|
3810
|
-
/**
|
|
3811
|
-
* An injectable service for executing work inside or outside of the Angular zone.
|
|
3812
|
-
*
|
|
3813
|
-
* The most common use of this service is to optimize performance when starting a work consisting of
|
|
3814
|
-
* one or more asynchronous tasks that don't require UI updates or error handling to be handled by
|
|
3815
|
-
* Angular. Such tasks can be kicked off via {@link #runOutsideAngular} and if needed, these tasks
|
|
3816
|
-
* can reenter the Angular zone via {@link #run}.
|
|
3817
|
-
*
|
|
3818
|
-
* <!-- TODO: add/fix links to:
|
|
3819
|
-
* - docs explaining zones and the use of zones in Angular and change-detection
|
|
3820
|
-
* - link to runOutsideAngular/run (throughout this file!)
|
|
3821
|
-
* -->
|
|
3822
|
-
*
|
|
3823
|
-
* @usageNotes
|
|
3824
|
-
* ### Example
|
|
3825
|
-
*
|
|
3826
|
-
* ```ts
|
|
3827
|
-
* import {Component, NgZone} from '@angular/core';
|
|
3828
|
-
*
|
|
3829
|
-
* @Component({
|
|
3830
|
-
* selector: 'ng-zone-demo',
|
|
3831
|
-
* template: `
|
|
3832
|
-
* <h2>Demo: NgZone</h2>
|
|
3833
|
-
*
|
|
3834
|
-
* <p>Progress: {{progress}}%</p>
|
|
3835
|
-
* @if(progress >= 100) {
|
|
3836
|
-
* <p>Done processing {{label}} of Angular zone!</p>
|
|
3837
|
-
* }
|
|
3838
|
-
*
|
|
3839
|
-
* <button (click)="processWithinAngularZone()">Process within Angular zone</button>
|
|
3840
|
-
* <button (click)="processOutsideOfAngularZone()">Process outside of Angular zone</button>
|
|
3841
|
-
* `,
|
|
3842
|
-
* })
|
|
3843
|
-
* export class NgZoneDemo {
|
|
3844
|
-
* progress: number = 0;
|
|
3845
|
-
* label: string;
|
|
3846
|
-
*
|
|
3847
|
-
* constructor(private _ngZone: NgZone) {}
|
|
3848
|
-
*
|
|
3849
|
-
* // Loop inside the Angular zone
|
|
3850
|
-
* // so the UI DOES refresh after each setTimeout cycle
|
|
3851
|
-
* processWithinAngularZone() {
|
|
3852
|
-
* this.label = 'inside';
|
|
3853
|
-
* this.progress = 0;
|
|
3854
|
-
* this._increaseProgress(() => console.log('Inside Done!'));
|
|
3855
|
-
* }
|
|
3856
|
-
*
|
|
3857
|
-
* // Loop outside of the Angular zone
|
|
3858
|
-
* // so the UI DOES NOT refresh after each setTimeout cycle
|
|
3859
|
-
* processOutsideOfAngularZone() {
|
|
3860
|
-
* this.label = 'outside';
|
|
3861
|
-
* this.progress = 0;
|
|
3862
|
-
* this._ngZone.runOutsideAngular(() => {
|
|
3863
|
-
* this._increaseProgress(() => {
|
|
3864
|
-
* // reenter the Angular zone and display done
|
|
3865
|
-
* this._ngZone.run(() => { console.log('Outside Done!'); });
|
|
3866
|
-
* });
|
|
3867
|
-
* });
|
|
3868
|
-
* }
|
|
3869
|
-
*
|
|
3870
|
-
* _increaseProgress(doneCallback: () => void) {
|
|
3871
|
-
* this.progress += 1;
|
|
3872
|
-
* console.log(`Current progress: ${this.progress}%`);
|
|
3873
|
-
*
|
|
3874
|
-
* if (this.progress < 100) {
|
|
3875
|
-
* window.setTimeout(() => this._increaseProgress(doneCallback), 10);
|
|
3876
|
-
* } else {
|
|
3877
|
-
* doneCallback();
|
|
3878
|
-
* }
|
|
3879
|
-
* }
|
|
3880
|
-
* }
|
|
3881
|
-
* ```
|
|
3882
|
-
*
|
|
3883
|
-
* @publicApi
|
|
3884
|
-
*/
|
|
3885
|
-
class NgZone {
|
|
3886
|
-
hasPendingMacrotasks = false;
|
|
3887
|
-
hasPendingMicrotasks = false;
|
|
3888
|
-
/**
|
|
3889
|
-
* Whether there are no outstanding microtasks or macrotasks.
|
|
3890
|
-
*/
|
|
3891
|
-
isStable = true;
|
|
3892
|
-
/**
|
|
3893
|
-
* Notifies when code enters Angular Zone. This gets fired first on VM Turn.
|
|
3894
|
-
*/
|
|
3895
|
-
onUnstable = new EventEmitter(false);
|
|
3896
|
-
/**
|
|
3897
|
-
* Notifies when there is no more microtasks enqueued in the current VM Turn.
|
|
3898
|
-
* This is a hint for Angular to do change detection, which may enqueue more microtasks.
|
|
3899
|
-
* For this reason this event can fire multiple times per VM Turn.
|
|
3900
|
-
*/
|
|
3901
|
-
onMicrotaskEmpty = new EventEmitter(false);
|
|
3902
|
-
/**
|
|
3903
|
-
* Notifies when the last `onMicrotaskEmpty` has run and there are no more microtasks, which
|
|
3904
|
-
* implies we are about to relinquish VM turn.
|
|
3905
|
-
* This event gets called just once.
|
|
3906
|
-
*/
|
|
3907
|
-
onStable = new EventEmitter(false);
|
|
3908
|
-
/**
|
|
3909
|
-
* Notifies that an error has been delivered.
|
|
3910
|
-
*/
|
|
3911
|
-
onError = new EventEmitter(false);
|
|
3912
|
-
constructor(options) {
|
|
3913
|
-
const { enableLongStackTrace = false, shouldCoalesceEventChangeDetection = false, shouldCoalesceRunChangeDetection = false, scheduleInRootZone = SCHEDULE_IN_ROOT_ZONE_DEFAULT, } = options;
|
|
3914
|
-
if (typeof Zone == 'undefined') {
|
|
3915
|
-
throw new RuntimeError(908 /* RuntimeErrorCode.MISSING_ZONEJS */, ngDevMode && `In this configuration Angular requires Zone.js`);
|
|
3916
|
-
}
|
|
3917
|
-
Zone.assertZonePatched();
|
|
3918
|
-
const self = this;
|
|
3919
|
-
self._nesting = 0;
|
|
3920
|
-
self._outer = self._inner = Zone.current;
|
|
3921
|
-
// AsyncStackTaggingZoneSpec provides `linked stack traces` to show
|
|
3922
|
-
// where the async operation is scheduled. For more details, refer
|
|
3923
|
-
// to this article, https://developer.chrome.com/blog/devtools-better-angular-debugging/
|
|
3924
|
-
// And we only import this AsyncStackTaggingZoneSpec in development mode,
|
|
3925
|
-
// in the production mode, the AsyncStackTaggingZoneSpec will be tree shaken away.
|
|
3926
|
-
if (ngDevMode) {
|
|
3927
|
-
self._inner = self._inner.fork(new AsyncStackTaggingZoneSpec('Angular'));
|
|
3928
|
-
}
|
|
3929
|
-
if (Zone['TaskTrackingZoneSpec']) {
|
|
3930
|
-
self._inner = self._inner.fork(new Zone['TaskTrackingZoneSpec']());
|
|
3931
|
-
}
|
|
3932
|
-
if (enableLongStackTrace && Zone['longStackTraceZoneSpec']) {
|
|
3933
|
-
self._inner = self._inner.fork(Zone['longStackTraceZoneSpec']);
|
|
3934
|
-
}
|
|
3935
|
-
// if shouldCoalesceRunChangeDetection is true, all tasks including event tasks will be
|
|
3936
|
-
// coalesced, so shouldCoalesceEventChangeDetection option is not necessary and can be skipped.
|
|
3937
|
-
self.shouldCoalesceEventChangeDetection =
|
|
3938
|
-
!shouldCoalesceRunChangeDetection && shouldCoalesceEventChangeDetection;
|
|
3939
|
-
self.shouldCoalesceRunChangeDetection = shouldCoalesceRunChangeDetection;
|
|
3940
|
-
self.callbackScheduled = false;
|
|
3941
|
-
self.scheduleInRootZone = scheduleInRootZone;
|
|
3942
|
-
forkInnerZoneWithAngularBehavior(self);
|
|
3943
|
-
}
|
|
3944
|
-
/**
|
|
3945
|
-
This method checks whether the method call happens within an Angular Zone instance.
|
|
3946
|
-
*/
|
|
3947
|
-
static isInAngularZone() {
|
|
3948
|
-
// Zone needs to be checked, because this method might be called even when NoopNgZone is used.
|
|
3949
|
-
return typeof Zone !== 'undefined' && Zone.current.get(isAngularZoneProperty) === true;
|
|
3950
|
-
}
|
|
3951
|
-
/**
|
|
3952
|
-
Assures that the method is called within the Angular Zone, otherwise throws an error.
|
|
3953
|
-
*/
|
|
3954
|
-
static assertInAngularZone() {
|
|
3955
|
-
if (!NgZone.isInAngularZone()) {
|
|
3956
|
-
throw new RuntimeError(909 /* RuntimeErrorCode.UNEXPECTED_ZONE_STATE */, ngDevMode && 'Expected to be in Angular Zone, but it is not!');
|
|
3957
|
-
}
|
|
3958
|
-
}
|
|
3959
|
-
/**
|
|
3960
|
-
Assures that the method is called outside of the Angular Zone, otherwise throws an error.
|
|
3961
|
-
*/
|
|
3962
|
-
static assertNotInAngularZone() {
|
|
3963
|
-
if (NgZone.isInAngularZone()) {
|
|
3964
|
-
throw new RuntimeError(909 /* RuntimeErrorCode.UNEXPECTED_ZONE_STATE */, ngDevMode && 'Expected to not be in Angular Zone, but it is!');
|
|
3965
|
-
}
|
|
3966
|
-
}
|
|
3967
|
-
/**
|
|
3968
|
-
* Executes the `fn` function synchronously within the Angular zone and returns value returned by
|
|
3969
|
-
* the function.
|
|
3970
|
-
*
|
|
3971
|
-
* Running functions via `run` allows you to reenter Angular zone from a task that was executed
|
|
3972
|
-
* outside of the Angular zone (typically started via {@link #runOutsideAngular}).
|
|
3973
|
-
*
|
|
3974
|
-
* Any future tasks or microtasks scheduled from within this function will continue executing from
|
|
3975
|
-
* within the Angular zone.
|
|
3976
|
-
*
|
|
3977
|
-
* If a synchronous error happens it will be rethrown and not reported via `onError`.
|
|
3978
|
-
*/
|
|
3979
|
-
run(fn, applyThis, applyArgs) {
|
|
3980
|
-
return this._inner.run(fn, applyThis, applyArgs);
|
|
3981
|
-
}
|
|
3982
|
-
/**
|
|
3983
|
-
* Executes the `fn` function synchronously within the Angular zone as a task and returns value
|
|
3984
|
-
* returned by the function.
|
|
3985
|
-
*
|
|
3986
|
-
* Running functions via `runTask` allows you to reenter Angular zone from a task that was executed
|
|
3987
|
-
* outside of the Angular zone (typically started via {@link #runOutsideAngular}).
|
|
3988
|
-
*
|
|
3989
|
-
* Any future tasks or microtasks scheduled from within this function will continue executing from
|
|
3990
|
-
* within the Angular zone.
|
|
3991
|
-
*
|
|
3992
|
-
* If a synchronous error happens it will be rethrown and not reported via `onError`.
|
|
3993
|
-
*/
|
|
3994
|
-
runTask(fn, applyThis, applyArgs, name) {
|
|
3995
|
-
const zone = this._inner;
|
|
3996
|
-
const task = zone.scheduleEventTask('NgZoneEvent: ' + name, fn, EMPTY_PAYLOAD, noop, noop);
|
|
3997
|
-
try {
|
|
3998
|
-
return zone.runTask(task, applyThis, applyArgs);
|
|
3999
|
-
}
|
|
4000
|
-
finally {
|
|
4001
|
-
zone.cancelTask(task);
|
|
4002
|
-
}
|
|
4003
|
-
}
|
|
4004
|
-
/**
|
|
4005
|
-
* Same as `run`, except that synchronous errors are caught and forwarded via `onError` and not
|
|
4006
|
-
* rethrown.
|
|
4007
|
-
*/
|
|
4008
|
-
runGuarded(fn, applyThis, applyArgs) {
|
|
4009
|
-
return this._inner.runGuarded(fn, applyThis, applyArgs);
|
|
4010
|
-
}
|
|
4011
|
-
/**
|
|
4012
|
-
* Executes the `fn` function synchronously in Angular's parent zone and returns value returned by
|
|
4013
|
-
* the function.
|
|
4014
|
-
*
|
|
4015
|
-
* Running functions via {@link #runOutsideAngular} allows you to escape Angular's zone and do
|
|
4016
|
-
* work that
|
|
4017
|
-
* doesn't trigger Angular change-detection or is subject to Angular's error handling.
|
|
4018
|
-
*
|
|
4019
|
-
* Any future tasks or microtasks scheduled from within this function will continue executing from
|
|
4020
|
-
* outside of the Angular zone.
|
|
4021
|
-
*
|
|
4022
|
-
* Use {@link #run} to reenter the Angular zone and do work that updates the application model.
|
|
4023
|
-
*/
|
|
4024
|
-
runOutsideAngular(fn) {
|
|
4025
|
-
return this._outer.run(fn);
|
|
4026
|
-
}
|
|
4027
|
-
}
|
|
4028
|
-
const EMPTY_PAYLOAD = {};
|
|
4029
|
-
function checkStable(zone) {
|
|
4030
|
-
// TODO: @JiaLiPassion, should check zone.isCheckStableRunning to prevent
|
|
4031
|
-
// re-entry. The case is:
|
|
4032
|
-
//
|
|
4033
|
-
// @Component({...})
|
|
4034
|
-
// export class AppComponent {
|
|
4035
|
-
// constructor(private ngZone: NgZone) {
|
|
4036
|
-
// this.ngZone.onStable.subscribe(() => {
|
|
4037
|
-
// this.ngZone.run(() => console.log('stable'););
|
|
4038
|
-
// });
|
|
4039
|
-
// }
|
|
4040
|
-
//
|
|
4041
|
-
// The onStable subscriber run another function inside ngZone
|
|
4042
|
-
// which causes `checkStable()` re-entry.
|
|
4043
|
-
// But this fix causes some issues in g3, so this fix will be
|
|
4044
|
-
// launched in another PR.
|
|
4045
|
-
if (zone._nesting == 0 && !zone.hasPendingMicrotasks && !zone.isStable) {
|
|
4046
|
-
try {
|
|
4047
|
-
zone._nesting++;
|
|
4048
|
-
zone.onMicrotaskEmpty.emit(null);
|
|
4049
|
-
}
|
|
4050
|
-
finally {
|
|
4051
|
-
zone._nesting--;
|
|
4052
|
-
if (!zone.hasPendingMicrotasks) {
|
|
4053
|
-
try {
|
|
4054
|
-
zone.runOutsideAngular(() => zone.onStable.emit(null));
|
|
4055
|
-
}
|
|
4056
|
-
finally {
|
|
4057
|
-
zone.isStable = true;
|
|
4058
|
-
}
|
|
4059
|
-
}
|
|
4060
|
-
}
|
|
4061
|
-
}
|
|
4062
|
-
}
|
|
4063
|
-
function delayChangeDetectionForEvents(zone) {
|
|
4064
|
-
/**
|
|
4065
|
-
* We also need to check _nesting here
|
|
4066
|
-
* Consider the following case with shouldCoalesceRunChangeDetection = true
|
|
4067
|
-
*
|
|
4068
|
-
* ngZone.run(() => {});
|
|
4069
|
-
* ngZone.run(() => {});
|
|
4070
|
-
*
|
|
4071
|
-
* We want the two `ngZone.run()` only trigger one change detection
|
|
4072
|
-
* when shouldCoalesceRunChangeDetection is true.
|
|
4073
|
-
* And because in this case, change detection run in async way(requestAnimationFrame),
|
|
4074
|
-
* so we also need to check the _nesting here to prevent multiple
|
|
4075
|
-
* change detections.
|
|
4076
|
-
*/
|
|
4077
|
-
if (zone.isCheckStableRunning || zone.callbackScheduled) {
|
|
4078
|
-
return;
|
|
4079
|
-
}
|
|
4080
|
-
zone.callbackScheduled = true;
|
|
4081
|
-
function scheduleCheckStable() {
|
|
4082
|
-
scheduleCallbackWithRafRace(() => {
|
|
4083
|
-
zone.callbackScheduled = false;
|
|
4084
|
-
updateMicroTaskStatus(zone);
|
|
4085
|
-
zone.isCheckStableRunning = true;
|
|
4086
|
-
checkStable(zone);
|
|
4087
|
-
zone.isCheckStableRunning = false;
|
|
4088
|
-
});
|
|
4089
|
-
}
|
|
4090
|
-
if (zone.scheduleInRootZone) {
|
|
4091
|
-
Zone.root.run(() => {
|
|
4092
|
-
scheduleCheckStable();
|
|
4093
|
-
});
|
|
4094
|
-
}
|
|
4095
|
-
else {
|
|
4096
|
-
zone._outer.run(() => {
|
|
4097
|
-
scheduleCheckStable();
|
|
4098
|
-
});
|
|
4099
|
-
}
|
|
4100
|
-
updateMicroTaskStatus(zone);
|
|
4101
|
-
}
|
|
4102
|
-
function forkInnerZoneWithAngularBehavior(zone) {
|
|
4103
|
-
const delayChangeDetectionForEventsDelegate = () => {
|
|
4104
|
-
delayChangeDetectionForEvents(zone);
|
|
4105
|
-
};
|
|
4106
|
-
const instanceId = ngZoneInstanceId++;
|
|
4107
|
-
zone._inner = zone._inner.fork({
|
|
4108
|
-
name: 'angular',
|
|
4109
|
-
properties: {
|
|
4110
|
-
[isAngularZoneProperty]: true,
|
|
4111
|
-
[angularZoneInstanceIdProperty]: instanceId,
|
|
4112
|
-
[angularZoneInstanceIdProperty + instanceId]: true,
|
|
4113
|
-
},
|
|
4114
|
-
onInvokeTask: (delegate, current, target, task, applyThis, applyArgs) => {
|
|
4115
|
-
// Prevent triggering change detection when the flag is detected.
|
|
4116
|
-
if (shouldBeIgnoredByZone(applyArgs)) {
|
|
4117
|
-
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
|
4118
|
-
}
|
|
4119
|
-
try {
|
|
4120
|
-
onEnter(zone);
|
|
4121
|
-
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
|
4122
|
-
}
|
|
4123
|
-
finally {
|
|
4124
|
-
if ((zone.shouldCoalesceEventChangeDetection && task.type === 'eventTask') ||
|
|
4125
|
-
zone.shouldCoalesceRunChangeDetection) {
|
|
4126
|
-
delayChangeDetectionForEventsDelegate();
|
|
4127
|
-
}
|
|
4128
|
-
onLeave(zone);
|
|
4129
|
-
}
|
|
4130
|
-
},
|
|
4131
|
-
onInvoke: (delegate, current, target, callback, applyThis, applyArgs, source) => {
|
|
4132
|
-
try {
|
|
4133
|
-
onEnter(zone);
|
|
4134
|
-
return delegate.invoke(target, callback, applyThis, applyArgs, source);
|
|
4135
|
-
}
|
|
4136
|
-
finally {
|
|
4137
|
-
if (zone.shouldCoalesceRunChangeDetection &&
|
|
4138
|
-
// Do not delay change detection when the task is the scheduler's tick.
|
|
4139
|
-
// We need to synchronously trigger the stability logic so that the
|
|
4140
|
-
// zone-based scheduler can prevent a duplicate ApplicationRef.tick
|
|
4141
|
-
// by first checking if the scheduler tick is running. This does seem a bit roundabout,
|
|
4142
|
-
// but we _do_ still want to trigger all the correct events when we exit the zone.run
|
|
4143
|
-
// (`onMicrotaskEmpty` and `onStable` _should_ emit; developers can have code which
|
|
4144
|
-
// relies on these events happening after change detection runs).
|
|
4145
|
-
// Note: `zone.callbackScheduled` is already in delayChangeDetectionForEventsDelegate
|
|
4146
|
-
// but is added here as well to prevent reads of applyArgs when not necessary
|
|
4147
|
-
!zone.callbackScheduled &&
|
|
4148
|
-
!isSchedulerTick(applyArgs)) {
|
|
4149
|
-
delayChangeDetectionForEventsDelegate();
|
|
4150
|
-
}
|
|
4151
|
-
onLeave(zone);
|
|
4152
|
-
}
|
|
4153
|
-
},
|
|
4154
|
-
onHasTask: (delegate, current, target, hasTaskState) => {
|
|
4155
|
-
delegate.hasTask(target, hasTaskState);
|
|
4156
|
-
if (current === target) {
|
|
4157
|
-
// We are only interested in hasTask events which originate from our zone
|
|
4158
|
-
// (A child hasTask event is not interesting to us)
|
|
4159
|
-
if (hasTaskState.change == 'microTask') {
|
|
4160
|
-
zone._hasPendingMicrotasks = hasTaskState.microTask;
|
|
4161
|
-
updateMicroTaskStatus(zone);
|
|
4162
|
-
checkStable(zone);
|
|
4163
|
-
}
|
|
4164
|
-
else if (hasTaskState.change == 'macroTask') {
|
|
4165
|
-
zone.hasPendingMacrotasks = hasTaskState.macroTask;
|
|
4166
|
-
}
|
|
4167
|
-
}
|
|
4168
|
-
},
|
|
4169
|
-
onHandleError: (delegate, current, target, error) => {
|
|
4170
|
-
delegate.handleError(target, error);
|
|
4171
|
-
zone.runOutsideAngular(() => zone.onError.emit(error));
|
|
4172
|
-
return false;
|
|
4173
|
-
},
|
|
4174
|
-
});
|
|
4175
|
-
}
|
|
4176
|
-
function updateMicroTaskStatus(zone) {
|
|
4177
|
-
if (zone._hasPendingMicrotasks ||
|
|
4178
|
-
((zone.shouldCoalesceEventChangeDetection || zone.shouldCoalesceRunChangeDetection) &&
|
|
4179
|
-
zone.callbackScheduled === true)) {
|
|
4180
|
-
zone.hasPendingMicrotasks = true;
|
|
4181
|
-
}
|
|
4182
|
-
else {
|
|
4183
|
-
zone.hasPendingMicrotasks = false;
|
|
4184
|
-
}
|
|
4185
|
-
}
|
|
4186
|
-
function onEnter(zone) {
|
|
4187
|
-
zone._nesting++;
|
|
4188
|
-
if (zone.isStable) {
|
|
4189
|
-
zone.isStable = false;
|
|
4190
|
-
zone.onUnstable.emit(null);
|
|
4191
|
-
}
|
|
4192
|
-
}
|
|
4193
|
-
function onLeave(zone) {
|
|
4194
|
-
zone._nesting--;
|
|
4195
|
-
checkStable(zone);
|
|
4196
|
-
}
|
|
4197
|
-
/**
|
|
4198
|
-
* Provides a noop implementation of `NgZone` which does nothing. This zone requires explicit calls
|
|
4199
|
-
* to framework to perform rendering.
|
|
4200
|
-
*/
|
|
4201
|
-
class NoopNgZone {
|
|
4202
|
-
hasPendingMicrotasks = false;
|
|
4203
|
-
hasPendingMacrotasks = false;
|
|
4204
|
-
isStable = true;
|
|
4205
|
-
onUnstable = new EventEmitter();
|
|
4206
|
-
onMicrotaskEmpty = new EventEmitter();
|
|
4207
|
-
onStable = new EventEmitter();
|
|
4208
|
-
onError = new EventEmitter();
|
|
4209
|
-
run(fn, applyThis, applyArgs) {
|
|
4210
|
-
return fn.apply(applyThis, applyArgs);
|
|
4211
|
-
}
|
|
4212
|
-
runGuarded(fn, applyThis, applyArgs) {
|
|
4213
|
-
return fn.apply(applyThis, applyArgs);
|
|
4214
|
-
}
|
|
4215
|
-
runOutsideAngular(fn) {
|
|
4216
|
-
return fn();
|
|
4217
|
-
}
|
|
4218
|
-
runTask(fn, applyThis, applyArgs, name) {
|
|
4219
|
-
return fn.apply(applyThis, applyArgs);
|
|
4220
|
-
}
|
|
4221
|
-
}
|
|
4222
|
-
function shouldBeIgnoredByZone(applyArgs) {
|
|
4223
|
-
return hasApplyArgsData(applyArgs, '__ignore_ng_zone__');
|
|
4224
|
-
}
|
|
4225
|
-
function isSchedulerTick(applyArgs) {
|
|
4226
|
-
return hasApplyArgsData(applyArgs, '__scheduler_tick__');
|
|
4227
|
-
}
|
|
4228
|
-
function hasApplyArgsData(applyArgs, key) {
|
|
4229
|
-
if (!Array.isArray(applyArgs)) {
|
|
4230
|
-
return false;
|
|
4231
|
-
}
|
|
4232
|
-
// We should only ever get 1 arg passed through to invokeTask.
|
|
4233
|
-
// Short circuit here incase that behavior changes.
|
|
4234
|
-
if (applyArgs.length !== 1) {
|
|
4235
|
-
return false;
|
|
4236
|
-
}
|
|
4237
|
-
return applyArgs[0]?.data?.[key] === true;
|
|
4238
|
-
}
|
|
4239
|
-
|
|
4240
|
-
/**
|
|
4241
|
-
* Provides a hook for centralized exception handling.
|
|
4242
|
-
*
|
|
4243
|
-
* The default implementation of `ErrorHandler` prints error messages to the `console`. To
|
|
4244
|
-
* intercept error handling, write a custom exception handler that replaces this default as
|
|
4245
|
-
* appropriate for your app.
|
|
4246
|
-
*
|
|
4247
|
-
* @usageNotes
|
|
4248
|
-
* ### Example
|
|
4249
|
-
*
|
|
4250
|
-
* ```ts
|
|
4251
|
-
* class MyErrorHandler implements ErrorHandler {
|
|
4252
|
-
* handleError(error) {
|
|
4253
|
-
* // do something with the exception
|
|
4254
|
-
* }
|
|
4255
|
-
* }
|
|
4256
|
-
*
|
|
4257
|
-
* // Provide in standalone apps
|
|
4258
|
-
* bootstrapApplication(AppComponent, {
|
|
4259
|
-
* providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
|
|
4260
|
-
* })
|
|
4261
|
-
*
|
|
4262
|
-
* // Provide in module-based apps
|
|
4263
|
-
* @NgModule({
|
|
4264
|
-
* providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
|
|
4265
|
-
* })
|
|
4266
|
-
* class MyModule {}
|
|
4267
|
-
* ```
|
|
4268
|
-
*
|
|
4269
|
-
* @publicApi
|
|
4270
|
-
*
|
|
4271
|
-
* @see [Unhandled errors in Angular](best-practices/error-handling)
|
|
4272
|
-
*
|
|
4273
|
-
*/
|
|
4274
|
-
class ErrorHandler {
|
|
4275
|
-
/**
|
|
4276
|
-
* @internal
|
|
4277
|
-
*/
|
|
4278
|
-
_console = console;
|
|
4279
|
-
handleError(error) {
|
|
4280
|
-
this._console.error('ERROR', error);
|
|
4281
|
-
}
|
|
4282
|
-
}
|
|
4283
|
-
/**
|
|
4284
|
-
* `InjectionToken` used to configure how to call the `ErrorHandler`.
|
|
4285
|
-
*/
|
|
4286
|
-
const INTERNAL_APPLICATION_ERROR_HANDLER = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'internal error handler' : '', {
|
|
4287
|
-
providedIn: 'root',
|
|
4288
|
-
factory: () => {
|
|
4289
|
-
// The user's error handler may depend on things that create a circular dependency
|
|
4290
|
-
// so we inject it lazily.
|
|
4291
|
-
const zone = inject(NgZone);
|
|
4292
|
-
const injector = inject(EnvironmentInjector);
|
|
4293
|
-
let userErrorHandler;
|
|
4294
|
-
return (e) => {
|
|
4295
|
-
zone.runOutsideAngular(() => {
|
|
4296
|
-
if (injector.destroyed && !userErrorHandler) {
|
|
4297
|
-
setTimeout(() => {
|
|
4298
|
-
throw e;
|
|
4299
|
-
});
|
|
4300
|
-
}
|
|
4301
|
-
else {
|
|
4302
|
-
userErrorHandler ??= injector.get(ErrorHandler);
|
|
4303
|
-
userErrorHandler.handleError(e);
|
|
4304
|
-
}
|
|
4305
|
-
});
|
|
4306
|
-
};
|
|
4307
|
-
},
|
|
4308
|
-
});
|
|
4309
|
-
const errorHandlerEnvironmentInitializer = {
|
|
4310
|
-
provide: ENVIRONMENT_INITIALIZER,
|
|
4311
|
-
useValue: () => {
|
|
4312
|
-
const handler = inject(ErrorHandler, { optional: true });
|
|
4313
|
-
if ((typeof ngDevMode === 'undefined' || ngDevMode) && handler === null) {
|
|
4314
|
-
throw new RuntimeError(402 /* RuntimeErrorCode.MISSING_REQUIRED_INJECTABLE_IN_BOOTSTRAP */, `A required Injectable was not found in the dependency injection tree. ` +
|
|
4315
|
-
'If you are bootstrapping an NgModule, make sure that the `BrowserModule` is imported.');
|
|
4316
|
-
}
|
|
4317
|
-
},
|
|
4318
|
-
multi: true,
|
|
4319
|
-
};
|
|
4320
|
-
const globalErrorListeners = new InjectionToken(typeof ngDevMode !== undefined && ngDevMode ? 'GlobalErrorListeners' : '', {
|
|
4321
|
-
providedIn: 'root',
|
|
4322
|
-
factory: () => {
|
|
4323
|
-
if (typeof ngServerMode !== 'undefined' && ngServerMode) {
|
|
4324
|
-
return;
|
|
4325
|
-
}
|
|
4326
|
-
const window = inject(DOCUMENT).defaultView;
|
|
4327
|
-
if (!window) {
|
|
4328
|
-
return;
|
|
4329
|
-
}
|
|
4330
|
-
const errorHandler = inject(INTERNAL_APPLICATION_ERROR_HANDLER);
|
|
4331
|
-
const rejectionListener = (e) => {
|
|
4332
|
-
errorHandler(e.reason);
|
|
4333
|
-
e.preventDefault();
|
|
4334
|
-
};
|
|
4335
|
-
const errorListener = (e) => {
|
|
4336
|
-
if (e.error) {
|
|
4337
|
-
errorHandler(e.error);
|
|
4338
|
-
}
|
|
4339
|
-
else {
|
|
4340
|
-
errorHandler(new Error(ngDevMode
|
|
4341
|
-
? `An ErrorEvent with no error occurred. See Error.cause for details: ${e.message}`
|
|
4342
|
-
: e.message, { cause: e }));
|
|
4343
|
-
}
|
|
4344
|
-
e.preventDefault();
|
|
4345
|
-
};
|
|
4346
|
-
const setupEventListeners = () => {
|
|
4347
|
-
window.addEventListener('unhandledrejection', rejectionListener);
|
|
4348
|
-
window.addEventListener('error', errorListener);
|
|
4349
|
-
};
|
|
4350
|
-
// Angular doesn't have to run change detection whenever any asynchronous tasks are invoked in
|
|
4351
|
-
// the scope of this functionality.
|
|
4352
|
-
if (typeof Zone !== 'undefined') {
|
|
4353
|
-
Zone.root.run(setupEventListeners);
|
|
4354
|
-
}
|
|
4355
|
-
else {
|
|
4356
|
-
setupEventListeners();
|
|
4357
|
-
}
|
|
4358
|
-
inject(DestroyRef).onDestroy(() => {
|
|
4359
|
-
window.removeEventListener('error', errorListener);
|
|
4360
|
-
window.removeEventListener('unhandledrejection', rejectionListener);
|
|
4361
|
-
});
|
|
4362
|
-
},
|
|
4363
|
-
});
|
|
4364
|
-
/**
|
|
4365
|
-
* Provides an environment initializer which forwards unhandled errors to the ErrorHandler.
|
|
4366
|
-
*
|
|
4367
|
-
* The listeners added are for the window's 'unhandledrejection' and 'error' events.
|
|
4368
|
-
*
|
|
4369
|
-
* @publicApi
|
|
4370
|
-
*/
|
|
4371
|
-
function provideBrowserGlobalErrorListeners() {
|
|
4372
|
-
return makeEnvironmentProviders([
|
|
4373
|
-
provideEnvironmentInitializer(() => void inject(globalErrorListeners)),
|
|
4374
|
-
]);
|
|
4375
|
-
}
|
|
4376
|
-
|
|
4377
|
-
/**
|
|
4378
|
-
* Checks if the given `value` is a reactive `Signal`.
|
|
4379
|
-
*
|
|
4380
|
-
* @publicApi 17.0
|
|
4381
|
-
*/
|
|
4382
|
-
function isSignal(value) {
|
|
4383
|
-
return typeof value === 'function' && value[SIGNAL] !== undefined;
|
|
4384
|
-
}
|
|
4385
|
-
|
|
4386
|
-
/**
|
|
4387
|
-
* Utility function used during template type checking to extract the value from a `WritableSignal`.
|
|
4388
|
-
* @codeGenApi
|
|
4389
|
-
*/
|
|
4390
|
-
function ɵunwrapWritableSignal(value) {
|
|
4391
|
-
// Note: the function uses `WRITABLE_SIGNAL` as a brand instead of `WritableSignal<T>`,
|
|
4392
|
-
// because the latter incorrectly unwraps non-signal getter functions.
|
|
4393
|
-
return null;
|
|
4394
|
-
}
|
|
4395
|
-
/**
|
|
4396
|
-
* Create a `Signal` that can be set or updated directly.
|
|
4397
|
-
*/
|
|
4398
|
-
function signal(initialValue, options) {
|
|
4399
|
-
const [get, set, update] = createSignal(initialValue, options?.equal);
|
|
4400
|
-
const signalFn = get;
|
|
4401
|
-
const node = signalFn[SIGNAL];
|
|
4402
|
-
signalFn.set = set;
|
|
4403
|
-
signalFn.update = update;
|
|
4404
|
-
signalFn.asReadonly = signalAsReadonlyFn.bind(signalFn);
|
|
4405
|
-
if (ngDevMode) {
|
|
4406
|
-
signalFn.toString = () => `[Signal: ${signalFn()}]`;
|
|
4407
|
-
node.debugName = options?.debugName;
|
|
4408
|
-
}
|
|
4409
|
-
return signalFn;
|
|
4410
|
-
}
|
|
4411
|
-
function signalAsReadonlyFn() {
|
|
4412
|
-
const node = this[SIGNAL];
|
|
4413
|
-
if (node.readonlyFn === undefined) {
|
|
4414
|
-
const readonlyFn = () => this();
|
|
4415
|
-
readonlyFn[SIGNAL] = node;
|
|
4416
|
-
node.readonlyFn = readonlyFn;
|
|
4417
|
-
}
|
|
4418
|
-
return node.readonlyFn;
|
|
4419
|
-
}
|
|
4420
|
-
/**
|
|
4421
|
-
* Checks if the given `value` is a writeable signal.
|
|
4422
|
-
*/
|
|
4423
|
-
function isWritableSignal(value) {
|
|
4424
|
-
return isSignal(value) && typeof value.set === 'function';
|
|
4425
|
-
}
|
|
4426
|
-
|
|
4427
|
-
/**
|
|
4428
|
-
* Injectable that is notified when an `LView` is made aware of changes to application state.
|
|
4429
|
-
*/
|
|
4430
|
-
class ChangeDetectionScheduler {
|
|
4431
|
-
}
|
|
4432
|
-
/** Token used to indicate if zoneless was enabled via provideZonelessChangeDetection(). */
|
|
4433
|
-
const ZONELESS_ENABLED = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'Zoneless enabled' : '', { providedIn: 'root', factory: () => true });
|
|
4434
|
-
/** Token used to indicate `provideZonelessChangeDetection` was used. */
|
|
4435
|
-
const PROVIDED_ZONELESS = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'Zoneless provided' : '', { providedIn: 'root', factory: () => false });
|
|
4436
|
-
// TODO(atscott): Remove in v19. Scheduler should be done with runOutsideAngular.
|
|
4437
|
-
const SCHEDULE_IN_ROOT_ZONE = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'run changes outside zone in root' : '');
|
|
4438
|
-
|
|
4439
|
-
/**
|
|
4440
|
-
* Asserts that the current stack frame is not within a reactive context. Useful
|
|
4441
|
-
* to disallow certain code from running inside a reactive context (see {@link /api/core/rxjs-interop/toSignal toSignal})
|
|
4442
|
-
*
|
|
4443
|
-
* @param debugFn a reference to the function making the assertion (used for the error message).
|
|
4444
|
-
*
|
|
4445
|
-
* @publicApi
|
|
4446
|
-
*/
|
|
4447
|
-
function assertNotInReactiveContext(debugFn, extraContext) {
|
|
4448
|
-
// Taking a `Function` instead of a string name here prevents the un-minified name of the function
|
|
4449
|
-
// from being retained in the bundle regardless of minification.
|
|
4450
|
-
if (getActiveConsumer() !== null) {
|
|
4451
|
-
throw new RuntimeError(-602 /* RuntimeErrorCode.ASSERTION_NOT_INSIDE_REACTIVE_CONTEXT */, ngDevMode &&
|
|
4452
|
-
`${debugFn.name}() cannot be called from within a reactive context.${extraContext ? ` ${extraContext}` : ''}`);
|
|
4453
|
-
}
|
|
4454
|
-
}
|
|
4455
|
-
|
|
4456
|
-
class ViewContext {
|
|
4457
|
-
view;
|
|
4458
|
-
node;
|
|
4459
|
-
constructor(view, node) {
|
|
4460
|
-
this.view = view;
|
|
4461
|
-
this.node = node;
|
|
4462
|
-
}
|
|
4463
|
-
/**
|
|
4464
|
-
* @internal
|
|
4465
|
-
* @nocollapse
|
|
4466
|
-
*/
|
|
4467
|
-
static __NG_ELEMENT_ID__ = injectViewContext;
|
|
4468
|
-
}
|
|
4469
|
-
function injectViewContext() {
|
|
4470
|
-
return new ViewContext(getLView(), getCurrentTNode());
|
|
4471
|
-
}
|
|
4472
|
-
|
|
4473
|
-
/**
|
|
4474
|
-
* Service that keeps track of pending tasks contributing to the stableness of Angular
|
|
4475
|
-
* application. While several existing Angular services (ex.: `HttpClient`) will internally manage
|
|
4476
|
-
* tasks influencing stability, this API gives control over stability to library and application
|
|
4477
|
-
* developers for specific cases not covered by Angular internals.
|
|
4478
|
-
*
|
|
4479
|
-
* The concept of stability comes into play in several important scenarios:
|
|
4480
|
-
* - SSR process needs to wait for the application stability before serializing and sending rendered
|
|
4481
|
-
* HTML;
|
|
4482
|
-
* - tests might want to delay assertions until the application becomes stable;
|
|
4483
|
-
*
|
|
4484
|
-
* @usageNotes
|
|
4485
|
-
* ```ts
|
|
4486
|
-
* const pendingTasks = inject(PendingTasks);
|
|
4487
|
-
* const taskCleanup = pendingTasks.add();
|
|
4488
|
-
* // do work that should block application's stability and then:
|
|
4489
|
-
* taskCleanup();
|
|
4490
|
-
* ```
|
|
4491
|
-
*
|
|
4492
|
-
* @publicApi 20.0
|
|
4493
|
-
*/
|
|
4494
|
-
class PendingTasks {
|
|
4495
|
-
internalPendingTasks = inject(PendingTasksInternal);
|
|
4496
|
-
scheduler = inject(ChangeDetectionScheduler);
|
|
4497
|
-
errorHandler = inject(INTERNAL_APPLICATION_ERROR_HANDLER);
|
|
4498
|
-
/**
|
|
4499
|
-
* Adds a new task that should block application's stability.
|
|
4500
|
-
* @returns A cleanup function that removes a task when called.
|
|
4501
|
-
*/
|
|
4502
|
-
add() {
|
|
4503
|
-
const taskId = this.internalPendingTasks.add();
|
|
4504
|
-
return () => {
|
|
4505
|
-
if (!this.internalPendingTasks.has(taskId)) {
|
|
4506
|
-
// This pending task has already been cleared.
|
|
4507
|
-
return;
|
|
4508
|
-
}
|
|
4509
|
-
// Notifying the scheduler will hold application stability open until the next tick.
|
|
4510
|
-
this.scheduler.notify(11 /* NotificationSource.PendingTaskRemoved */);
|
|
4511
|
-
this.internalPendingTasks.remove(taskId);
|
|
4512
|
-
};
|
|
4513
|
-
}
|
|
4514
|
-
/**
|
|
4515
|
-
* Runs an asynchronous function and blocks the application's stability until the function completes.
|
|
4516
|
-
*
|
|
4517
|
-
* ```ts
|
|
4518
|
-
* pendingTasks.run(async () => {
|
|
4519
|
-
* const userData = await fetch('/api/user');
|
|
4520
|
-
* this.userData.set(userData);
|
|
4521
|
-
* });
|
|
4522
|
-
* ```
|
|
4523
|
-
*
|
|
4524
|
-
* @param fn The asynchronous function to execute
|
|
4525
|
-
* @developerPreview 19.0
|
|
4526
|
-
*/
|
|
4527
|
-
run(fn) {
|
|
4528
|
-
const removeTask = this.add();
|
|
4529
|
-
fn().catch(this.errorHandler).finally(removeTask);
|
|
4530
|
-
}
|
|
4531
|
-
/** @nocollapse */
|
|
4532
|
-
static ɵprov = /** @pureOrBreakMyCode */ /* @__PURE__ */ ɵɵdefineInjectable({
|
|
4533
|
-
token: PendingTasks,
|
|
4534
|
-
providedIn: 'root',
|
|
4535
|
-
factory: () => new PendingTasks(),
|
|
4536
|
-
});
|
|
4537
|
-
}
|
|
4538
|
-
|
|
4539
|
-
/**
|
|
4540
|
-
* A scheduler which manages the execution of effects.
|
|
4541
|
-
*/
|
|
4542
|
-
class EffectScheduler {
|
|
4543
|
-
/** @nocollapse */
|
|
4544
|
-
static ɵprov = /** @pureOrBreakMyCode */ /* @__PURE__ */ ɵɵdefineInjectable({
|
|
4545
|
-
token: EffectScheduler,
|
|
4546
|
-
providedIn: 'root',
|
|
4547
|
-
factory: () => new ZoneAwareEffectScheduler(),
|
|
4548
|
-
});
|
|
4549
|
-
}
|
|
4550
|
-
/**
|
|
4551
|
-
* A wrapper around `ZoneAwareQueueingScheduler` that schedules flushing via the microtask queue
|
|
4552
|
-
* when.
|
|
4553
|
-
*/
|
|
4554
|
-
class ZoneAwareEffectScheduler {
|
|
4555
|
-
dirtyEffectCount = 0;
|
|
4556
|
-
queues = new Map();
|
|
4557
|
-
add(handle) {
|
|
4558
|
-
this.enqueue(handle);
|
|
4559
|
-
this.schedule(handle);
|
|
4560
|
-
}
|
|
4561
|
-
schedule(handle) {
|
|
4562
|
-
if (!handle.dirty) {
|
|
4563
|
-
return;
|
|
4564
|
-
}
|
|
4565
|
-
this.dirtyEffectCount++;
|
|
4566
|
-
}
|
|
4567
|
-
remove(handle) {
|
|
4568
|
-
const zone = handle.zone;
|
|
4569
|
-
const queue = this.queues.get(zone);
|
|
4570
|
-
if (!queue.has(handle)) {
|
|
4571
|
-
return;
|
|
4572
|
-
}
|
|
4573
|
-
queue.delete(handle);
|
|
4574
|
-
if (handle.dirty) {
|
|
4575
|
-
this.dirtyEffectCount--;
|
|
4576
|
-
}
|
|
4577
|
-
}
|
|
4578
|
-
enqueue(handle) {
|
|
4579
|
-
const zone = handle.zone;
|
|
4580
|
-
if (!this.queues.has(zone)) {
|
|
4581
|
-
this.queues.set(zone, new Set());
|
|
4582
|
-
}
|
|
4583
|
-
const queue = this.queues.get(zone);
|
|
4584
|
-
if (queue.has(handle)) {
|
|
4585
|
-
return;
|
|
4586
|
-
}
|
|
4587
|
-
queue.add(handle);
|
|
4588
|
-
}
|
|
4589
|
-
/**
|
|
4590
|
-
* Run all scheduled effects.
|
|
4591
|
-
*
|
|
4592
|
-
* Execution order of effects within the same zone is guaranteed to be FIFO, but there is no
|
|
4593
|
-
* ordering guarantee between effects scheduled in different zones.
|
|
4594
|
-
*/
|
|
4595
|
-
flush() {
|
|
4596
|
-
while (this.dirtyEffectCount > 0) {
|
|
4597
|
-
let ranOneEffect = false;
|
|
4598
|
-
for (const [zone, queue] of this.queues) {
|
|
4599
|
-
// `zone` here must be defined.
|
|
4600
|
-
if (zone === null) {
|
|
4601
|
-
ranOneEffect ||= this.flushQueue(queue);
|
|
4602
|
-
}
|
|
4603
|
-
else {
|
|
4604
|
-
ranOneEffect ||= zone.run(() => this.flushQueue(queue));
|
|
4605
|
-
}
|
|
4606
|
-
}
|
|
4607
|
-
// Safeguard against infinite looping if somehow our dirty effect count gets out of sync with
|
|
4608
|
-
// the dirty flag across all the effects.
|
|
4609
|
-
if (!ranOneEffect) {
|
|
4610
|
-
this.dirtyEffectCount = 0;
|
|
4611
|
-
}
|
|
4612
|
-
}
|
|
4613
|
-
}
|
|
4614
|
-
flushQueue(queue) {
|
|
4615
|
-
let ranOneEffect = false;
|
|
4616
|
-
for (const handle of queue) {
|
|
4617
|
-
if (!handle.dirty) {
|
|
4618
|
-
continue;
|
|
4619
|
-
}
|
|
4620
|
-
this.dirtyEffectCount--;
|
|
4621
|
-
ranOneEffect = true;
|
|
4622
|
-
// TODO: what happens if this throws an error?
|
|
4623
|
-
handle.run();
|
|
4624
|
-
}
|
|
4625
|
-
return ranOneEffect;
|
|
4626
|
-
}
|
|
4627
|
-
}
|
|
4628
|
-
|
|
4629
|
-
export { AFTER_RENDER_SEQUENCES_TO_ADD, ANIMATIONS, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTAINER_HEADER_OFFSET, CONTEXT, ChangeDetectionScheduler, CheckNoChangesMode, DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, DECLARATION_VIEW, DEHYDRATED_VIEWS, DOCUMENT, DestroyRef, EFFECTS, EFFECTS_TO_SCHEDULE, EMBEDDED_VIEW_INJECTOR, EMPTY_ARRAY, EMPTY_OBJ, ENVIRONMENT, ENVIRONMENT_INITIALIZER, EffectScheduler, EnvironmentInjector, ErrorHandler, EventEmitter, FLAGS, HEADER_OFFSET, HOST, HYDRATION, ID, INJECTOR$1 as INJECTOR, INJECTOR as INJECTOR$1, INJECTOR_DEF_TYPES, INJECTOR_SCOPE, INTERNAL_APPLICATION_ERROR_HANDLER, InjectionToken, Injector, MATH_ML_NAMESPACE, MOVED_VIEWS, NATIVE, NEXT, NG_COMP_DEF, NG_DIR_DEF, NG_ELEMENT_ID, NG_FACTORY_DEF, NG_INJ_DEF, NG_MOD_DEF, NG_PIPE_DEF, NG_PROV_DEF, NgZone, NodeInjectorDestroyRef, NoopNgZone, NullInjector, ON_DESTROY_HOOKS, PARENT, PREORDER_HOOK_FLAGS, PROVIDED_ZONELESS, PendingTasks, PendingTasksInternal, QUERIES, R3Injector, REACTIVE_TEMPLATE_CONSUMER, RENDERER, RuntimeError, SCHEDULE_IN_ROOT_ZONE, SCHEDULE_IN_ROOT_ZONE_DEFAULT, SVG_NAMESPACE, TVIEW, T_HOST, VERSION, VIEW_REFS, Version, ViewContext, XSS_SECURITY_URL, ZONELESS_ENABLED, _global, addToArray, angularZoneInstanceIdProperty, arrayEquals, arrayInsert2, arraySplice, assertComponentType, assertDefined, assertDirectiveDef, assertDomNode, assertElement, assertEqual, assertFirstCreatePass, assertFirstUpdatePass, assertFunction, assertGreaterThan, assertGreaterThanOrEqual, assertHasParent, assertInInjectionContext, assertIndexInDeclRange, assertIndexInExpandoRange, assertIndexInRange, assertInjectImplementationNotEqual, assertLContainer, assertLView, assertLessThan, assertNgModuleType, assertNodeInjector, assertNotDefined, assertNotEqual, assertNotInReactiveContext, assertNotReactive, assertNotSame, assertNumber, assertNumberInRange, assertOneOf, assertParentView, assertProjectionSlots, assertSame, assertString, assertTIcu, assertTNode, assertTNodeCreationIndex, assertTNodeForLView, assertTNodeForTView, attachInjectFlag, concatStringsWithSpace, convertToBitFlags, createInjector, createInjectorWithoutInjectorInstances, cyclicDependencyError, cyclicDependencyErrorWithDetails, debugStringifyTypeForError, decreaseElementDepthCount, deepForEach, emitEffectCreatedEvent, emitInjectEvent, emitInjectorToCreateInstanceEvent, emitInstanceCreatedByInjectorEvent, emitProviderConfiguredEvent, enterDI, enterSkipHydrationBlock, enterView, errorHandlerEnvironmentInitializer, fillProperties, flatten, formatRuntimeError, forwardRef, getBindingIndex, getBindingRoot, getBindingsEnabled, getClosureSafeProperty, getComponentDef, getComponentLViewByIndex, getConstant, getContextLView, getCurrentDirectiveDef, getCurrentDirectiveIndex, getCurrentParentTNode, getCurrentQueryIndex, getCurrentTNode, getCurrentTNodePlaceholderOk, getDirectiveDef, getDirectiveDefOrThrow, getElementDepthCount, getFactoryDef, getInjectableDef, getInjectorDef, getLView, getLViewParent, getNamespace, getNativeByIndex, getNativeByTNode, getNativeByTNodeOrNull, getNgModuleDef, getNgModuleDefOrThrow, getNullInjector, getOrCreateLViewCleanup, getOrCreateTViewCleanup, getPipeDef, getSelectedIndex, getSelectedTNode, getTNode, getTView, hasI18n, importProvidersFrom, increaseElementDepthCount, incrementBindingIndex, initNgDevMode, inject, injectRootLimpMode, internalImportProvidersFrom, isClassProvider, isComponentDef, isComponentHost, isContentQueryHost, isCreationMode, isCurrentTNodeParent, isDestroyed, isDirectiveHost, isEnvironmentProviders, isExhaustiveCheckNoChanges, isForwardRef, isInCheckNoChangesMode, isInI18nBlock, isInInjectionContext, isInSkipHydrationBlock, isInjectable, isLContainer, isLView, isProjectionTNode, isRefreshingViews, isRootView, isSignal, isSkipHydrationRootTNode, isStandalone, isTypeProvider, isWritableSignal, keyValueArrayGet, keyValueArrayIndexOf, keyValueArraySet, lastNodeWasCreated, leaveDI, leaveSkipHydrationBlock, leaveView, load, makeEnvironmentProviders, markAncestorsForTraversal, markViewForRefresh, newArray, nextBindingIndex, nextContextImpl, noop, provideBrowserGlobalErrorListeners, provideEnvironmentInitializer, providerToFactory, removeFromArray, removeLViewOnDestroy, renderStringify, requiresRefreshOrTraversal, resetPreOrderHookFlags, resolveForwardRef, runInInjectionContext, runInInjectorProfilerContext, scheduleCallbackWithMicrotask, scheduleCallbackWithRafRace, setBindingIndex, setBindingRootForHostBindings, setCurrentDirectiveIndex, setCurrentQueryIndex, setCurrentTNode, setCurrentTNodeAsNotParent, setInI18nBlock, setInjectImplementation, setInjectorProfiler, setInjectorProfilerContext, setIsInCheckNoChangesMode, setIsRefreshingViews, setSelectedIndex, signal, signalAsReadonlyFn, store, storeCleanupWithContext, storeLViewOnDestroy, stringify, stringifyForError, throwError, throwProviderNotFoundError, truncateMiddle, unwrapLView, unwrapRNode, updateAncestorTraversalFlagsOnAttach, viewAttachedToChangeDetector, viewAttachedToContainer, walkProviderTree, walkUpViews, wasLastNodeCreated, ɵunwrapWritableSignal, ɵɵdefineInjectable, ɵɵdefineInjector, ɵɵdisableBindings, ɵɵenableBindings, ɵɵinject, ɵɵinvalidFactoryDep, ɵɵnamespaceHTML, ɵɵnamespaceMathML, ɵɵnamespaceSVG, ɵɵresetView, ɵɵrestoreView };
|
|
4630
|
-
//# sourceMappingURL=_root_effect_scheduler-chunk.mjs.map
|