1 | /**
|
2 | * @license Angular v13.0.2
|
3 | * (c) 2010-2021 Google LLC. https://angular.io/
|
4 | * License: MIT
|
5 | */
|
6 |
|
7 | import * as i0 from '@angular/core';
|
8 | import { Version, Injector, ChangeDetectorRef, Testability, TestabilityRegistry, ApplicationRef, SimpleChange, NgZone, ComponentFactoryResolver, Directive, Inject, ElementRef, EventEmitter, Compiler, resolveForwardRef, NgModule, isDevMode } from '@angular/core';
|
9 | import { __decorate, __param, __metadata } from 'tslib';
|
10 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
11 |
|
12 | /**
|
13 | * @license
|
14 | * Copyright Google LLC All Rights Reserved.
|
15 | *
|
16 | * Use of this source code is governed by an MIT-style license that can be
|
17 | * found in the LICENSE file at https://angular.io/license
|
18 | */
|
19 | /**
|
20 | * @publicApi
|
21 | */
|
22 | const VERSION = new Version('13.0.2');
|
23 |
|
24 | /**
|
25 | * @license
|
26 | * Copyright Google LLC All Rights Reserved.
|
27 | *
|
28 | * Use of this source code is governed by an MIT-style license that can be
|
29 | * found in the LICENSE file at https://angular.io/license
|
30 | */
|
31 | function noNg() {
|
32 | throw new Error('AngularJS v1.x is not loaded!');
|
33 | }
|
34 | const noNgElement = (() => noNg());
|
35 | noNgElement.cleanData = noNg;
|
36 | let angular = {
|
37 | bootstrap: noNg,
|
38 | module: noNg,
|
39 | element: noNgElement,
|
40 | injector: noNg,
|
41 | version: undefined,
|
42 | resumeBootstrap: noNg,
|
43 | getTestability: noNg
|
44 | };
|
45 | try {
|
46 | if (window.hasOwnProperty('angular')) {
|
47 | angular = window.angular;
|
48 | }
|
49 | }
|
50 | catch (_a) {
|
51 | // ignore in CJS mode.
|
52 | }
|
53 | /**
|
54 | * @deprecated Use `setAngularJSGlobal` instead.
|
55 | *
|
56 | * @publicApi
|
57 | */
|
58 | function setAngularLib(ng) {
|
59 | setAngularJSGlobal(ng);
|
60 | }
|
61 | /**
|
62 | * @deprecated Use `getAngularJSGlobal` instead.
|
63 | *
|
64 | * @publicApi
|
65 | */
|
66 | function getAngularLib() {
|
67 | return getAngularJSGlobal();
|
68 | }
|
69 | /**
|
70 | * Resets the AngularJS global.
|
71 | *
|
72 | * Used when AngularJS is loaded lazily, and not available on `window`.
|
73 | *
|
74 | * @publicApi
|
75 | */
|
76 | function setAngularJSGlobal(ng) {
|
77 | angular = ng;
|
78 | }
|
79 | /**
|
80 | * Returns the current AngularJS global.
|
81 | *
|
82 | * @publicApi
|
83 | */
|
84 | function getAngularJSGlobal() {
|
85 | return angular;
|
86 | }
|
87 | const bootstrap = (e, modules, config) => angular.bootstrap(e, modules, config);
|
88 | // Do not declare as `module` to avoid webpack bug
|
89 | // (see https://github.com/angular/angular/issues/30050).
|
90 | const module_ = (prefix, dependencies) => angular.module(prefix, dependencies);
|
91 | const element = (e => angular.element(e));
|
92 | element.cleanData = nodes => angular.element.cleanData(nodes);
|
93 | const injector = (modules, strictDi) => angular.injector(modules, strictDi);
|
94 | const resumeBootstrap = () => angular.resumeBootstrap();
|
95 | const getTestability = e => angular.getTestability(e);
|
96 |
|
97 | /**
|
98 | * @license
|
99 | * Copyright Google LLC All Rights Reserved.
|
100 | *
|
101 | * Use of this source code is governed by an MIT-style license that can be
|
102 | * found in the LICENSE file at https://angular.io/license
|
103 | */
|
104 | const $COMPILE = '$compile';
|
105 | const $CONTROLLER = '$controller';
|
106 | const $DELEGATE = '$delegate';
|
107 | const $EXCEPTION_HANDLER = '$exceptionHandler';
|
108 | const $HTTP_BACKEND = '$httpBackend';
|
109 | const $INJECTOR = '$injector';
|
110 | const $INTERVAL = '$interval';
|
111 | const $PARSE = '$parse';
|
112 | const $PROVIDE = '$provide';
|
113 | const $ROOT_ELEMENT = '$rootElement';
|
114 | const $ROOT_SCOPE = '$rootScope';
|
115 | const $SCOPE = '$scope';
|
116 | const $TEMPLATE_CACHE = '$templateCache';
|
117 | const $TEMPLATE_REQUEST = '$templateRequest';
|
118 | const $$TESTABILITY = '$$testability';
|
119 | const COMPILER_KEY = '$$angularCompiler';
|
120 | const DOWNGRADED_MODULE_COUNT_KEY = '$$angularDowngradedModuleCount';
|
121 | const GROUP_PROJECTABLE_NODES_KEY = '$$angularGroupProjectableNodes';
|
122 | const INJECTOR_KEY = '$$angularInjector';
|
123 | const LAZY_MODULE_REF = '$$angularLazyModuleRef';
|
124 | const NG_ZONE_KEY = '$$angularNgZone';
|
125 | const UPGRADE_APP_TYPE_KEY = '$$angularUpgradeAppType';
|
126 | const REQUIRE_INJECTOR = '?^^' + INJECTOR_KEY;
|
127 | const REQUIRE_NG_MODEL = '?ngModel';
|
128 | const UPGRADE_MODULE_NAME = '$$UpgradeModule';
|
129 |
|
130 | /**
|
131 | * @license
|
132 | * Copyright Google LLC All Rights Reserved.
|
133 | *
|
134 | * Use of this source code is governed by an MIT-style license that can be
|
135 | * found in the LICENSE file at https://angular.io/license
|
136 | */
|
137 | /**
|
138 | * A `PropertyBinding` represents a mapping between a property name
|
139 | * and an attribute name. It is parsed from a string of the form
|
140 | * `"prop: attr"`; or simply `"propAndAttr" where the property
|
141 | * and attribute have the same identifier.
|
142 | */
|
143 | class PropertyBinding {
|
144 | constructor(prop, attr) {
|
145 | this.prop = prop;
|
146 | this.attr = attr;
|
147 | this.parseBinding();
|
148 | }
|
149 | parseBinding() {
|
150 | this.bracketAttr = `[${this.attr}]`;
|
151 | this.parenAttr = `(${this.attr})`;
|
152 | this.bracketParenAttr = `[(${this.attr})]`;
|
153 | const capitalAttr = this.attr.charAt(0).toUpperCase() + this.attr.substr(1);
|
154 | this.onAttr = `on${capitalAttr}`;
|
155 | this.bindAttr = `bind${capitalAttr}`;
|
156 | this.bindonAttr = `bindon${capitalAttr}`;
|
157 | }
|
158 | }
|
159 |
|
160 | /**
|
161 | * @license
|
162 | * Copyright Google LLC All Rights Reserved.
|
163 | *
|
164 | * Use of this source code is governed by an MIT-style license that can be
|
165 | * found in the LICENSE file at https://angular.io/license
|
166 | */
|
167 | const DIRECTIVE_PREFIX_REGEXP = /^(?:x|data)[:\-_]/i;
|
168 | const DIRECTIVE_SPECIAL_CHARS_REGEXP = /[:\-_]+(.)/g;
|
169 | function onError(e) {
|
170 | // TODO: (misko): We seem to not have a stack trace here!
|
171 | if (console.error) {
|
172 | console.error(e, e.stack);
|
173 | }
|
174 | else {
|
175 | // tslint:disable-next-line:no-console
|
176 | console.log(e, e.stack);
|
177 | }
|
178 | throw e;
|
179 | }
|
180 | /**
|
181 | * Clean the jqLite/jQuery data on the element and all its descendants.
|
182 | * Equivalent to how jqLite/jQuery invoke `cleanData()` on an Element when removed:
|
183 | * https://github.com/angular/angular.js/blob/2e72ea13fa98bebf6ed4b5e3c45eaf5f990ed16f/src/jqLite.js#L349-L355
|
184 | * https://github.com/jquery/jquery/blob/6984d1747623dbc5e87fd6c261a5b6b1628c107c/src/manipulation.js#L182
|
185 | *
|
186 | * NOTE:
|
187 | * `cleanData()` will also invoke the AngularJS `$destroy` DOM event on the element:
|
188 | * https://github.com/angular/angular.js/blob/2e72ea13fa98bebf6ed4b5e3c45eaf5f990ed16f/src/Angular.js#L1932-L1945
|
189 | *
|
190 | * @param node The DOM node whose data needs to be cleaned.
|
191 | */
|
192 | function cleanData(node) {
|
193 | element.cleanData([node]);
|
194 | if (isParentNode(node)) {
|
195 | element.cleanData(node.querySelectorAll('*'));
|
196 | }
|
197 | }
|
198 | function controllerKey(name) {
|
199 | return '$' + name + 'Controller';
|
200 | }
|
201 | /**
|
202 | * Destroy an AngularJS app given the app `$injector`.
|
203 | *
|
204 | * NOTE: Destroying an app is not officially supported by AngularJS, but try to do our best by
|
205 | * destroying `$rootScope` and clean the jqLite/jQuery data on `$rootElement` and all
|
206 | * descendants.
|
207 | *
|
208 | * @param $injector The `$injector` of the AngularJS app to destroy.
|
209 | */
|
210 | function destroyApp($injector) {
|
211 | const $rootElement = $injector.get($ROOT_ELEMENT);
|
212 | const $rootScope = $injector.get($ROOT_SCOPE);
|
213 | $rootScope.$destroy();
|
214 | cleanData($rootElement[0]);
|
215 | }
|
216 | function directiveNormalize(name) {
|
217 | return name.replace(DIRECTIVE_PREFIX_REGEXP, '')
|
218 | .replace(DIRECTIVE_SPECIAL_CHARS_REGEXP, (_, letter) => letter.toUpperCase());
|
219 | }
|
220 | function getTypeName(type) {
|
221 | // Return the name of the type or the first line of its stringified version.
|
222 | return type.overriddenName || type.name || type.toString().split('\n')[0];
|
223 | }
|
224 | function getDowngradedModuleCount($injector) {
|
225 | return $injector.has(DOWNGRADED_MODULE_COUNT_KEY) ? $injector.get(DOWNGRADED_MODULE_COUNT_KEY) :
|
226 | 0;
|
227 | }
|
228 | function getUpgradeAppType($injector) {
|
229 | return $injector.has(UPGRADE_APP_TYPE_KEY) ? $injector.get(UPGRADE_APP_TYPE_KEY) :
|
230 | 0 /* None */;
|
231 | }
|
232 | function isFunction(value) {
|
233 | return typeof value === 'function';
|
234 | }
|
235 | function isParentNode(node) {
|
236 | return isFunction(node.querySelectorAll);
|
237 | }
|
238 | function validateInjectionKey($injector, downgradedModule, injectionKey, attemptedAction) {
|
239 | const upgradeAppType = getUpgradeAppType($injector);
|
240 | const downgradedModuleCount = getDowngradedModuleCount($injector);
|
241 | // Check for common errors.
|
242 | switch (upgradeAppType) {
|
243 | case 1 /* Dynamic */:
|
244 | case 2 /* Static */:
|
245 | if (downgradedModule) {
|
246 | throw new Error(`Error while ${attemptedAction}: 'downgradedModule' unexpectedly specified.\n` +
|
247 | 'You should not specify a value for \'downgradedModule\', unless you are downgrading ' +
|
248 | 'more than one Angular module (via \'downgradeModule()\').');
|
249 | }
|
250 | break;
|
251 | case 3 /* Lite */:
|
252 | if (!downgradedModule && (downgradedModuleCount >= 2)) {
|
253 | throw new Error(`Error while ${attemptedAction}: 'downgradedModule' not specified.\n` +
|
254 | 'This application contains more than one downgraded Angular module, thus you need to ' +
|
255 | 'always specify \'downgradedModule\' when downgrading components and injectables.');
|
256 | }
|
257 | if (!$injector.has(injectionKey)) {
|
258 | throw new Error(`Error while ${attemptedAction}: Unable to find the specified downgraded module.\n` +
|
259 | 'Did you forget to downgrade an Angular module or include it in the AngularJS ' +
|
260 | 'application?');
|
261 | }
|
262 | break;
|
263 | default:
|
264 | throw new Error(`Error while ${attemptedAction}: Not a valid '@angular/upgrade' application.\n` +
|
265 | 'Did you forget to downgrade an Angular module or include it in the AngularJS ' +
|
266 | 'application?');
|
267 | }
|
268 | }
|
269 | class Deferred {
|
270 | constructor() {
|
271 | this.promise = new Promise((res, rej) => {
|
272 | this.resolve = res;
|
273 | this.reject = rej;
|
274 | });
|
275 | }
|
276 | }
|
277 | /**
|
278 | * @return Whether the passed-in component implements the subset of the
|
279 | * `ControlValueAccessor` interface needed for AngularJS `ng-model`
|
280 | * compatibility.
|
281 | */
|
282 | function supportsNgModel(component) {
|
283 | return typeof component.writeValue === 'function' &&
|
284 | typeof component.registerOnChange === 'function';
|
285 | }
|
286 | /**
|
287 | * Glue the AngularJS `NgModelController` (if it exists) to the component
|
288 | * (if it implements the needed subset of the `ControlValueAccessor` interface).
|
289 | */
|
290 | function hookupNgModel(ngModel, component) {
|
291 | if (ngModel && supportsNgModel(component)) {
|
292 | ngModel.$render = () => {
|
293 | component.writeValue(ngModel.$viewValue);
|
294 | };
|
295 | component.registerOnChange(ngModel.$setViewValue.bind(ngModel));
|
296 | if (typeof component.registerOnTouched === 'function') {
|
297 | component.registerOnTouched(ngModel.$setTouched.bind(ngModel));
|
298 | }
|
299 | }
|
300 | }
|
301 | /**
|
302 | * Test two values for strict equality, accounting for the fact that `NaN !== NaN`.
|
303 | */
|
304 | function strictEquals(val1, val2) {
|
305 | return val1 === val2 || (val1 !== val1 && val2 !== val2);
|
306 | }
|
307 |
|
308 | /**
|
309 | * @license
|
310 | * Copyright Google LLC All Rights Reserved.
|
311 | *
|
312 | * Use of this source code is governed by an MIT-style license that can be
|
313 | * found in the LICENSE file at https://angular.io/license
|
314 | */
|
315 | const INITIAL_VALUE$1 = {
|
316 | __UNINITIALIZED__: true
|
317 | };
|
318 | class DowngradeComponentAdapter {
|
319 | constructor(element, attrs, scope, ngModel, parentInjector, $compile, $parse, componentFactory, wrapCallback) {
|
320 | this.element = element;
|
321 | this.attrs = attrs;
|
322 | this.scope = scope;
|
323 | this.ngModel = ngModel;
|
324 | this.parentInjector = parentInjector;
|
325 | this.$compile = $compile;
|
326 | this.$parse = $parse;
|
327 | this.componentFactory = componentFactory;
|
328 | this.wrapCallback = wrapCallback;
|
329 | this.implementsOnChanges = false;
|
330 | this.inputChangeCount = 0;
|
331 | this.inputChanges = {};
|
332 | this.componentScope = scope.$new();
|
333 | }
|
334 | compileContents() {
|
335 | const compiledProjectableNodes = [];
|
336 | const projectableNodes = this.groupProjectableNodes();
|
337 | const linkFns = projectableNodes.map(nodes => this.$compile(nodes));
|
338 | this.element.empty();
|
339 | linkFns.forEach(linkFn => {
|
340 | linkFn(this.scope, (clone) => {
|
341 | compiledProjectableNodes.push(clone);
|
342 | this.element.append(clone);
|
343 | });
|
344 | });
|
345 | return compiledProjectableNodes;
|
346 | }
|
347 | createComponent(projectableNodes) {
|
348 | const providers = [{ provide: $SCOPE, useValue: this.componentScope }];
|
349 | const childInjector = Injector.create({ providers: providers, parent: this.parentInjector, name: 'DowngradeComponentAdapter' });
|
350 | this.componentRef =
|
351 | this.componentFactory.create(childInjector, projectableNodes, this.element[0]);
|
352 | this.viewChangeDetector = this.componentRef.injector.get(ChangeDetectorRef);
|
353 | this.changeDetector = this.componentRef.changeDetectorRef;
|
354 | this.component = this.componentRef.instance;
|
355 | // testability hook is commonly added during component bootstrap in
|
356 | // packages/core/src/application_ref.bootstrap()
|
357 | // in downgraded application, component creation will take place here as well as adding the
|
358 | // testability hook.
|
359 | const testability = this.componentRef.injector.get(Testability, null);
|
360 | if (testability) {
|
361 | this.componentRef.injector.get(TestabilityRegistry)
|
362 | .registerApplication(this.componentRef.location.nativeElement, testability);
|
363 | }
|
364 | hookupNgModel(this.ngModel, this.component);
|
365 | }
|
366 | setupInputs(manuallyAttachView, propagateDigest = true) {
|
367 | const attrs = this.attrs;
|
368 | const inputs = this.componentFactory.inputs || [];
|
369 | for (let i = 0; i < inputs.length; i++) {
|
370 | const input = new PropertyBinding(inputs[i].propName, inputs[i].templateName);
|
371 | let expr = null;
|
372 | if (attrs.hasOwnProperty(input.attr)) {
|
373 | const observeFn = (prop => {
|
374 | let prevValue = INITIAL_VALUE$1;
|
375 | return (currValue) => {
|
376 | // Initially, both `$observe()` and `$watch()` will call this function.
|
377 | if (!strictEquals(prevValue, currValue)) {
|
378 | if (prevValue === INITIAL_VALUE$1) {
|
379 | prevValue = currValue;
|
380 | }
|
381 | this.updateInput(prop, prevValue, currValue);
|
382 | prevValue = currValue;
|
383 | }
|
384 | };
|
385 | })(input.prop);
|
386 | attrs.$observe(input.attr, observeFn);
|
387 | // Use `$watch()` (in addition to `$observe()`) in order to initialize the input in time
|
388 | // for `ngOnChanges()`. This is necessary if we are already in a `$digest`, which means that
|
389 | // `ngOnChanges()` (which is called by a watcher) will run before the `$observe()` callback.
|
390 | let unwatch = this.componentScope.$watch(() => {
|
391 | unwatch();
|
392 | unwatch = null;
|
393 | observeFn(attrs[input.attr]);
|
394 | });
|
395 | }
|
396 | else if (attrs.hasOwnProperty(input.bindAttr)) {
|
397 | expr = attrs[input.bindAttr];
|
398 | }
|
399 | else if (attrs.hasOwnProperty(input.bracketAttr)) {
|
400 | expr = attrs[input.bracketAttr];
|
401 | }
|
402 | else if (attrs.hasOwnProperty(input.bindonAttr)) {
|
403 | expr = attrs[input.bindonAttr];
|
404 | }
|
405 | else if (attrs.hasOwnProperty(input.bracketParenAttr)) {
|
406 | expr = attrs[input.bracketParenAttr];
|
407 | }
|
408 | if (expr != null) {
|
409 | const watchFn = (prop => (currValue, prevValue) => this.updateInput(prop, prevValue, currValue))(input.prop);
|
410 | this.componentScope.$watch(expr, watchFn);
|
411 | }
|
412 | }
|
413 | // Invoke `ngOnChanges()` and Change Detection (when necessary)
|
414 | const detectChanges = () => this.changeDetector.detectChanges();
|
415 | const prototype = this.componentFactory.componentType.prototype;
|
416 | this.implementsOnChanges = !!(prototype && prototype.ngOnChanges);
|
417 | this.componentScope.$watch(() => this.inputChangeCount, this.wrapCallback(() => {
|
418 | // Invoke `ngOnChanges()`
|
419 | if (this.implementsOnChanges) {
|
420 | const inputChanges = this.inputChanges;
|
421 | this.inputChanges = {};
|
422 | this.component.ngOnChanges(inputChanges);
|
423 | }
|
424 | this.viewChangeDetector.markForCheck();
|
425 | // If opted out of propagating digests, invoke change detection when inputs change.
|
426 | if (!propagateDigest) {
|
427 | detectChanges();
|
428 | }
|
429 | }));
|
430 | // If not opted out of propagating digests, invoke change detection on every digest
|
431 | if (propagateDigest) {
|
432 | this.componentScope.$watch(this.wrapCallback(detectChanges));
|
433 | }
|
434 | // If necessary, attach the view so that it will be dirty-checked.
|
435 | // (Allow time for the initial input values to be set and `ngOnChanges()` to be called.)
|
436 | if (manuallyAttachView || !propagateDigest) {
|
437 | let unwatch = this.componentScope.$watch(() => {
|
438 | unwatch();
|
439 | unwatch = null;
|
440 | const appRef = this.parentInjector.get(ApplicationRef);
|
441 | appRef.attachView(this.componentRef.hostView);
|
442 | });
|
443 | }
|
444 | }
|
445 | setupOutputs() {
|
446 | const attrs = this.attrs;
|
447 | const outputs = this.componentFactory.outputs || [];
|
448 | for (let j = 0; j < outputs.length; j++) {
|
449 | const output = new PropertyBinding(outputs[j].propName, outputs[j].templateName);
|
450 | const bindonAttr = output.bindonAttr.substring(0, output.bindonAttr.length - 6);
|
451 | const bracketParenAttr = `[(${output.bracketParenAttr.substring(2, output.bracketParenAttr.length - 8)})]`;
|
452 | // order below is important - first update bindings then evaluate expressions
|
453 | if (attrs.hasOwnProperty(bindonAttr)) {
|
454 | this.subscribeToOutput(output, attrs[bindonAttr], true);
|
455 | }
|
456 | if (attrs.hasOwnProperty(bracketParenAttr)) {
|
457 | this.subscribeToOutput(output, attrs[bracketParenAttr], true);
|
458 | }
|
459 | if (attrs.hasOwnProperty(output.onAttr)) {
|
460 | this.subscribeToOutput(output, attrs[output.onAttr]);
|
461 | }
|
462 | if (attrs.hasOwnProperty(output.parenAttr)) {
|
463 | this.subscribeToOutput(output, attrs[output.parenAttr]);
|
464 | }
|
465 | }
|
466 | }
|
467 | subscribeToOutput(output, expr, isAssignment = false) {
|
468 | const getter = this.$parse(expr);
|
469 | const setter = getter.assign;
|
470 | if (isAssignment && !setter) {
|
471 | throw new Error(`Expression '${expr}' is not assignable!`);
|
472 | }
|
473 | const emitter = this.component[output.prop];
|
474 | if (emitter) {
|
475 | emitter.subscribe({
|
476 | next: isAssignment ? (v) => setter(this.scope, v) :
|
477 | (v) => getter(this.scope, { '$event': v })
|
478 | });
|
479 | }
|
480 | else {
|
481 | throw new Error(`Missing emitter '${output.prop}' on component '${getTypeName(this.componentFactory.componentType)}'!`);
|
482 | }
|
483 | }
|
484 | registerCleanup() {
|
485 | const testabilityRegistry = this.componentRef.injector.get(TestabilityRegistry);
|
486 | const destroyComponentRef = this.wrapCallback(() => this.componentRef.destroy());
|
487 | let destroyed = false;
|
488 | this.element.on('$destroy', () => {
|
489 | // The `$destroy` event may have been triggered by the `cleanData()` call in the
|
490 | // `componentScope` `$destroy` handler below. In that case, we don't want to call
|
491 | // `componentScope.$destroy()` again.
|
492 | if (!destroyed)
|
493 | this.componentScope.$destroy();
|
494 | });
|
495 | this.componentScope.$on('$destroy', () => {
|
496 | if (!destroyed) {
|
497 | destroyed = true;
|
498 | testabilityRegistry.unregisterApplication(this.componentRef.location.nativeElement);
|
499 | // The `componentScope` might be getting destroyed, because an ancestor element is being
|
500 | // removed/destroyed. If that is the case, jqLite/jQuery would normally invoke `cleanData()`
|
501 | // on the removed element and all descendants.
|
502 | // https://github.com/angular/angular.js/blob/2e72ea13fa98bebf6ed4b5e3c45eaf5f990ed16f/src/jqLite.js#L349-L355
|
503 | // https://github.com/jquery/jquery/blob/6984d1747623dbc5e87fd6c261a5b6b1628c107c/src/manipulation.js#L182
|
504 | //
|
505 | // Here, however, `destroyComponentRef()` may under some circumstances remove the element
|
506 | // from the DOM and therefore it will no longer be a descendant of the removed element when
|
507 | // `cleanData()` is called. This would result in a memory leak, because the element's data
|
508 | // and event handlers (and all objects directly or indirectly referenced by them) would be
|
509 | // retained.
|
510 | //
|
511 | // To ensure the element is always properly cleaned up, we manually call `cleanData()` on
|
512 | // this element and its descendants before destroying the `ComponentRef`.
|
513 | cleanData(this.element[0]);
|
514 | destroyComponentRef();
|
515 | }
|
516 | });
|
517 | }
|
518 | getInjector() {
|
519 | return this.componentRef.injector;
|
520 | }
|
521 | updateInput(prop, prevValue, currValue) {
|
522 | if (this.implementsOnChanges) {
|
523 | this.inputChanges[prop] = new SimpleChange(prevValue, currValue, prevValue === currValue);
|
524 | }
|
525 | this.inputChangeCount++;
|
526 | this.component[prop] = currValue;
|
527 | }
|
528 | groupProjectableNodes() {
|
529 | let ngContentSelectors = this.componentFactory.ngContentSelectors;
|
530 | return groupNodesBySelector(ngContentSelectors, this.element.contents());
|
531 | }
|
532 | }
|
533 | /**
|
534 | * Group a set of DOM nodes into `ngContent` groups, based on the given content selectors.
|
535 | */
|
536 | function groupNodesBySelector(ngContentSelectors, nodes) {
|
537 | const projectableNodes = [];
|
538 | for (let i = 0, ii = ngContentSelectors.length; i < ii; ++i) {
|
539 | projectableNodes[i] = [];
|
540 | }
|
541 | for (let j = 0, jj = nodes.length; j < jj; ++j) {
|
542 | const node = nodes[j];
|
543 | const ngContentIndex = findMatchingNgContentIndex(node, ngContentSelectors);
|
544 | if (ngContentIndex != null) {
|
545 | projectableNodes[ngContentIndex].push(node);
|
546 | }
|
547 | }
|
548 | return projectableNodes;
|
549 | }
|
550 | function findMatchingNgContentIndex(element, ngContentSelectors) {
|
551 | const ngContentIndices = [];
|
552 | let wildcardNgContentIndex = -1;
|
553 | for (let i = 0; i < ngContentSelectors.length; i++) {
|
554 | const selector = ngContentSelectors[i];
|
555 | if (selector === '*') {
|
556 | wildcardNgContentIndex = i;
|
557 | }
|
558 | else {
|
559 | if (matchesSelector(element, selector)) {
|
560 | ngContentIndices.push(i);
|
561 | }
|
562 | }
|
563 | }
|
564 | ngContentIndices.sort();
|
565 | if (wildcardNgContentIndex !== -1) {
|
566 | ngContentIndices.push(wildcardNgContentIndex);
|
567 | }
|
568 | return ngContentIndices.length ? ngContentIndices[0] : null;
|
569 | }
|
570 | let _matches;
|
571 | function matchesSelector(el, selector) {
|
572 | if (!_matches) {
|
573 | const elProto = Element.prototype;
|
574 | _matches = elProto.matches || elProto.matchesSelector || elProto.mozMatchesSelector ||
|
575 | elProto.msMatchesSelector || elProto.oMatchesSelector || elProto.webkitMatchesSelector;
|
576 | }
|
577 | return el.nodeType === Node.ELEMENT_NODE ? _matches.call(el, selector) : false;
|
578 | }
|
579 |
|
580 | /**
|
581 | * @license
|
582 | * Copyright Google LLC All Rights Reserved.
|
583 | *
|
584 | * Use of this source code is governed by an MIT-style license that can be
|
585 | * found in the LICENSE file at https://angular.io/license
|
586 | */
|
587 | function isThenable(obj) {
|
588 | return !!obj && isFunction(obj.then);
|
589 | }
|
590 | /**
|
591 | * Synchronous, promise-like object.
|
592 | */
|
593 | class SyncPromise {
|
594 | constructor() {
|
595 | this.resolved = false;
|
596 | this.callbacks = [];
|
597 | }
|
598 | static all(valuesOrPromises) {
|
599 | const aggrPromise = new SyncPromise();
|
600 | let resolvedCount = 0;
|
601 | const results = [];
|
602 | const resolve = (idx, value) => {
|
603 | results[idx] = value;
|
604 | if (++resolvedCount === valuesOrPromises.length)
|
605 | aggrPromise.resolve(results);
|
606 | };
|
607 | valuesOrPromises.forEach((p, idx) => {
|
608 | if (isThenable(p)) {
|
609 | p.then(v => resolve(idx, v));
|
610 | }
|
611 | else {
|
612 | resolve(idx, p);
|
613 | }
|
614 | });
|
615 | return aggrPromise;
|
616 | }
|
617 | resolve(value) {
|
618 | // Do nothing, if already resolved.
|
619 | if (this.resolved)
|
620 | return;
|
621 | this.value = value;
|
622 | this.resolved = true;
|
623 | // Run the queued callbacks.
|
624 | this.callbacks.forEach(callback => callback(value));
|
625 | this.callbacks.length = 0;
|
626 | }
|
627 | then(callback) {
|
628 | if (this.resolved) {
|
629 | callback(this.value);
|
630 | }
|
631 | else {
|
632 | this.callbacks.push(callback);
|
633 | }
|
634 | }
|
635 | }
|
636 |
|
637 | /**
|
638 | * @license
|
639 | * Copyright Google LLC All Rights Reserved.
|
640 | *
|
641 | * Use of this source code is governed by an MIT-style license that can be
|
642 | * found in the LICENSE file at https://angular.io/license
|
643 | */
|
644 | /**
|
645 | * @description
|
646 | *
|
647 | * A helper function that allows an Angular component to be used from AngularJS.
|
648 | *
|
649 | * *Part of the [upgrade/static](api?query=upgrade%2Fstatic)
|
650 | * library for hybrid upgrade apps that support AOT compilation*
|
651 | *
|
652 | * This helper function returns a factory function to be used for registering
|
653 | * an AngularJS wrapper directive for "downgrading" an Angular component.
|
654 | *
|
655 | * @usageNotes
|
656 | * ### Examples
|
657 | *
|
658 | * Let's assume that you have an Angular component called `ng2Heroes` that needs
|
659 | * to be made available in AngularJS templates.
|
660 | *
|
661 | * {@example upgrade/static/ts/full/module.ts region="ng2-heroes"}
|
662 | *
|
663 | * We must create an AngularJS [directive](https://docs.angularjs.org/guide/directive)
|
664 | * that will make this Angular component available inside AngularJS templates.
|
665 | * The `downgradeComponent()` function returns a factory function that we
|
666 | * can use to define the AngularJS directive that wraps the "downgraded" component.
|
667 | *
|
668 | * {@example upgrade/static/ts/full/module.ts region="ng2-heroes-wrapper"}
|
669 | *
|
670 | * For more details and examples on downgrading Angular components to AngularJS components please
|
671 | * visit the [Upgrade guide](guide/upgrade#using-angular-components-from-angularjs-code).
|
672 | *
|
673 | * @param info contains information about the Component that is being downgraded:
|
674 | *
|
675 | * - `component: Type<any>`: The type of the Component that will be downgraded
|
676 | * - `downgradedModule?: string`: The name of the downgraded module (if any) that the component
|
677 | * "belongs to", as returned by a call to `downgradeModule()`. It is the module, whose
|
678 | * corresponding Angular module will be bootstrapped, when the component needs to be instantiated.
|
679 | * <br />
|
680 | * (This option is only necessary when using `downgradeModule()` to downgrade more than one
|
681 | * Angular module.)
|
682 | * - `propagateDigest?: boolean`: Whether to perform {@link ChangeDetectorRef#detectChanges
|
683 | * change detection} on the component on every
|
684 | * [$digest](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest). If set to `false`,
|
685 | * change detection will still be performed when any of the component's inputs changes.
|
686 | * (Default: true)
|
687 | *
|
688 | * @returns a factory function that can be used to register the component in an
|
689 | * AngularJS module.
|
690 | *
|
691 | * @publicApi
|
692 | */
|
693 | function downgradeComponent(info) {
|
694 | const directiveFactory = function ($compile, $injector, $parse) {
|
695 | // When using `downgradeModule()`, we need to handle certain things specially. For example:
|
696 | // - We always need to attach the component view to the `ApplicationRef` for it to be
|
697 | // dirty-checked.
|
698 | // - We need to ensure callbacks to Angular APIs (e.g. change detection) are run inside the
|
699 | // Angular zone.
|
700 | // NOTE: This is not needed, when using `UpgradeModule`, because `$digest()` will be run
|
701 | // inside the Angular zone (except if explicitly escaped, in which case we shouldn't
|
702 | // force it back in).
|
703 | const isNgUpgradeLite = getUpgradeAppType($injector) === 3 /* Lite */;
|
704 | const wrapCallback = !isNgUpgradeLite ? cb => cb : cb => () => NgZone.isInAngularZone() ? cb() : ngZone.run(cb);
|
705 | let ngZone;
|
706 | // When downgrading multiple modules, special handling is needed wrt injectors.
|
707 | const hasMultipleDowngradedModules = isNgUpgradeLite && (getDowngradedModuleCount($injector) > 1);
|
708 | return {
|
709 | restrict: 'E',
|
710 | terminal: true,
|
711 | require: [REQUIRE_INJECTOR, REQUIRE_NG_MODEL],
|
712 | link: (scope, element, attrs, required) => {
|
713 | // We might have to compile the contents asynchronously, because this might have been
|
714 | // triggered by `UpgradeNg1ComponentAdapterBuilder`, before the Angular templates have
|
715 | // been compiled.
|
716 | const ngModel = required[1];
|
717 | const parentInjector = required[0];
|
718 | let moduleInjector = undefined;
|
719 | let ranAsync = false;
|
720 | if (!parentInjector || hasMultipleDowngradedModules) {
|
721 | const downgradedModule = info.downgradedModule || '';
|
722 | const lazyModuleRefKey = `${LAZY_MODULE_REF}${downgradedModule}`;
|
723 | const attemptedAction = `instantiating component '${getTypeName(info.component)}'`;
|
724 | validateInjectionKey($injector, downgradedModule, lazyModuleRefKey, attemptedAction);
|
725 | const lazyModuleRef = $injector.get(lazyModuleRefKey);
|
726 | moduleInjector = lazyModuleRef.injector || lazyModuleRef.promise;
|
727 | }
|
728 | // Notes:
|
729 | //
|
730 | // There are two injectors: `finalModuleInjector` and `finalParentInjector` (they might be
|
731 | // the same instance, but that is irrelevant):
|
732 | // - `finalModuleInjector` is used to retrieve `ComponentFactoryResolver`, thus it must be
|
733 | // on the same tree as the `NgModule` that declares this downgraded component.
|
734 | // - `finalParentInjector` is used for all other injection purposes.
|
735 | // (Note that Angular knows to only traverse the component-tree part of that injector,
|
736 | // when looking for an injectable and then switch to the module injector.)
|
737 | //
|
738 | // There are basically three cases:
|
739 | // - If there is no parent component (thus no `parentInjector`), we bootstrap the downgraded
|
740 | // `NgModule` and use its injector as both `finalModuleInjector` and
|
741 | // `finalParentInjector`.
|
742 | // - If there is a parent component (and thus a `parentInjector`) and we are sure that it
|
743 | // belongs to the same `NgModule` as this downgraded component (e.g. because there is only
|
744 | // one downgraded module, we use that `parentInjector` as both `finalModuleInjector` and
|
745 | // `finalParentInjector`.
|
746 | // - If there is a parent component, but it may belong to a different `NgModule`, then we
|
747 | // use the `parentInjector` as `finalParentInjector` and this downgraded component's
|
748 | // declaring `NgModule`'s injector as `finalModuleInjector`.
|
749 | // Note 1: If the `NgModule` is already bootstrapped, we just get its injector (we don't
|
750 | // bootstrap again).
|
751 | // Note 2: It is possible that (while there are multiple downgraded modules) this
|
752 | // downgraded component and its parent component both belong to the same NgModule.
|
753 | // In that case, we could have used the `parentInjector` as both
|
754 | // `finalModuleInjector` and `finalParentInjector`, but (for simplicity) we are
|
755 | // treating this case as if they belong to different `NgModule`s. That doesn't
|
756 | // really affect anything, since `parentInjector` has `moduleInjector` as ancestor
|
757 | // and trying to resolve `ComponentFactoryResolver` from either one will return
|
758 | // the same instance.
|
759 | // If there is a parent component, use its injector as parent injector.
|
760 | // If this is a "top-level" Angular component, use the module injector.
|
761 | const finalParentInjector = parentInjector || moduleInjector;
|
762 | // If this is a "top-level" Angular component or the parent component may belong to a
|
763 | // different `NgModule`, use the module injector for module-specific dependencies.
|
764 | // If there is a parent component that belongs to the same `NgModule`, use its injector.
|
765 | const finalModuleInjector = moduleInjector || parentInjector;
|
766 | const doDowngrade = (injector, moduleInjector) => {
|
767 | // Retrieve `ComponentFactoryResolver` from the injector tied to the `NgModule` this
|
768 | // component belongs to.
|
769 | const componentFactoryResolver = moduleInjector.get(ComponentFactoryResolver);
|
770 | const componentFactory = componentFactoryResolver.resolveComponentFactory(info.component);
|
771 | if (!componentFactory) {
|
772 | throw new Error(`Expecting ComponentFactory for: ${getTypeName(info.component)}`);
|
773 | }
|
774 | const injectorPromise = new ParentInjectorPromise$1(element);
|
775 | const facade = new DowngradeComponentAdapter(element, attrs, scope, ngModel, injector, $compile, $parse, componentFactory, wrapCallback);
|
776 | const projectableNodes = facade.compileContents();
|
777 | facade.createComponent(projectableNodes);
|
778 | facade.setupInputs(isNgUpgradeLite, info.propagateDigest);
|
779 | facade.setupOutputs();
|
780 | facade.registerCleanup();
|
781 | injectorPromise.resolve(facade.getInjector());
|
782 | if (ranAsync) {
|
783 | // If this is run async, it is possible that it is not run inside a
|
784 | // digest and initial input values will not be detected.
|
785 | scope.$evalAsync(() => { });
|
786 | }
|
787 | };
|
788 | const downgradeFn = !isNgUpgradeLite ? doDowngrade : (pInjector, mInjector) => {
|
789 | if (!ngZone) {
|
790 | ngZone = pInjector.get(NgZone);
|
791 | }
|
792 | wrapCallback(() => doDowngrade(pInjector, mInjector))();
|
793 | };
|
794 | // NOTE:
|
795 | // Not using `ParentInjectorPromise.all()` (which is inherited from `SyncPromise`), because
|
796 | // Closure Compiler (or some related tool) complains:
|
797 | // `TypeError: ...$src$downgrade_component_ParentInjectorPromise.all is not a function`
|
798 | SyncPromise.all([finalParentInjector, finalModuleInjector])
|
799 | .then(([pInjector, mInjector]) => downgradeFn(pInjector, mInjector));
|
800 | ranAsync = true;
|
801 | }
|
802 | };
|
803 | };
|
804 | // bracket-notation because of closure - see #14441
|
805 | directiveFactory['$inject'] = [$COMPILE, $INJECTOR, $PARSE];
|
806 | return directiveFactory;
|
807 | }
|
808 | /**
|
809 | * Synchronous promise-like object to wrap parent injectors,
|
810 | * to preserve the synchronous nature of AngularJS's `$compile`.
|
811 | */
|
812 | class ParentInjectorPromise$1 extends SyncPromise {
|
813 | constructor(element) {
|
814 | super();
|
815 | this.element = element;
|
816 | this.injectorKey = controllerKey(INJECTOR_KEY);
|
817 | // Store the promise on the element.
|
818 | element.data(this.injectorKey, this);
|
819 | }
|
820 | resolve(injector) {
|
821 | // Store the real injector on the element.
|
822 | this.element.data(this.injectorKey, injector);
|
823 | // Release the element to prevent memory leaks.
|
824 | this.element = null;
|
825 | // Resolve the promise.
|
826 | super.resolve(injector);
|
827 | }
|
828 | }
|
829 |
|
830 | /**
|
831 | * @license
|
832 | * Copyright Google LLC All Rights Reserved.
|
833 | *
|
834 | * Use of this source code is governed by an MIT-style license that can be
|
835 | * found in the LICENSE file at https://angular.io/license
|
836 | */
|
837 | /**
|
838 | * @description
|
839 | *
|
840 | * A helper function to allow an Angular service to be accessible from AngularJS.
|
841 | *
|
842 | * *Part of the [upgrade/static](api?query=upgrade%2Fstatic)
|
843 | * library for hybrid upgrade apps that support AOT compilation*
|
844 | *
|
845 | * This helper function returns a factory function that provides access to the Angular
|
846 | * service identified by the `token` parameter.
|
847 | *
|
848 | * @usageNotes
|
849 | * ### Examples
|
850 | *
|
851 | * First ensure that the service to be downgraded is provided in an `NgModule`
|
852 | * that will be part of the upgrade application. For example, let's assume we have
|
853 | * defined `HeroesService`
|
854 | *
|
855 | * {@example upgrade/static/ts/full/module.ts region="ng2-heroes-service"}
|
856 | *
|
857 | * and that we have included this in our upgrade app `NgModule`
|
858 | *
|
859 | * {@example upgrade/static/ts/full/module.ts region="ng2-module"}
|
860 | *
|
861 | * Now we can register the `downgradeInjectable` factory function for the service
|
862 | * on an AngularJS module.
|
863 | *
|
864 | * {@example upgrade/static/ts/full/module.ts region="downgrade-ng2-heroes-service"}
|
865 | *
|
866 | * Inside an AngularJS component's controller we can get hold of the
|
867 | * downgraded service via the name we gave when downgrading.
|
868 | *
|
869 | * {@example upgrade/static/ts/full/module.ts region="example-app"}
|
870 | *
|
871 | * <div class="alert is-important">
|
872 | *
|
873 | * When using `downgradeModule()`, downgraded injectables will not be available until the Angular
|
874 | * module that provides them is instantiated. In order to be safe, you need to ensure that the
|
875 | * downgraded injectables are not used anywhere _outside_ the part of the app where it is
|
876 | * guaranteed that their module has been instantiated.
|
877 | *
|
878 | * For example, it is _OK_ to use a downgraded service in an upgraded component that is only used
|
879 | * from a downgraded Angular component provided by the same Angular module as the injectable, but
|
880 | * it is _not OK_ to use it in an AngularJS component that may be used independently of Angular or
|
881 | * use it in a downgraded Angular component from a different module.
|
882 | *
|
883 | * </div>
|
884 | *
|
885 | * @param token an `InjectionToken` that identifies a service provided from Angular.
|
886 | * @param downgradedModule the name of the downgraded module (if any) that the injectable
|
887 | * "belongs to", as returned by a call to `downgradeModule()`. It is the module, whose injector will
|
888 | * be used for instantiating the injectable.<br />
|
889 | * (This option is only necessary when using `downgradeModule()` to downgrade more than one Angular
|
890 | * module.)
|
891 | *
|
892 | * @returns a [factory function](https://docs.angularjs.org/guide/di) that can be
|
893 | * used to register the service on an AngularJS module.
|
894 | *
|
895 | * @publicApi
|
896 | */
|
897 | function downgradeInjectable(token, downgradedModule = '') {
|
898 | const factory = function ($injector) {
|
899 | const injectorKey = `${INJECTOR_KEY}${downgradedModule}`;
|
900 | const injectableName = isFunction(token) ? getTypeName(token) : String(token);
|
901 | const attemptedAction = `instantiating injectable '${injectableName}'`;
|
902 | validateInjectionKey($injector, downgradedModule, injectorKey, attemptedAction);
|
903 | try {
|
904 | const injector = $injector.get(injectorKey);
|
905 | return injector.get(token);
|
906 | }
|
907 | catch (err) {
|
908 | throw new Error(`Error while ${attemptedAction}: ${err.message || err}`);
|
909 | }
|
910 | };
|
911 | factory['$inject'] = [$INJECTOR];
|
912 | return factory;
|
913 | }
|
914 |
|
915 | /**
|
916 | * @license
|
917 | * Copyright Google LLC All Rights Reserved.
|
918 | *
|
919 | * Use of this source code is governed by an MIT-style license that can be
|
920 | * found in the LICENSE file at https://angular.io/license
|
921 | */
|
922 | // Constants
|
923 | const REQUIRE_PREFIX_RE = /^(\^\^?)?(\?)?(\^\^?)?/;
|
924 | // Classes
|
925 | class UpgradeHelper {
|
926 | constructor(injector, name, elementRef, directive) {
|
927 | this.name = name;
|
928 | this.$injector = injector.get($INJECTOR);
|
929 | this.$compile = this.$injector.get($COMPILE);
|
930 | this.$controller = this.$injector.get($CONTROLLER);
|
931 | this.element = elementRef.nativeElement;
|
932 | this.$element = element(this.element);
|
933 | this.directive = directive || UpgradeHelper.getDirective(this.$injector, name);
|
934 | }
|
935 | static getDirective($injector, name) {
|
936 | const directives = $injector.get(name + 'Directive');
|
937 | if (directives.length > 1) {
|
938 | throw new Error(`Only support single directive definition for: ${name}`);
|
939 | }
|
940 | const directive = directives[0];
|
941 | // AngularJS will transform `link: xyz` to `compile: () => xyz`. So we can only tell there was a
|
942 | // user-defined `compile` if there is no `link`. In other cases, we will just ignore `compile`.
|
943 | if (directive.compile && !directive.link)
|
944 | notSupported(name, 'compile');
|
945 | if (directive.replace)
|
946 | notSupported(name, 'replace');
|
947 | if (directive.terminal)
|
948 | notSupported(name, 'terminal');
|
949 | return directive;
|
950 | }
|
951 | static getTemplate($injector, directive, fetchRemoteTemplate = false, $element) {
|
952 | if (directive.template !== undefined) {
|
953 | return getOrCall(directive.template, $element);
|
954 | }
|
955 | else if (directive.templateUrl) {
|
956 | const $templateCache = $injector.get($TEMPLATE_CACHE);
|
957 | const url = getOrCall(directive.templateUrl, $element);
|
958 | const template = $templateCache.get(url);
|
959 | if (template !== undefined) {
|
960 | return template;
|
961 | }
|
962 | else if (!fetchRemoteTemplate) {
|
963 | throw new Error('loading directive templates asynchronously is not supported');
|
964 | }
|
965 | return new Promise((resolve, reject) => {
|
966 | const $httpBackend = $injector.get($HTTP_BACKEND);
|
967 | $httpBackend('GET', url, null, (status, response) => {
|
968 | if (status === 200) {
|
969 | resolve($templateCache.put(url, response));
|
970 | }
|
971 | else {
|
972 | reject(`GET component template from '${url}' returned '${status}: ${response}'`);
|
973 | }
|
974 | });
|
975 | });
|
976 | }
|
977 | else {
|
978 | throw new Error(`Directive '${directive.name}' is not a component, it is missing template.`);
|
979 | }
|
980 | }
|
981 | buildController(controllerType, $scope) {
|
982 | // TODO: Document that we do not pre-assign bindings on the controller instance.
|
983 | // Quoted properties below so that this code can be optimized with Closure Compiler.
|
984 | const locals = { '$scope': $scope, '$element': this.$element };
|
985 | const controller = this.$controller(controllerType, locals, null, this.directive.controllerAs);
|
986 | this.$element.data(controllerKey(this.directive.name), controller);
|
987 | return controller;
|
988 | }
|
989 | compileTemplate(template) {
|
990 | if (template === undefined) {
|
991 | template =
|
992 | UpgradeHelper.getTemplate(this.$injector, this.directive, false, this.$element);
|
993 | }
|
994 | return this.compileHtml(template);
|
995 | }
|
996 | onDestroy($scope, controllerInstance) {
|
997 | if (controllerInstance && isFunction(controllerInstance.$onDestroy)) {
|
998 | controllerInstance.$onDestroy();
|
999 | }
|
1000 | $scope.$destroy();
|
1001 | cleanData(this.element);
|
1002 | }
|
1003 | prepareTransclusion() {
|
1004 | const transclude = this.directive.transclude;
|
1005 | const contentChildNodes = this.extractChildNodes();
|
1006 | const attachChildrenFn = (scope, cloneAttachFn) => {
|
1007 | // Since AngularJS v1.5.8, `cloneAttachFn` will try to destroy the transclusion scope if
|
1008 | // `$template` is empty. Since the transcluded content comes from Angular, not AngularJS,
|
1009 | // there will be no transclusion scope here.
|
1010 | // Provide a dummy `scope.$destroy()` method to prevent `cloneAttachFn` from throwing.
|
1011 | scope = scope || { $destroy: () => undefined };
|
1012 | return cloneAttachFn($template, scope);
|
1013 | };
|
1014 | let $template = contentChildNodes;
|
1015 | if (transclude) {
|
1016 | const slots = Object.create(null);
|
1017 | if (typeof transclude === 'object') {
|
1018 | $template = [];
|
1019 | const slotMap = Object.create(null);
|
1020 | const filledSlots = Object.create(null);
|
1021 | // Parse the element selectors.
|
1022 | Object.keys(transclude).forEach(slotName => {
|
1023 | let selector = transclude[slotName];
|
1024 | const optional = selector.charAt(0) === '?';
|
1025 | selector = optional ? selector.substring(1) : selector;
|
1026 | slotMap[selector] = slotName;
|
1027 | slots[slotName] = null; // `null`: Defined but not yet filled.
|
1028 | filledSlots[slotName] = optional; // Consider optional slots as filled.
|
1029 | });
|
1030 | // Add the matching elements into their slot.
|
1031 | contentChildNodes.forEach(node => {
|
1032 | const slotName = slotMap[directiveNormalize(node.nodeName.toLowerCase())];
|
1033 | if (slotName) {
|
1034 | filledSlots[slotName] = true;
|
1035 | slots[slotName] = slots[slotName] || [];
|
1036 | slots[slotName].push(node);
|
1037 | }
|
1038 | else {
|
1039 | $template.push(node);
|
1040 | }
|
1041 | });
|
1042 | // Check for required slots that were not filled.
|
1043 | Object.keys(filledSlots).forEach(slotName => {
|
1044 | if (!filledSlots[slotName]) {
|
1045 | throw new Error(`Required transclusion slot '${slotName}' on directive: ${this.name}`);
|
1046 | }
|
1047 | });
|
1048 | Object.keys(slots).filter(slotName => slots[slotName]).forEach(slotName => {
|
1049 | const nodes = slots[slotName];
|
1050 | slots[slotName] = (scope, cloneAttach) => {
|
1051 | return cloneAttach(nodes, scope);
|
1052 | };
|
1053 | });
|
1054 | }
|
1055 | // Attach `$$slots` to default slot transclude fn.
|
1056 | attachChildrenFn.$$slots = slots;
|
1057 | // AngularJS v1.6+ ignores empty or whitespace-only transcluded text nodes. But Angular
|
1058 | // removes all text content after the first interpolation and updates it later, after
|
1059 | // evaluating the expressions. This would result in AngularJS failing to recognize text
|
1060 | // nodes that start with an interpolation as transcluded content and use the fallback
|
1061 | // content instead.
|
1062 | // To avoid this issue, we add a
|
1063 | // [zero-width non-joiner character](https://en.wikipedia.org/wiki/Zero-width_non-joiner)
|
1064 | // to empty text nodes (which can only be a result of Angular removing their initial content).
|
1065 | // NOTE: Transcluded text content that starts with whitespace followed by an interpolation
|
1066 | // will still fail to be detected by AngularJS v1.6+
|
1067 | $template.forEach(node => {
|
1068 | if (node.nodeType === Node.TEXT_NODE && !node.nodeValue) {
|
1069 | node.nodeValue = '\u200C';
|
1070 | }
|
1071 | });
|
1072 | }
|
1073 | return attachChildrenFn;
|
1074 | }
|
1075 | resolveAndBindRequiredControllers(controllerInstance) {
|
1076 | const directiveRequire = this.getDirectiveRequire();
|
1077 | const requiredControllers = this.resolveRequire(directiveRequire);
|
1078 | if (controllerInstance && this.directive.bindToController && isMap(directiveRequire)) {
|
1079 | const requiredControllersMap = requiredControllers;
|
1080 | Object.keys(requiredControllersMap).forEach(key => {
|
1081 | controllerInstance[key] = requiredControllersMap[key];
|
1082 | });
|
1083 | }
|
1084 | return requiredControllers;
|
1085 | }
|
1086 | compileHtml(html) {
|
1087 | this.element.innerHTML = html;
|
1088 | return this.$compile(this.element.childNodes);
|
1089 | }
|
1090 | extractChildNodes() {
|
1091 | const childNodes = [];
|
1092 | let childNode;
|
1093 | while (childNode = this.element.firstChild) {
|
1094 | this.element.removeChild(childNode);
|
1095 | childNodes.push(childNode);
|
1096 | }
|
1097 | return childNodes;
|
1098 | }
|
1099 | getDirectiveRequire() {
|
1100 | const require = this.directive.require || (this.directive.controller && this.directive.name);
|
1101 | if (isMap(require)) {
|
1102 | Object.keys(require).forEach(key => {
|
1103 | const value = require[key];
|
1104 | const match = value.match(REQUIRE_PREFIX_RE);
|
1105 | const name = value.substring(match[0].length);
|
1106 | if (!name) {
|
1107 | require[key] = match[0] + key;
|
1108 | }
|
1109 | });
|
1110 | }
|
1111 | return require;
|
1112 | }
|
1113 | resolveRequire(require, controllerInstance) {
|
1114 | if (!require) {
|
1115 | return null;
|
1116 | }
|
1117 | else if (Array.isArray(require)) {
|
1118 | return require.map(req => this.resolveRequire(req));
|
1119 | }
|
1120 | else if (typeof require === 'object') {
|
1121 | const value = {};
|
1122 | Object.keys(require).forEach(key => value[key] = this.resolveRequire(require[key]));
|
1123 | return value;
|
1124 | }
|
1125 | else if (typeof require === 'string') {
|
1126 | const match = require.match(REQUIRE_PREFIX_RE);
|
1127 | const inheritType = match[1] || match[3];
|
1128 | const name = require.substring(match[0].length);
|
1129 | const isOptional = !!match[2];
|
1130 | const searchParents = !!inheritType;
|
1131 | const startOnParent = inheritType === '^^';
|
1132 | const ctrlKey = controllerKey(name);
|
1133 | const elem = startOnParent ? this.$element.parent() : this.$element;
|
1134 | const value = searchParents ? elem.inheritedData(ctrlKey) : elem.data(ctrlKey);
|
1135 | if (!value && !isOptional) {
|
1136 | throw new Error(`Unable to find required '${require}' in upgraded directive '${this.name}'.`);
|
1137 | }
|
1138 | return value;
|
1139 | }
|
1140 | else {
|
1141 | throw new Error(`Unrecognized 'require' syntax on upgraded directive '${this.name}': ${require}`);
|
1142 | }
|
1143 | }
|
1144 | }
|
1145 | function getOrCall(property, ...args) {
|
1146 | return isFunction(property) ? property(...args) : property;
|
1147 | }
|
1148 | // NOTE: Only works for `typeof T !== 'object'`.
|
1149 | function isMap(value) {
|
1150 | return value && !Array.isArray(value) && typeof value === 'object';
|
1151 | }
|
1152 | function notSupported(name, feature) {
|
1153 | throw new Error(`Upgraded directive '${name}' contains unsupported feature: '${feature}'.`);
|
1154 | }
|
1155 |
|
1156 | const CAMEL_CASE = /([A-Z])/g;
|
1157 | const INITIAL_VALUE = {
|
1158 | __UNINITIALIZED__: true
|
1159 | };
|
1160 | const NOT_SUPPORTED = 'NOT_SUPPORTED';
|
1161 | class UpgradeNg1ComponentAdapterBuilder {
|
1162 | constructor(name) {
|
1163 | this.name = name;
|
1164 | this.inputs = [];
|
1165 | this.inputsRename = [];
|
1166 | this.outputs = [];
|
1167 | this.outputsRename = [];
|
1168 | this.propertyOutputs = [];
|
1169 | this.checkProperties = [];
|
1170 | this.propertyMap = {};
|
1171 | this.directive = null;
|
1172 | const selector = name.replace(CAMEL_CASE, (all, next) => '-' + next.toLowerCase());
|
1173 | const self = this;
|
1174 | // Note: There is a bug in TS 2.4 that prevents us from
|
1175 | // inlining this into @Directive
|
1176 | // TODO(tbosch): find or file a bug against TypeScript for this.
|
1177 | const directive = { selector: selector, inputs: this.inputsRename, outputs: this.outputsRename };
|
1178 | let MyClass = class MyClass extends UpgradeNg1ComponentAdapter {
|
1179 | constructor(scope, injector, elementRef) {
|
1180 | super(new UpgradeHelper(injector, name, elementRef, self.directive || undefined), scope, self.template, self.inputs, self.outputs, self.propertyOutputs, self.checkProperties, self.propertyMap);
|
1181 | }
|
1182 | };
|
1183 | MyClass = __decorate([
|
1184 | Directive(Object.assign({ jit: true }, directive)),
|
1185 | __param(0, Inject($SCOPE)),
|
1186 | __metadata("design:paramtypes", [Object, Injector, ElementRef])
|
1187 | ], MyClass);
|
1188 | this.type = MyClass;
|
1189 | }
|
1190 | extractBindings() {
|
1191 | const btcIsObject = typeof this.directive.bindToController === 'object';
|
1192 | if (btcIsObject && Object.keys(this.directive.scope).length) {
|
1193 | throw new Error(`Binding definitions on scope and controller at the same time are not supported.`);
|
1194 | }
|
1195 | const context = (btcIsObject) ? this.directive.bindToController : this.directive.scope;
|
1196 | if (typeof context == 'object') {
|
1197 | Object.keys(context).forEach(propName => {
|
1198 | const definition = context[propName];
|
1199 | const bindingType = definition.charAt(0);
|
1200 | const bindingOptions = definition.charAt(1);
|
1201 | const attrName = definition.substring(bindingOptions === '?' ? 2 : 1) || propName;
|
1202 | // QUESTION: What about `=*`? Ignore? Throw? Support?
|
1203 | const inputName = `input_${attrName}`;
|
1204 | const inputNameRename = `${inputName}: ${attrName}`;
|
1205 | const outputName = `output_${attrName}`;
|
1206 | const outputNameRename = `${outputName}: ${attrName}`;
|
1207 | const outputNameRenameChange = `${outputNameRename}Change`;
|
1208 | switch (bindingType) {
|
1209 | case '@':
|
1210 | case '<':
|
1211 | this.inputs.push(inputName);
|
1212 | this.inputsRename.push(inputNameRename);
|
1213 | this.propertyMap[inputName] = propName;
|
1214 | break;
|
1215 | case '=':
|
1216 | this.inputs.push(inputName);
|
1217 | this.inputsRename.push(inputNameRename);
|
1218 | this.propertyMap[inputName] = propName;
|
1219 | this.outputs.push(outputName);
|
1220 | this.outputsRename.push(outputNameRenameChange);
|
1221 | this.propertyMap[outputName] = propName;
|
1222 | this.checkProperties.push(propName);
|
1223 | this.propertyOutputs.push(outputName);
|
1224 | break;
|
1225 | case '&':
|
1226 | this.outputs.push(outputName);
|
1227 | this.outputsRename.push(outputNameRename);
|
1228 | this.propertyMap[outputName] = propName;
|
1229 | break;
|
1230 | default:
|
1231 | let json = JSON.stringify(context);
|
1232 | throw new Error(`Unexpected mapping '${bindingType}' in '${json}' in '${this.name}' directive.`);
|
1233 | }
|
1234 | });
|
1235 | }
|
1236 | }
|
1237 | /**
|
1238 | * Upgrade ng1 components into Angular.
|
1239 | */
|
1240 | static resolve(exportedComponents, $injector) {
|
1241 | const promises = Object.keys(exportedComponents).map(name => {
|
1242 | const exportedComponent = exportedComponents[name];
|
1243 | exportedComponent.directive = UpgradeHelper.getDirective($injector, name);
|
1244 | exportedComponent.extractBindings();
|
1245 | return Promise
|
1246 | .resolve(UpgradeHelper.getTemplate($injector, exportedComponent.directive, true))
|
1247 | .then(template => exportedComponent.template = template);
|
1248 | });
|
1249 | return Promise.all(promises);
|
1250 | }
|
1251 | }
|
1252 | class UpgradeNg1ComponentAdapter {
|
1253 | constructor(helper, scope, template, inputs, outputs, propOuts, checkProperties, propertyMap) {
|
1254 | this.helper = helper;
|
1255 | this.template = template;
|
1256 | this.inputs = inputs;
|
1257 | this.outputs = outputs;
|
1258 | this.propOuts = propOuts;
|
1259 | this.checkProperties = checkProperties;
|
1260 | this.propertyMap = propertyMap;
|
1261 | this.controllerInstance = null;
|
1262 | this.destinationObj = null;
|
1263 | this.checkLastValues = [];
|
1264 | this.$element = null;
|
1265 | this.directive = helper.directive;
|
1266 | this.element = helper.element;
|
1267 | this.$element = helper.$element;
|
1268 | this.componentScope = scope.$new(!!this.directive.scope);
|
1269 | const controllerType = this.directive.controller;
|
1270 | if (this.directive.bindToController && controllerType) {
|
1271 | this.controllerInstance = this.helper.buildController(controllerType, this.componentScope);
|
1272 | this.destinationObj = this.controllerInstance;
|
1273 | }
|
1274 | else {
|
1275 | this.destinationObj = this.componentScope;
|
1276 | }
|
1277 | for (let i = 0; i < inputs.length; i++) {
|
1278 | this[inputs[i]] = null;
|
1279 | }
|
1280 | for (let j = 0; j < outputs.length; j++) {
|
1281 | const emitter = this[outputs[j]] = new EventEmitter();
|
1282 | if (this.propOuts.indexOf(outputs[j]) === -1) {
|
1283 | this.setComponentProperty(outputs[j], (emitter => (value) => emitter.emit(value))(emitter));
|
1284 | }
|
1285 | }
|
1286 | for (let k = 0; k < propOuts.length; k++) {
|
1287 | this.checkLastValues.push(INITIAL_VALUE);
|
1288 | }
|
1289 | }
|
1290 | ngOnInit() {
|
1291 | // Collect contents, insert and compile template
|
1292 | const attachChildNodes = this.helper.prepareTransclusion();
|
1293 | const linkFn = this.helper.compileTemplate(this.template);
|
1294 | // Instantiate controller (if not already done so)
|
1295 | const controllerType = this.directive.controller;
|
1296 | const bindToController = this.directive.bindToController;
|
1297 | if (controllerType && !bindToController) {
|
1298 | this.controllerInstance = this.helper.buildController(controllerType, this.componentScope);
|
1299 | }
|
1300 | // Require other controllers
|
1301 | const requiredControllers = this.helper.resolveAndBindRequiredControllers(this.controllerInstance);
|
1302 | // Hook: $onInit
|
1303 | if (this.controllerInstance && isFunction(this.controllerInstance.$onInit)) {
|
1304 | this.controllerInstance.$onInit();
|
1305 | }
|
1306 | // Linking
|
1307 | const link = this.directive.link;
|
1308 | const preLink = typeof link == 'object' && link.pre;
|
1309 | const postLink = typeof link == 'object' ? link.post : link;
|
1310 | const attrs = NOT_SUPPORTED;
|
1311 | const transcludeFn = NOT_SUPPORTED;
|
1312 | if (preLink) {
|
1313 | preLink(this.componentScope, this.$element, attrs, requiredControllers, transcludeFn);
|
1314 | }
|
1315 | linkFn(this.componentScope, null, { parentBoundTranscludeFn: attachChildNodes });
|
1316 | if (postLink) {
|
1317 | postLink(this.componentScope, this.$element, attrs, requiredControllers, transcludeFn);
|
1318 | }
|
1319 | // Hook: $postLink
|
1320 | if (this.controllerInstance && isFunction(this.controllerInstance.$postLink)) {
|
1321 | this.controllerInstance.$postLink();
|
1322 | }
|
1323 | }
|
1324 | ngOnChanges(changes) {
|
1325 | const ng1Changes = {};
|
1326 | Object.keys(changes).forEach(name => {
|
1327 | const change = changes[name];
|
1328 | this.setComponentProperty(name, change.currentValue);
|
1329 | ng1Changes[this.propertyMap[name]] = change;
|
1330 | });
|
1331 | if (isFunction(this.destinationObj.$onChanges)) {
|
1332 | this.destinationObj.$onChanges(ng1Changes);
|
1333 | }
|
1334 | }
|
1335 | ngDoCheck() {
|
1336 | const destinationObj = this.destinationObj;
|
1337 | const lastValues = this.checkLastValues;
|
1338 | const checkProperties = this.checkProperties;
|
1339 | const propOuts = this.propOuts;
|
1340 | checkProperties.forEach((propName, i) => {
|
1341 | const value = destinationObj[propName];
|
1342 | const last = lastValues[i];
|
1343 | if (!strictEquals(last, value)) {
|
1344 | const eventEmitter = this[propOuts[i]];
|
1345 | eventEmitter.emit(lastValues[i] = value);
|
1346 | }
|
1347 | });
|
1348 | if (this.controllerInstance && isFunction(this.controllerInstance.$doCheck)) {
|
1349 | this.controllerInstance.$doCheck();
|
1350 | }
|
1351 | }
|
1352 | ngOnDestroy() {
|
1353 | this.helper.onDestroy(this.componentScope, this.controllerInstance);
|
1354 | }
|
1355 | setComponentProperty(name, value) {
|
1356 | this.destinationObj[this.propertyMap[name]] = value;
|
1357 | }
|
1358 | }
|
1359 | UpgradeNg1ComponentAdapter.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: UpgradeNg1ComponentAdapter, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive });
|
1360 | UpgradeNg1ComponentAdapter.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.0.2", type: UpgradeNg1ComponentAdapter, usesOnChanges: true, ngImport: i0 });
|
1361 | i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: UpgradeNg1ComponentAdapter, decorators: [{
|
1362 | type: Directive
|
1363 | }], ctorParameters: function () { return [{ type: UpgradeHelper }, { type: undefined }, { type: undefined }, { type: undefined }, { type: undefined }, { type: undefined }, { type: undefined }, { type: undefined }]; } });
|
1364 |
|
1365 | /**
|
1366 | * @license
|
1367 | * Copyright Google LLC All Rights Reserved.
|
1368 | *
|
1369 | * Use of this source code is governed by an MIT-style license that can be
|
1370 | * found in the LICENSE file at https://angular.io/license
|
1371 | */
|
1372 | let upgradeCount = 0;
|
1373 | /**
|
1374 | * Use `UpgradeAdapter` to allow AngularJS and Angular to coexist in a single application.
|
1375 | *
|
1376 | * The `UpgradeAdapter` allows:
|
1377 | * 1. creation of Angular component from AngularJS component directive
|
1378 | * (See [UpgradeAdapter#upgradeNg1Component()])
|
1379 | * 2. creation of AngularJS directive from Angular component.
|
1380 | * (See [UpgradeAdapter#downgradeNg2Component()])
|
1381 | * 3. Bootstrapping of a hybrid Angular application which contains both of the frameworks
|
1382 | * coexisting in a single application.
|
1383 | *
|
1384 | * @usageNotes
|
1385 | * ### Mental Model
|
1386 | *
|
1387 | * When reasoning about how a hybrid application works it is useful to have a mental model which
|
1388 | * describes what is happening and explains what is happening at the lowest level.
|
1389 | *
|
1390 | * 1. There are two independent frameworks running in a single application, each framework treats
|
1391 | * the other as a black box.
|
1392 | * 2. Each DOM element on the page is owned exactly by one framework. Whichever framework
|
1393 | * instantiated the element is the owner. Each framework only updates/interacts with its own
|
1394 | * DOM elements and ignores others.
|
1395 | * 3. AngularJS directives always execute inside AngularJS framework codebase regardless of
|
1396 | * where they are instantiated.
|
1397 | * 4. Angular components always execute inside Angular framework codebase regardless of
|
1398 | * where they are instantiated.
|
1399 | * 5. An AngularJS component can be upgraded to an Angular component. This creates an
|
1400 | * Angular directive, which bootstraps the AngularJS component directive in that location.
|
1401 | * 6. An Angular component can be downgraded to an AngularJS component directive. This creates
|
1402 | * an AngularJS directive, which bootstraps the Angular component in that location.
|
1403 | * 7. Whenever an adapter component is instantiated the host element is owned by the framework
|
1404 | * doing the instantiation. The other framework then instantiates and owns the view for that
|
1405 | * component. This implies that component bindings will always follow the semantics of the
|
1406 | * instantiation framework. The syntax is always that of Angular syntax.
|
1407 | * 8. AngularJS is always bootstrapped first and owns the bottom most view.
|
1408 | * 9. The new application is running in Angular zone, and therefore it no longer needs calls to
|
1409 | * `$apply()`.
|
1410 | *
|
1411 | * ### Example
|
1412 | *
|
1413 | * ```
|
1414 | * const adapter = new UpgradeAdapter(forwardRef(() => MyNg2Module), myCompilerOptions);
|
1415 | * const module = angular.module('myExample', []);
|
1416 | * module.directive('ng2Comp', adapter.downgradeNg2Component(Ng2Component));
|
1417 | *
|
1418 | * module.directive('ng1Hello', function() {
|
1419 | * return {
|
1420 | * scope: { title: '=' },
|
1421 | * template: 'ng1[Hello {{title}}!](<span ng-transclude></span>)'
|
1422 | * };
|
1423 | * });
|
1424 | *
|
1425 | *
|
1426 | * @Component({
|
1427 | * selector: 'ng2-comp',
|
1428 | * inputs: ['name'],
|
1429 | * template: 'ng2[<ng1-hello [title]="name">transclude</ng1-hello>](<ng-content></ng-content>)',
|
1430 | * directives:
|
1431 | * })
|
1432 | * class Ng2Component {
|
1433 | * }
|
1434 | *
|
1435 | * @NgModule({
|
1436 | * declarations: [Ng2Component, adapter.upgradeNg1Component('ng1Hello')],
|
1437 | * imports: [BrowserModule]
|
1438 | * })
|
1439 | * class MyNg2Module {}
|
1440 | *
|
1441 | *
|
1442 | * document.body.innerHTML = '<ng2-comp name="World">project</ng2-comp>';
|
1443 | *
|
1444 | * adapter.bootstrap(document.body, ['myExample']).ready(function() {
|
1445 | * expect(document.body.textContent).toEqual(
|
1446 | * "ng2[ng1[Hello World!](transclude)](project)");
|
1447 | * });
|
1448 | *
|
1449 | * ```
|
1450 | *
|
1451 | * @deprecated Deprecated since v5. Use `upgrade/static` instead, which also supports
|
1452 | * [Ahead-of-Time compilation](guide/aot-compiler).
|
1453 | * @publicApi
|
1454 | */
|
1455 | class UpgradeAdapter {
|
1456 | constructor(ng2AppModule, compilerOptions) {
|
1457 | this.ng2AppModule = ng2AppModule;
|
1458 | this.compilerOptions = compilerOptions;
|
1459 | this.idPrefix = `NG2_UPGRADE_${upgradeCount++}_`;
|
1460 | this.downgradedComponents = [];
|
1461 | /**
|
1462 | * An internal map of ng1 components which need to up upgraded to ng2.
|
1463 | *
|
1464 | * We can't upgrade until injector is instantiated and we can retrieve the component metadata.
|
1465 | * For this reason we keep a list of components to upgrade until ng1 injector is bootstrapped.
|
1466 | *
|
1467 | * @internal
|
1468 | */
|
1469 | this.ng1ComponentsToBeUpgraded = {};
|
1470 | this.upgradedProviders = [];
|
1471 | this.moduleRef = null;
|
1472 | if (!ng2AppModule) {
|
1473 | throw new Error('UpgradeAdapter cannot be instantiated without an NgModule of the Angular app.');
|
1474 | }
|
1475 | }
|
1476 | /**
|
1477 | * Allows Angular Component to be used from AngularJS.
|
1478 | *
|
1479 | * Use `downgradeNg2Component` to create an AngularJS Directive Definition Factory from
|
1480 | * Angular Component. The adapter will bootstrap Angular component from within the
|
1481 | * AngularJS template.
|
1482 | *
|
1483 | * @usageNotes
|
1484 | * ### Mental Model
|
1485 | *
|
1486 | * 1. The component is instantiated by being listed in AngularJS template. This means that the
|
1487 | * host element is controlled by AngularJS, but the component's view will be controlled by
|
1488 | * Angular.
|
1489 | * 2. Even thought the component is instantiated in AngularJS, it will be using Angular
|
1490 | * syntax. This has to be done, this way because we must follow Angular components do not
|
1491 | * declare how the attributes should be interpreted.
|
1492 | * 3. `ng-model` is controlled by AngularJS and communicates with the downgraded Angular component
|
1493 | * by way of the `ControlValueAccessor` interface from @angular/forms. Only components that
|
1494 | * implement this interface are eligible.
|
1495 | *
|
1496 | * ### Supported Features
|
1497 | *
|
1498 | * - Bindings:
|
1499 | * - Attribute: `<comp name="World">`
|
1500 | * - Interpolation: `<comp greeting="Hello {{name}}!">`
|
1501 | * - Expression: `<comp [name]="username">`
|
1502 | * - Event: `<comp (close)="doSomething()">`
|
1503 | * - ng-model: `<comp ng-model="name">`
|
1504 | * - Content projection: yes
|
1505 | *
|
1506 | * ### Example
|
1507 | *
|
1508 | * ```
|
1509 | * const adapter = new UpgradeAdapter(forwardRef(() => MyNg2Module));
|
1510 | * const module = angular.module('myExample', []);
|
1511 | * module.directive('greet', adapter.downgradeNg2Component(Greeter));
|
1512 | *
|
1513 | * @Component({
|
1514 | * selector: 'greet',
|
1515 | * template: '{{salutation}} {{name}}! - <ng-content></ng-content>'
|
1516 | * })
|
1517 | * class Greeter {
|
1518 | * @Input() salutation: string;
|
1519 | * @Input() name: string;
|
1520 | * }
|
1521 | *
|
1522 | * @NgModule({
|
1523 | * declarations: [Greeter],
|
1524 | * imports: [BrowserModule]
|
1525 | * })
|
1526 | * class MyNg2Module {}
|
1527 | *
|
1528 | * document.body.innerHTML =
|
1529 | * 'ng1 template: <greet salutation="Hello" [name]="world">text</greet>';
|
1530 | *
|
1531 | * adapter.bootstrap(document.body, ['myExample']).ready(function() {
|
1532 | * expect(document.body.textContent).toEqual("ng1 template: Hello world! - text");
|
1533 | * });
|
1534 | * ```
|
1535 | */
|
1536 | downgradeNg2Component(component) {
|
1537 | this.downgradedComponents.push(component);
|
1538 | return downgradeComponent({ component });
|
1539 | }
|
1540 | /**
|
1541 | * Allows AngularJS Component to be used from Angular.
|
1542 | *
|
1543 | * Use `upgradeNg1Component` to create an Angular component from AngularJS Component
|
1544 | * directive. The adapter will bootstrap AngularJS component from within the Angular
|
1545 | * template.
|
1546 | *
|
1547 | * @usageNotes
|
1548 | * ### Mental Model
|
1549 | *
|
1550 | * 1. The component is instantiated by being listed in Angular template. This means that the
|
1551 | * host element is controlled by Angular, but the component's view will be controlled by
|
1552 | * AngularJS.
|
1553 | *
|
1554 | * ### Supported Features
|
1555 | *
|
1556 | * - Bindings:
|
1557 | * - Attribute: `<comp name="World">`
|
1558 | * - Interpolation: `<comp greeting="Hello {{name}}!">`
|
1559 | * - Expression: `<comp [name]="username">`
|
1560 | * - Event: `<comp (close)="doSomething()">`
|
1561 | * - Transclusion: yes
|
1562 | * - Only some of the features of
|
1563 | * [Directive Definition Object](https://docs.angularjs.org/api/ng/service/$compile) are
|
1564 | * supported:
|
1565 | * - `compile`: not supported because the host element is owned by Angular, which does
|
1566 | * not allow modifying DOM structure during compilation.
|
1567 | * - `controller`: supported. (NOTE: injection of `$attrs` and `$transclude` is not supported.)
|
1568 | * - `controllerAs`: supported.
|
1569 | * - `bindToController`: supported.
|
1570 | * - `link`: supported. (NOTE: only pre-link function is supported.)
|
1571 | * - `name`: supported.
|
1572 | * - `priority`: ignored.
|
1573 | * - `replace`: not supported.
|
1574 | * - `require`: supported.
|
1575 | * - `restrict`: must be set to 'E'.
|
1576 | * - `scope`: supported.
|
1577 | * - `template`: supported.
|
1578 | * - `templateUrl`: supported.
|
1579 | * - `terminal`: ignored.
|
1580 | * - `transclude`: supported.
|
1581 | *
|
1582 | *
|
1583 | * ### Example
|
1584 | *
|
1585 | * ```
|
1586 | * const adapter = new UpgradeAdapter(forwardRef(() => MyNg2Module));
|
1587 | * const module = angular.module('myExample', []);
|
1588 | *
|
1589 | * module.directive('greet', function() {
|
1590 | * return {
|
1591 | * scope: {salutation: '=', name: '=' },
|
1592 | * template: '{{salutation}} {{name}}! - <span ng-transclude></span>'
|
1593 | * };
|
1594 | * });
|
1595 | *
|
1596 | * module.directive('ng2', adapter.downgradeNg2Component(Ng2Component));
|
1597 | *
|
1598 | * @Component({
|
1599 | * selector: 'ng2',
|
1600 | * template: 'ng2 template: <greet salutation="Hello" [name]="world">text</greet>'
|
1601 | * })
|
1602 | * class Ng2Component {
|
1603 | * }
|
1604 | *
|
1605 | * @NgModule({
|
1606 | * declarations: [Ng2Component, adapter.upgradeNg1Component('greet')],
|
1607 | * imports: [BrowserModule]
|
1608 | * })
|
1609 | * class MyNg2Module {}
|
1610 | *
|
1611 | * document.body.innerHTML = '<ng2></ng2>';
|
1612 | *
|
1613 | * adapter.bootstrap(document.body, ['myExample']).ready(function() {
|
1614 | * expect(document.body.textContent).toEqual("ng2 template: Hello world! - text");
|
1615 | * });
|
1616 | * ```
|
1617 | */
|
1618 | upgradeNg1Component(name) {
|
1619 | if (this.ng1ComponentsToBeUpgraded.hasOwnProperty(name)) {
|
1620 | return this.ng1ComponentsToBeUpgraded[name].type;
|
1621 | }
|
1622 | else {
|
1623 | return (this.ng1ComponentsToBeUpgraded[name] = new UpgradeNg1ComponentAdapterBuilder(name))
|
1624 | .type;
|
1625 | }
|
1626 | }
|
1627 | /**
|
1628 | * Registers the adapter's AngularJS upgrade module for unit testing in AngularJS.
|
1629 | * Use this instead of `angular.mock.module()` to load the upgrade module into
|
1630 | * the AngularJS testing injector.
|
1631 | *
|
1632 | * @usageNotes
|
1633 | * ### Example
|
1634 | *
|
1635 | * ```
|
1636 | * const upgradeAdapter = new UpgradeAdapter(MyNg2Module);
|
1637 | *
|
1638 | * // configure the adapter with upgrade/downgrade components and services
|
1639 | * upgradeAdapter.downgradeNg2Component(MyComponent);
|
1640 | *
|
1641 | * let upgradeAdapterRef: UpgradeAdapterRef;
|
1642 | * let $compile, $rootScope;
|
1643 | *
|
1644 | * // We must register the adapter before any calls to `inject()`
|
1645 | * beforeEach(() => {
|
1646 | * upgradeAdapterRef = upgradeAdapter.registerForNg1Tests(['heroApp']);
|
1647 | * });
|
1648 | *
|
1649 | * beforeEach(inject((_$compile_, _$rootScope_) => {
|
1650 | * $compile = _$compile_;
|
1651 | * $rootScope = _$rootScope_;
|
1652 | * }));
|
1653 | *
|
1654 | * it("says hello", (done) => {
|
1655 | * upgradeAdapterRef.ready(() => {
|
1656 | * const element = $compile("<my-component></my-component>")($rootScope);
|
1657 | * $rootScope.$apply();
|
1658 | * expect(element.html()).toContain("Hello World");
|
1659 | * done();
|
1660 | * })
|
1661 | * });
|
1662 | *
|
1663 | * ```
|
1664 | *
|
1665 | * @param modules any AngularJS modules that the upgrade module should depend upon
|
1666 | * @returns an `UpgradeAdapterRef`, which lets you register a `ready()` callback to
|
1667 | * run assertions once the Angular components are ready to test through AngularJS.
|
1668 | */
|
1669 | registerForNg1Tests(modules) {
|
1670 | const windowNgMock = window['angular'].mock;
|
1671 | if (!windowNgMock || !windowNgMock.module) {
|
1672 | throw new Error('Failed to find \'angular.mock.module\'.');
|
1673 | }
|
1674 | this.declareNg1Module(modules);
|
1675 | windowNgMock.module(this.ng1Module.name);
|
1676 | const upgrade = new UpgradeAdapterRef();
|
1677 | this.ng2BootstrapDeferred.promise.then((ng1Injector) => {
|
1678 | upgrade._bootstrapDone(this.moduleRef, ng1Injector);
|
1679 | }, onError);
|
1680 | return upgrade;
|
1681 | }
|
1682 | /**
|
1683 | * Bootstrap a hybrid AngularJS / Angular application.
|
1684 | *
|
1685 | * This `bootstrap` method is a direct replacement (takes same arguments) for AngularJS
|
1686 | * [`bootstrap`](https://docs.angularjs.org/api/ng/function/angular.bootstrap) method. Unlike
|
1687 | * AngularJS, this bootstrap is asynchronous.
|
1688 | *
|
1689 | * @usageNotes
|
1690 | * ### Example
|
1691 | *
|
1692 | * ```
|
1693 | * const adapter = new UpgradeAdapter(MyNg2Module);
|
1694 | * const module = angular.module('myExample', []);
|
1695 | * module.directive('ng2', adapter.downgradeNg2Component(Ng2));
|
1696 | *
|
1697 | * module.directive('ng1', function() {
|
1698 | * return {
|
1699 | * scope: { title: '=' },
|
1700 | * template: 'ng1[Hello {{title}}!](<span ng-transclude></span>)'
|
1701 | * };
|
1702 | * });
|
1703 | *
|
1704 | *
|
1705 | * @Component({
|
1706 | * selector: 'ng2',
|
1707 | * inputs: ['name'],
|
1708 | * template: 'ng2[<ng1 [title]="name">transclude</ng1>](<ng-content></ng-content>)'
|
1709 | * })
|
1710 | * class Ng2 {
|
1711 | * }
|
1712 | *
|
1713 | * @NgModule({
|
1714 | * declarations: [Ng2, adapter.upgradeNg1Component('ng1')],
|
1715 | * imports: [BrowserModule]
|
1716 | * })
|
1717 | * class MyNg2Module {}
|
1718 | *
|
1719 | * document.body.innerHTML = '<ng2 name="World">project</ng2>';
|
1720 | *
|
1721 | * adapter.bootstrap(document.body, ['myExample']).ready(function() {
|
1722 | * expect(document.body.textContent).toEqual(
|
1723 | * "ng2[ng1[Hello World!](transclude)](project)");
|
1724 | * });
|
1725 | * ```
|
1726 | */
|
1727 | bootstrap(element$1, modules, config) {
|
1728 | this.declareNg1Module(modules);
|
1729 | const upgrade = new UpgradeAdapterRef();
|
1730 | // Make sure resumeBootstrap() only exists if the current bootstrap is deferred
|
1731 | const windowAngular = window /** TODO #???? */['angular'];
|
1732 | windowAngular.resumeBootstrap = undefined;
|
1733 | this.ngZone.run(() => {
|
1734 | bootstrap(element$1, [this.ng1Module.name], config);
|
1735 | });
|
1736 | const ng1BootstrapPromise = new Promise((resolve) => {
|
1737 | if (windowAngular.resumeBootstrap) {
|
1738 | const originalResumeBootstrap = windowAngular.resumeBootstrap;
|
1739 | windowAngular.resumeBootstrap = function () {
|
1740 | windowAngular.resumeBootstrap = originalResumeBootstrap;
|
1741 | const r = windowAngular.resumeBootstrap.apply(this, arguments);
|
1742 | resolve();
|
1743 | return r;
|
1744 | };
|
1745 | }
|
1746 | else {
|
1747 | resolve();
|
1748 | }
|
1749 | });
|
1750 | Promise.all([this.ng2BootstrapDeferred.promise, ng1BootstrapPromise]).then(([ng1Injector]) => {
|
1751 | element(element$1).data(controllerKey(INJECTOR_KEY), this.moduleRef.injector);
|
1752 | this.moduleRef.injector.get(NgZone).run(() => {
|
1753 | upgrade._bootstrapDone(this.moduleRef, ng1Injector);
|
1754 | });
|
1755 | }, onError);
|
1756 | return upgrade;
|
1757 | }
|
1758 | /**
|
1759 | * Allows AngularJS service to be accessible from Angular.
|
1760 | *
|
1761 | * @usageNotes
|
1762 | * ### Example
|
1763 | *
|
1764 | * ```
|
1765 | * class Login { ... }
|
1766 | * class Server { ... }
|
1767 | *
|
1768 | * @Injectable()
|
1769 | * class Example {
|
1770 | * constructor(@Inject('server') server, login: Login) {
|
1771 | * ...
|
1772 | * }
|
1773 | * }
|
1774 | *
|
1775 | * const module = angular.module('myExample', []);
|
1776 | * module.service('server', Server);
|
1777 | * module.service('login', Login);
|
1778 | *
|
1779 | * const adapter = new UpgradeAdapter(MyNg2Module);
|
1780 | * adapter.upgradeNg1Provider('server');
|
1781 | * adapter.upgradeNg1Provider('login', {asToken: Login});
|
1782 | *
|
1783 | * adapter.bootstrap(document.body, ['myExample']).ready((ref) => {
|
1784 | * const example: Example = ref.ng2Injector.get(Example);
|
1785 | * });
|
1786 | *
|
1787 | * ```
|
1788 | */
|
1789 | upgradeNg1Provider(name, options) {
|
1790 | const token = options && options.asToken || name;
|
1791 | this.upgradedProviders.push({
|
1792 | provide: token,
|
1793 | useFactory: ($injector) => $injector.get(name),
|
1794 | deps: [$INJECTOR]
|
1795 | });
|
1796 | }
|
1797 | /**
|
1798 | * Allows Angular service to be accessible from AngularJS.
|
1799 | *
|
1800 | * @usageNotes
|
1801 | * ### Example
|
1802 | *
|
1803 | * ```
|
1804 | * class Example {
|
1805 | * }
|
1806 | *
|
1807 | * const adapter = new UpgradeAdapter(MyNg2Module);
|
1808 | *
|
1809 | * const module = angular.module('myExample', []);
|
1810 | * module.factory('example', adapter.downgradeNg2Provider(Example));
|
1811 | *
|
1812 | * adapter.bootstrap(document.body, ['myExample']).ready((ref) => {
|
1813 | * const example: Example = ref.ng1Injector.get('example');
|
1814 | * });
|
1815 | *
|
1816 | * ```
|
1817 | */
|
1818 | downgradeNg2Provider(token) {
|
1819 | return downgradeInjectable(token);
|
1820 | }
|
1821 | /**
|
1822 | * Declare the AngularJS upgrade module for this adapter without bootstrapping the whole
|
1823 | * hybrid application.
|
1824 | *
|
1825 | * This method is automatically called by `bootstrap()` and `registerForNg1Tests()`.
|
1826 | *
|
1827 | * @param modules The AngularJS modules that this upgrade module should depend upon.
|
1828 | * @returns The AngularJS upgrade module that is declared by this method
|
1829 | *
|
1830 | * @usageNotes
|
1831 | * ### Example
|
1832 | *
|
1833 | * ```
|
1834 | * const upgradeAdapter = new UpgradeAdapter(MyNg2Module);
|
1835 | * upgradeAdapter.declareNg1Module(['heroApp']);
|
1836 | * ```
|
1837 | */
|
1838 | declareNg1Module(modules = []) {
|
1839 | const delayApplyExps = [];
|
1840 | let original$applyFn;
|
1841 | let rootScopePrototype;
|
1842 | const upgradeAdapter = this;
|
1843 | const ng1Module = this.ng1Module = module_(this.idPrefix, modules);
|
1844 | const platformRef = platformBrowserDynamic();
|
1845 | this.ngZone = new NgZone({ enableLongStackTrace: Zone.hasOwnProperty('longStackTraceZoneSpec') });
|
1846 | this.ng2BootstrapDeferred = new Deferred();
|
1847 | ng1Module.constant(UPGRADE_APP_TYPE_KEY, 1 /* Dynamic */)
|
1848 | .factory(INJECTOR_KEY, () => this.moduleRef.injector.get(Injector))
|
1849 | .factory(LAZY_MODULE_REF, [INJECTOR_KEY, (injector) => ({ injector })])
|
1850 | .constant(NG_ZONE_KEY, this.ngZone)
|
1851 | .factory(COMPILER_KEY, () => this.moduleRef.injector.get(Compiler))
|
1852 | .config([
|
1853 | '$provide', '$injector',
|
1854 | (provide, ng1Injector) => {
|
1855 | provide.decorator($ROOT_SCOPE, [
|
1856 | '$delegate',
|
1857 | function (rootScopeDelegate) {
|
1858 | // Capture the root apply so that we can delay first call to $apply until we
|
1859 | // bootstrap Angular and then we replay and restore the $apply.
|
1860 | rootScopePrototype = rootScopeDelegate.constructor.prototype;
|
1861 | if (rootScopePrototype.hasOwnProperty('$apply')) {
|
1862 | original$applyFn = rootScopePrototype.$apply;
|
1863 | rootScopePrototype.$apply = (exp) => delayApplyExps.push(exp);
|
1864 | }
|
1865 | else {
|
1866 | throw new Error('Failed to find \'$apply\' on \'$rootScope\'!');
|
1867 | }
|
1868 | return rootScopeDelegate;
|
1869 | }
|
1870 | ]);
|
1871 | if (ng1Injector.has($$TESTABILITY)) {
|
1872 | provide.decorator($$TESTABILITY, [
|
1873 | '$delegate',
|
1874 | function (testabilityDelegate) {
|
1875 | const originalWhenStable = testabilityDelegate.whenStable;
|
1876 | // Cannot use arrow function below because we need the context
|
1877 | const newWhenStable = function (callback) {
|
1878 | originalWhenStable.call(this, function () {
|
1879 | const ng2Testability = upgradeAdapter.moduleRef.injector.get(Testability);
|
1880 | if (ng2Testability.isStable()) {
|
1881 | callback.apply(this, arguments);
|
1882 | }
|
1883 | else {
|
1884 | ng2Testability.whenStable(newWhenStable.bind(this, callback));
|
1885 | }
|
1886 | });
|
1887 | };
|
1888 | testabilityDelegate.whenStable = newWhenStable;
|
1889 | return testabilityDelegate;
|
1890 | }
|
1891 | ]);
|
1892 | }
|
1893 | }
|
1894 | ]);
|
1895 | ng1Module.run([
|
1896 | '$injector', '$rootScope',
|
1897 | (ng1Injector, rootScope) => {
|
1898 | UpgradeNg1ComponentAdapterBuilder.resolve(this.ng1ComponentsToBeUpgraded, ng1Injector)
|
1899 | .then(() => {
|
1900 | // Note: There is a bug in TS 2.4 that prevents us from
|
1901 | // inlining this into @NgModule
|
1902 | // TODO(tbosch): find or file a bug against TypeScript for this.
|
1903 | const ngModule = {
|
1904 | providers: [
|
1905 | { provide: $INJECTOR, useFactory: () => ng1Injector },
|
1906 | { provide: $COMPILE, useFactory: () => ng1Injector.get($COMPILE) },
|
1907 | this.upgradedProviders
|
1908 | ],
|
1909 | imports: [resolveForwardRef(this.ng2AppModule)],
|
1910 | entryComponents: this.downgradedComponents
|
1911 | };
|
1912 | // At this point we have ng1 injector and we have prepared
|
1913 | // ng1 components to be upgraded, we now can bootstrap ng2.
|
1914 | let DynamicNgUpgradeModule = class DynamicNgUpgradeModule {
|
1915 | constructor() { }
|
1916 | ngDoBootstrap() { }
|
1917 | };
|
1918 | DynamicNgUpgradeModule = __decorate([
|
1919 | NgModule(Object.assign({ jit: true }, ngModule)),
|
1920 | __metadata("design:paramtypes", [])
|
1921 | ], DynamicNgUpgradeModule);
|
1922 | platformRef
|
1923 | .bootstrapModule(DynamicNgUpgradeModule, [this.compilerOptions, { ngZone: this.ngZone }])
|
1924 | .then((ref) => {
|
1925 | this.moduleRef = ref;
|
1926 | this.ngZone.run(() => {
|
1927 | if (rootScopePrototype) {
|
1928 | rootScopePrototype.$apply = original$applyFn; // restore original $apply
|
1929 | while (delayApplyExps.length) {
|
1930 | rootScope.$apply(delayApplyExps.shift());
|
1931 | }
|
1932 | rootScopePrototype = null;
|
1933 | }
|
1934 | });
|
1935 | })
|
1936 | .then(() => this.ng2BootstrapDeferred.resolve(ng1Injector), onError)
|
1937 | .then(() => {
|
1938 | let subscription = this.ngZone.onMicrotaskEmpty.subscribe({
|
1939 | next: () => {
|
1940 | if (rootScope.$$phase) {
|
1941 | if (isDevMode()) {
|
1942 | console.warn('A digest was triggered while one was already in progress. This may mean that something is triggering digests outside the Angular zone.');
|
1943 | }
|
1944 | return rootScope.$evalAsync(() => { });
|
1945 | }
|
1946 | return rootScope.$digest();
|
1947 | }
|
1948 | });
|
1949 | rootScope.$on('$destroy', () => {
|
1950 | subscription.unsubscribe();
|
1951 | });
|
1952 | // Destroy the AngularJS app once the Angular `PlatformRef` is destroyed.
|
1953 | // This does not happen in a typical SPA scenario, but it might be useful for
|
1954 | // other use-cases where disposing of an Angular/AngularJS app is necessary
|
1955 | // (such as Hot Module Replacement (HMR)).
|
1956 | // See https://github.com/angular/angular/issues/39935.
|
1957 | platformRef.onDestroy(() => destroyApp(ng1Injector));
|
1958 | });
|
1959 | })
|
1960 | .catch((e) => this.ng2BootstrapDeferred.reject(e));
|
1961 | }
|
1962 | ]);
|
1963 | return ng1Module;
|
1964 | }
|
1965 | }
|
1966 | /**
|
1967 | * Synchronous promise-like object to wrap parent injectors,
|
1968 | * to preserve the synchronous nature of AngularJS's $compile.
|
1969 | */
|
1970 | class ParentInjectorPromise {
|
1971 | constructor(element) {
|
1972 | this.element = element;
|
1973 | this.callbacks = [];
|
1974 | // store the promise on the element
|
1975 | element.data(controllerKey(INJECTOR_KEY), this);
|
1976 | }
|
1977 | then(callback) {
|
1978 | if (this.injector) {
|
1979 | callback(this.injector);
|
1980 | }
|
1981 | else {
|
1982 | this.callbacks.push(callback);
|
1983 | }
|
1984 | }
|
1985 | resolve(injector) {
|
1986 | this.injector = injector;
|
1987 | // reset the element data to point to the real injector
|
1988 | this.element.data(controllerKey(INJECTOR_KEY), injector);
|
1989 | // clean out the element to prevent memory leaks
|
1990 | this.element = null;
|
1991 | // run all the queued callbacks
|
1992 | this.callbacks.forEach((callback) => callback(injector));
|
1993 | this.callbacks.length = 0;
|
1994 | }
|
1995 | }
|
1996 | /**
|
1997 | * Use `UpgradeAdapterRef` to control a hybrid AngularJS / Angular application.
|
1998 | *
|
1999 | * @deprecated Deprecated since v5. Use `upgrade/static` instead, which also supports
|
2000 | * [Ahead-of-Time compilation](guide/aot-compiler).
|
2001 | * @publicApi
|
2002 | */
|
2003 | class UpgradeAdapterRef {
|
2004 | constructor() {
|
2005 | /* @internal */
|
2006 | this._readyFn = null;
|
2007 | this.ng1RootScope = null;
|
2008 | this.ng1Injector = null;
|
2009 | this.ng2ModuleRef = null;
|
2010 | this.ng2Injector = null;
|
2011 | }
|
2012 | /* @internal */
|
2013 | _bootstrapDone(ngModuleRef, ng1Injector) {
|
2014 | this.ng2ModuleRef = ngModuleRef;
|
2015 | this.ng2Injector = ngModuleRef.injector;
|
2016 | this.ng1Injector = ng1Injector;
|
2017 | this.ng1RootScope = ng1Injector.get($ROOT_SCOPE);
|
2018 | this._readyFn && this._readyFn(this);
|
2019 | }
|
2020 | /**
|
2021 | * Register a callback function which is notified upon successful hybrid AngularJS / Angular
|
2022 | * application has been bootstrapped.
|
2023 | *
|
2024 | * The `ready` callback function is invoked inside the Angular zone, therefore it does not
|
2025 | * require a call to `$apply()`.
|
2026 | */
|
2027 | ready(fn) {
|
2028 | this._readyFn = fn;
|
2029 | }
|
2030 | /**
|
2031 | * Dispose of running hybrid AngularJS / Angular application.
|
2032 | */
|
2033 | dispose() {
|
2034 | this.ng1Injector.get($ROOT_SCOPE).$destroy();
|
2035 | this.ng2ModuleRef.destroy();
|
2036 | }
|
2037 | }
|
2038 |
|
2039 | /**
|
2040 | * @license
|
2041 | * Copyright Google LLC All Rights Reserved.
|
2042 | *
|
2043 | * Use of this source code is governed by an MIT-style license that can be
|
2044 | * found in the LICENSE file at https://angular.io/license
|
2045 | */
|
2046 | // This file only re-exports content of the `src` folder. Keep it that way.
|
2047 |
|
2048 | /**
|
2049 | * @license
|
2050 | * Copyright Google LLC All Rights Reserved.
|
2051 | *
|
2052 | * Use of this source code is governed by an MIT-style license that can be
|
2053 | * found in the LICENSE file at https://angular.io/license
|
2054 | */
|
2055 |
|
2056 | /**
|
2057 | * Generated bundle index. Do not edit.
|
2058 | */
|
2059 |
|
2060 | export { UpgradeAdapter, UpgradeAdapterRef, VERSION };
|
2061 | //# sourceMappingURL=upgrade.mjs.map
|