UNPKG

@angular/upgrade

Version:

Angular - the library for easing update from v1 to v2

794 lines (786 loc) 38.7 kB
/** * @license Angular v20.1.6 * (c) 2010-2025 Google LLC. https://angular.io/ * License: MIT */ import { module_, UPGRADE_APP_TYPE_KEY, INJECTOR_KEY, LAZY_MODULE_REF, $INJECTOR, $PROVIDE, DOWNGRADED_MODULE_COUNT_KEY, UPGRADE_MODULE_NAME, $SCOPE, $$TESTABILITY, $DELEGATE, $INTERVAL, element, bootstrap } from './constants.mjs'; export { getAngularJSGlobal, getAngularLib, setAngularJSGlobal, setAngularLib, angular1 as ɵangular1, constants as ɵconstants } from './constants.mjs'; import { destroyApp, getDowngradedModuleCount, isNgModuleType, isFunction, UpgradeHelper, controllerKey } from './upgrade_helper.mjs'; export { VERSION, downgradeComponent, downgradeInjectable, upgrade_helper as ɵupgradeHelper, util as ɵutil } from './upgrade_helper.mjs'; import * as i0 from '@angular/core'; import { ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as _NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, PlatformRef, EventEmitter, Directive, ApplicationRef, ɵNoopNgZone as _NoopNgZone, NgModule, Testability } from '@angular/core'; import { platformBrowser } from '@angular/platform-browser'; // We have to do a little dance to get the ng1 injector into the module injector. // We store the ng1 injector so that the provider in the module injector can access it // Then we "get" the ng1 injector from the module injector, which triggers the provider to read // the stored injector and release the reference to it. let tempInjectorRef = null; function setTempInjectorRef(injector) { tempInjectorRef = injector; } function injectorFactory() { if (!tempInjectorRef) { throw new Error('Trying to get the AngularJS injector before it being set.'); } const injector = tempInjectorRef; tempInjectorRef = null; // clear the value to prevent memory leaks return injector; } function rootScopeFactory(i) { return i.get('$rootScope'); } function compileFactory(i) { return i.get('$compile'); } function parseFactory(i) { return i.get('$parse'); } const angular1Providers = [ // We must use exported named functions for the ng2 factories to keep the compiler happy: // > Metadata collected contains an error that will be reported at runtime: // > Function calls are not supported. // > Consider replacing the function or lambda with a reference to an exported function { provide: '$injector', useFactory: injectorFactory, deps: [] }, { provide: '$rootScope', useFactory: rootScopeFactory, deps: ['$injector'] }, { provide: '$compile', useFactory: compileFactory, deps: ['$injector'] }, { provide: '$parse', useFactory: parseFactory, deps: ['$injector'] }, ]; class NgAdapterInjector { modInjector; constructor(modInjector) { this.modInjector = modInjector; } // When Angular locate a service in the component injector tree, the not found value is set to // `NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR`. In such a case we should not walk up to the module // injector. // AngularJS only supports a single tree and should always check the module injector. get(token, notFoundValue) { if (notFoundValue === _NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) { return notFoundValue; } return this.modInjector.get(token, notFoundValue); } } let moduleUid = 0; /** * @description * * A helper function for creating an AngularJS module that can bootstrap an Angular module * "on-demand" (possibly lazily) when a {@link downgradeComponent downgraded component} needs to be * instantiated. * * *Part of the [upgrade/static](api?query=upgrade/static) library for hybrid upgrade apps that * support AOT compilation.* * * It allows loading/bootstrapping the Angular part of a hybrid application lazily and not having to * pay the cost up-front. For example, you can have an AngularJS application that uses Angular for * specific routes and only instantiate the Angular modules if/when the user visits one of these * routes. * * The Angular module will be bootstrapped once (when requested for the first time) and the same * reference will be used from that point onwards. * * `downgradeModule()` requires either an `NgModuleFactory`, `NgModule` class or a function: * - `NgModuleFactory`: If you pass an `NgModuleFactory`, it will be used to instantiate a module * using `platformBrowser`'s {@link PlatformRef#bootstrapModuleFactory bootstrapModuleFactory()}. * NOTE: this type of the argument is deprecated. Please either provide an `NgModule` class or a * bootstrap function instead. * - `NgModule` class: If you pass an NgModule class, it will be used to instantiate a module * using `platformBrowser`'s {@link PlatformRef#bootstrapModule bootstrapModule()}. * - `Function`: If you pass a function, it is expected to return a promise resolving to an * `NgModuleRef`. The function is called with an array of extra {@link StaticProvider Providers} * that are expected to be available from the returned `NgModuleRef`'s `Injector`. * * `downgradeModule()` returns the name of the created AngularJS wrapper module. You can use it to * declare a dependency in your main AngularJS module. * * {@example upgrade/static/ts/lite/module.ts region="basic-how-to"} * * For more details on how to use `downgradeModule()` see * [Upgrading for Performance](https://angular.io/guide/upgrade). * * @usageNotes * * Apart from `UpgradeModule`, you can use the rest of the `upgrade/static` helpers as usual to * build a hybrid application. Note that the Angular pieces (e.g. downgraded services) will not be * available until the downgraded module has been bootstrapped, i.e. by instantiating a downgraded * component. * * <div class="docs-alert docs-alert-important"> * * You cannot use `downgradeModule()` and `UpgradeModule` in the same hybrid application.<br /> * Use one or the other. * * </div> * * ### Differences with `UpgradeModule` * * Besides their different API, there are two important internal differences between * `downgradeModule()` and `UpgradeModule` that affect the behavior of hybrid applications: * * 1. Unlike `UpgradeModule`, `downgradeModule()` does not bootstrap the main AngularJS module * inside the {@link NgZone Angular zone}. * 2. Unlike `UpgradeModule`, `downgradeModule()` does not automatically run a * [$digest()](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest) when changes are * detected in the Angular part of the application. * * What this means is that applications using `UpgradeModule` will run change detection more * frequently in order to ensure that both frameworks are properly notified about possible changes. * This will inevitably result in more change detection runs than necessary. * * `downgradeModule()`, on the other side, does not try to tie the two change detection systems as * tightly, restricting the explicit change detection runs only to cases where it knows it is * necessary (e.g. when the inputs of a downgraded component change). This improves performance, * especially in change-detection-heavy applications, but leaves it up to the developer to manually * notify each framework as needed. * * For a more detailed discussion of the differences and their implications, see * [Upgrading for Performance](https://angular.io/guide/upgrade). * * <div class="docs-alert docs-alert-helpful"> * * You can manually trigger a change detection run in AngularJS using * [scope.$apply(...)](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply) or * [$rootScope.$digest()](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest). * * You can manually trigger a change detection run in Angular using {@link NgZone#run * ngZone.run(...)}. * * </div> * * ### Downgrading multiple modules * * It is possible to downgrade multiple modules and include them in an AngularJS application. In * that case, each downgraded module will be bootstrapped when an associated downgraded component or * injectable needs to be instantiated. * * Things to keep in mind, when downgrading multiple modules: * * - Each downgraded component/injectable needs to be explicitly associated with a downgraded * module. See `downgradeComponent()` and `downgradeInjectable()` for more details. * * - If you want some injectables to be shared among all downgraded modules, you can provide them as * `StaticProvider`s, when creating the `PlatformRef` (e.g. via `platformBrowser` or * `platformBrowserDynamic`). * * - When using {@link PlatformRef#bootstrapmodule `bootstrapModule()`} or * {@link PlatformRef#bootstrapmodulefactory `bootstrapModuleFactory()`} to bootstrap the * downgraded modules, each one is considered a "root" module. As a consequence, a new instance * will be created for every injectable provided in `"root"` (via * {@link /api/core/Injectable#providedIn providedIn} * If this is not your intention, you can have a shared module (that will act as act as the "root" * module) and create all downgraded modules using that module's injector: * * {@example upgrade/static/ts/lite-multi-shared/module.ts region="shared-root-module"} * * @publicApi */ function downgradeModule(moduleOrBootstrapFn) { const lazyModuleName = `${UPGRADE_MODULE_NAME}.lazy${++moduleUid}`; const lazyModuleRefKey = `${LAZY_MODULE_REF}${lazyModuleName}`; const lazyInjectorKey = `${INJECTOR_KEY}${lazyModuleName}`; let bootstrapFn; if (isNgModuleType(moduleOrBootstrapFn)) { // NgModule class bootstrapFn = (extraProviders) => platformBrowser(extraProviders).bootstrapModule(moduleOrBootstrapFn); } else if (!isFunction(moduleOrBootstrapFn)) { // NgModule factory bootstrapFn = (extraProviders) => platformBrowser(extraProviders).bootstrapModuleFactory(moduleOrBootstrapFn); } else { // bootstrap function bootstrapFn = moduleOrBootstrapFn; } let injector; // Create an ng1 module to bootstrap. module_(lazyModuleName, []) .constant(UPGRADE_APP_TYPE_KEY, 3 /* ɵutil.UpgradeAppType.Lite */) .factory(INJECTOR_KEY, [lazyInjectorKey, identity]) .factory(lazyInjectorKey, () => { if (!injector) { throw new Error('Trying to get the Angular injector before bootstrapping the corresponding ' + 'Angular module.'); } return injector; }) .factory(LAZY_MODULE_REF, [lazyModuleRefKey, identity]) .factory(lazyModuleRefKey, [ $INJECTOR, ($injector) => { setTempInjectorRef($injector); const result = { promise: bootstrapFn(angular1Providers).then((ref) => { injector = result.injector = new NgAdapterInjector(ref.injector); injector.get($INJECTOR); // Destroy the AngularJS app once the Angular `PlatformRef` is destroyed. // This does not happen in a typical SPA scenario, but it might be useful for // other use-cases where disposing of an Angular/AngularJS app is necessary // (such as Hot Module Replacement (HMR)). // See https://github.com/angular/angular/issues/39935. injector.get(PlatformRef).onDestroy(() => destroyApp($injector)); return injector; }), }; return result; }, ]) .config([ $INJECTOR, $PROVIDE, ($injector, $provide) => { $provide.constant(DOWNGRADED_MODULE_COUNT_KEY, getDowngradedModuleCount($injector) + 1); }, ]); return lazyModuleName; } function identity(x) { return x; } const NOT_SUPPORTED = 'NOT_SUPPORTED'; const INITIAL_VALUE = { __UNINITIALIZED__: true, }; class Bindings { twoWayBoundProperties = []; twoWayBoundLastValues = []; expressionBoundProperties = []; propertyToOutputMap = {}; } /** * @description * * A helper class that allows an AngularJS component to be used from Angular. * * *Part of the [upgrade/static](api?query=upgrade%2Fstatic) * library for hybrid upgrade apps that support AOT compilation.* * * This helper class should be used as a base class for creating Angular directives * that wrap AngularJS components that need to be "upgraded". * * @usageNotes * ### Examples * * Let's assume that you have an AngularJS component called `ng1Hero` that needs * to be made available in Angular templates. * * {@example upgrade/static/ts/full/module.ts region="ng1-hero"} * * We must create a `Directive` that will make this AngularJS component * available inside Angular templates. * * {@example upgrade/static/ts/full/module.ts region="ng1-hero-wrapper"} * * In this example you can see that we must derive from the `UpgradeComponent` * base class but also provide an {@link Directive `@Directive`} decorator. This is * because the AOT compiler requires that this information is statically available at * compile time. * * Note that we must do the following: * * specify the directive's selector (`ng1-hero`) * * specify all inputs and outputs that the AngularJS component expects * * derive from `UpgradeComponent` * * call the base class from the constructor, passing * * the AngularJS name of the component (`ng1Hero`) * * the `ElementRef` and `Injector` for the component wrapper * * @publicApi * @extensible */ class UpgradeComponent { helper; $element; $componentScope; directive; bindings; controllerInstance; bindingDestination; // We will be instantiating the controller in the `ngOnInit` hook, when the // first `ngOnChanges` will have been already triggered. We store the // `SimpleChanges` and "play them back" later. pendingChanges = null; unregisterDoCheckWatcher; /** * Create a new `UpgradeComponent` instance. You should not normally need to do this. * Instead you should derive a new class from this one and call the super constructor * from the base class. * * {@example upgrade/static/ts/full/module.ts region="ng1-hero-wrapper" } * * * The `name` parameter should be the name of the AngularJS directive. * * The `elementRef` and `injector` parameters should be acquired from Angular by dependency * injection into the base class constructor. */ constructor(name, elementRef, injector) { this.helper = new UpgradeHelper(injector, name, elementRef); this.$element = this.helper.$element; this.directive = this.helper.directive; this.bindings = this.initializeBindings(this.directive, name); // We ask for the AngularJS scope from the Angular injector, since // we will put the new component scope onto the new injector for each component const $parentScope = injector.get($SCOPE); // QUESTION 1: Should we create an isolated scope if the scope is only true? // QUESTION 2: Should we make the scope accessible through `$element.scope()/isolateScope()`? this.$componentScope = $parentScope.$new(!!this.directive.scope); this.initializeOutputs(); } /** @docs-private */ ngOnInit() { // Collect contents, insert and compile template const attachChildNodes = this.helper.prepareTransclusion(); const linkFn = this.helper.compileTemplate(); // Instantiate controller const controllerType = this.directive.controller; const bindToController = this.directive.bindToController; let controllerInstance = controllerType ? this.helper.buildController(controllerType, this.$componentScope) : undefined; let bindingDestination; if (!bindToController) { bindingDestination = this.$componentScope; } else if (controllerType && controllerInstance) { bindingDestination = controllerInstance; } else { throw new Error(`Upgraded directive '${this.directive.name}' specifies 'bindToController' but no controller.`); } this.controllerInstance = controllerInstance; this.bindingDestination = bindingDestination; // Set up outputs this.bindOutputs(bindingDestination); // Require other controllers const requiredControllers = this.helper.resolveAndBindRequiredControllers(controllerInstance); // Hook: $onChanges if (this.pendingChanges) { this.forwardChanges(this.pendingChanges, bindingDestination); this.pendingChanges = null; } // Hook: $onInit if (this.controllerInstance && isFunction(this.controllerInstance.$onInit)) { this.controllerInstance.$onInit(); } // Hook: $doCheck if (controllerInstance && isFunction(controllerInstance.$doCheck)) { const callDoCheck = () => controllerInstance?.$doCheck?.(); this.unregisterDoCheckWatcher = this.$componentScope.$parent.$watch(callDoCheck); callDoCheck(); } // Linking const link = this.directive.link; const preLink = typeof link == 'object' && link.pre; const postLink = typeof link == 'object' ? link.post : link; const attrs = NOT_SUPPORTED; const transcludeFn = NOT_SUPPORTED; if (preLink) { preLink(this.$componentScope, this.$element, attrs, requiredControllers, transcludeFn); } linkFn(this.$componentScope, null, { parentBoundTranscludeFn: attachChildNodes }); if (postLink) { postLink(this.$componentScope, this.$element, attrs, requiredControllers, transcludeFn); } // Hook: $postLink if (this.controllerInstance && isFunction(this.controllerInstance.$postLink)) { this.controllerInstance.$postLink(); } } /** @docs-private */ ngOnChanges(changes) { if (!this.bindingDestination) { this.pendingChanges = changes; } else { this.forwardChanges(changes, this.bindingDestination); } } /** @docs-private */ ngDoCheck() { const twoWayBoundProperties = this.bindings.twoWayBoundProperties; const twoWayBoundLastValues = this.bindings.twoWayBoundLastValues; const propertyToOutputMap = this.bindings.propertyToOutputMap; twoWayBoundProperties.forEach((propName, idx) => { const newValue = this.bindingDestination?.[propName]; const oldValue = twoWayBoundLastValues[idx]; if (!Object.is(newValue, oldValue)) { const outputName = propertyToOutputMap[propName]; const eventEmitter = this[outputName]; eventEmitter.emit(newValue); twoWayBoundLastValues[idx] = newValue; } }); } /** @docs-private */ ngOnDestroy() { if (isFunction(this.unregisterDoCheckWatcher)) { this.unregisterDoCheckWatcher(); } this.helper.onDestroy(this.$componentScope, this.controllerInstance); } initializeBindings(directive, name) { const btcIsObject = typeof directive.bindToController === 'object'; if (btcIsObject && Object.keys(directive.scope).length) { throw new Error(`Binding definitions on scope and controller at the same time is not supported.`); } const context = btcIsObject ? directive.bindToController : directive.scope; const bindings = new Bindings(); if (typeof context == 'object') { Object.keys(context).forEach((propName) => { const definition = context[propName]; const bindingType = definition.charAt(0); // QUESTION: What about `=*`? Ignore? Throw? Support? switch (bindingType) { case '@': case '<': // We don't need to do anything special. They will be defined as inputs on the // upgraded component facade and the change propagation will be handled by // `ngOnChanges()`. break; case '=': bindings.twoWayBoundProperties.push(propName); bindings.twoWayBoundLastValues.push(INITIAL_VALUE); bindings.propertyToOutputMap[propName] = propName + 'Change'; break; case '&': bindings.expressionBoundProperties.push(propName); bindings.propertyToOutputMap[propName] = propName; break; default: let json = JSON.stringify(context); throw new Error(`Unexpected mapping '${bindingType}' in '${json}' in '${name}' directive.`); } }); } return bindings; } initializeOutputs() { // Initialize the outputs for `=` and `&` bindings this.bindings.twoWayBoundProperties .concat(this.bindings.expressionBoundProperties) .forEach((propName) => { const outputName = this.bindings.propertyToOutputMap[propName]; this[outputName] = new EventEmitter(); }); } bindOutputs(bindingDestination) { // Bind `&` bindings to the corresponding outputs this.bindings.expressionBoundProperties.forEach((propName) => { const outputName = this.bindings.propertyToOutputMap[propName]; const emitter = this[outputName]; bindingDestination[propName] = (value) => emitter.emit(value); }); } forwardChanges(changes, bindingDestination) { // Forward input changes to `bindingDestination` Object.keys(changes).forEach((propName) => (bindingDestination[propName] = changes[propName].currentValue)); if (isFunction(bindingDestination.$onChanges)) { bindingDestination.$onChanges(changes); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: UpgradeComponent, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.6", type: UpgradeComponent, isStandalone: true, usesOnChanges: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: UpgradeComponent, decorators: [{ type: Directive }], ctorParameters: () => [{ type: undefined }, { type: i0.ElementRef }, { type: i0.Injector }] }); /** * @description * * An `NgModule`, which you import to provide AngularJS core services, * and has an instance method used to bootstrap the hybrid upgrade application. * * *Part of the [upgrade/static](api?query=upgrade/static) * library for hybrid upgrade apps that support AOT compilation* * * The `upgrade/static` package contains helpers that allow AngularJS and Angular components * to be used together inside a hybrid upgrade application, which supports AOT compilation. * * Specifically, the classes and functions in the `upgrade/static` module allow the following: * * 1. Creation of an Angular directive that wraps and exposes an AngularJS component so * that it can be used in an Angular template. See `UpgradeComponent`. * 2. Creation of an AngularJS directive that wraps and exposes an Angular component so * that it can be used in an AngularJS template. See `downgradeComponent`. * 3. Creation of an Angular root injector provider that wraps and exposes an AngularJS * service so that it can be injected into an Angular context. See * {@link UpgradeModule#upgrading-an-angular-1-service Upgrading an AngularJS service} below. * 4. Creation of an AngularJS service that wraps and exposes an Angular injectable * so that it can be injected into an AngularJS context. See `downgradeInjectable`. * 5. Bootstrapping of a hybrid Angular application which contains both of the frameworks * coexisting in a single application. * * @usageNotes * * ```ts * import {UpgradeModule} from '@angular/upgrade/static'; * ``` * * See also the {@link UpgradeModule#examples examples} below. * * ### Mental Model * * When reasoning about how a hybrid application works it is useful to have a mental model which * describes what is happening and explains what is happening at the lowest level. * * 1. There are two independent frameworks running in a single application, each framework treats * the other as a black box. * 2. Each DOM element on the page is owned exactly by one framework. Whichever framework * instantiated the element is the owner. Each framework only updates/interacts with its own * DOM elements and ignores others. * 3. AngularJS directives always execute inside the AngularJS framework codebase regardless of * where they are instantiated. * 4. Angular components always execute inside the Angular framework codebase regardless of * where they are instantiated. * 5. An AngularJS component can be "upgraded"" to an Angular component. This is achieved by * defining an Angular directive, which bootstraps the AngularJS component at its location * in the DOM. See `UpgradeComponent`. * 6. An Angular component can be "downgraded" to an AngularJS component. This is achieved by * defining an AngularJS directive, which bootstraps the Angular component at its location * in the DOM. See `downgradeComponent`. * 7. Whenever an "upgraded"/"downgraded" component is instantiated the host element is owned by * the framework doing the instantiation. The other framework then instantiates and owns the * view for that component. * 1. This implies that the component bindings will always follow the semantics of the * instantiation framework. * 2. The DOM attributes are parsed by the framework that owns the current template. So * attributes in AngularJS templates must use kebab-case, while AngularJS templates must use * camelCase. * 3. However the template binding syntax will always use the Angular style, e.g. square * brackets (`[...]`) for property binding. * 8. Angular is bootstrapped first; AngularJS is bootstrapped second. AngularJS always owns the * root component of the application. * 9. The new application is running in an Angular zone, and therefore it no longer needs calls to * `$apply()`. * * ### The `UpgradeModule` class * * This class is an `NgModule`, which you import to provide AngularJS core services, * and has an instance method used to bootstrap the hybrid upgrade application. * * * Core AngularJS services<br /> * Importing this `NgModule` will add providers for the core * [AngularJS services](https://docs.angularjs.org/api/ng/service) to the root injector. * * * Bootstrap<br /> * The runtime instance of this class contains a {@link UpgradeModule#bootstrap `bootstrap()`} * method, which you use to bootstrap the top level AngularJS module onto an element in the * DOM for the hybrid upgrade app. * * It also contains properties to access the {@link UpgradeModule#injector root injector}, the * bootstrap `NgZone` and the * [AngularJS $injector](https://docs.angularjs.org/api/auto/service/$injector). * * ### Examples * * Import the `UpgradeModule` into your top level Angular {@link NgModule NgModule}. * * {@example upgrade/static/ts/full/module.ts region='ng2-module'} * * Then inject `UpgradeModule` into your Angular `NgModule` and use it to bootstrap the top level * [AngularJS module](https://docs.angularjs.org/api/ng/type/angular.Module) in the * `ngDoBootstrap()` method. * * {@example upgrade/static/ts/full/module.ts region='bootstrap-ng1'} * * Finally, kick off the whole process, by bootstrapping your top level Angular `NgModule`. * * {@example upgrade/static/ts/full/module.ts region='bootstrap-ng2'} * * ### Upgrading an AngularJS service * * There is no specific API for upgrading an AngularJS service. Instead you should just follow the * following recipe: * * Let's say you have an AngularJS service: * * {@example upgrade/static/ts/full/module.ts region="ng1-text-formatter-service"} * * Then you should define an Angular provider to be included in your `NgModule` `providers` * property. * * {@example upgrade/static/ts/full/module.ts region="upgrade-ng1-service"} * * Then you can use the "upgraded" AngularJS service by injecting it into an Angular component * or service. * * {@example upgrade/static/ts/full/module.ts region="use-ng1-upgraded-service"} * * @publicApi */ class UpgradeModule { ngZone; platformRef; /** * The AngularJS `$injector` for the upgrade application. */ $injector; /** The Angular Injector **/ injector; applicationRef; constructor( /** The root `Injector` for the upgrade application. */ injector, /** The bootstrap zone for the upgrade application */ ngZone, /** * The owning `NgModuleRef`s `PlatformRef` instance. * This is used to tie the lifecycle of the bootstrapped AngularJS apps to that of the Angular * `PlatformRef`. */ platformRef) { this.ngZone = ngZone; this.platformRef = platformRef; this.injector = new NgAdapterInjector(injector); this.applicationRef = this.injector.get(ApplicationRef); } /** * Bootstrap an AngularJS application from this NgModule * @param element the element on which to bootstrap the AngularJS application * @param [modules] the AngularJS modules to bootstrap for this application * @param [config] optional extra AngularJS bootstrap configuration * @return The value returned by * [angular.bootstrap()](https://docs.angularjs.org/api/ng/function/angular.bootstrap). */ bootstrap(element$1, modules = [], config /*angular.IAngularBootstrapConfig*/) { const INIT_MODULE_NAME = UPGRADE_MODULE_NAME + '.init'; // Create an ng1 module to bootstrap module_(INIT_MODULE_NAME, []) .constant(UPGRADE_APP_TYPE_KEY, 2 /* ɵutil.UpgradeAppType.Static */) .value(INJECTOR_KEY, this.injector) .factory(LAZY_MODULE_REF, [ INJECTOR_KEY, (injector) => ({ injector }), ]) .config([ $PROVIDE, $INJECTOR, ($provide, $injector) => { if ($injector.has($$TESTABILITY)) { $provide.decorator($$TESTABILITY, [ $DELEGATE, (testabilityDelegate) => { const originalWhenStable = testabilityDelegate.whenStable; const injector = this.injector; // Cannot use arrow function below because we need the context const newWhenStable = function (callback) { originalWhenStable.call(testabilityDelegate, function () { const ng2Testability = injector.get(Testability); if (ng2Testability.isStable()) { callback(); } else { ng2Testability.whenStable(newWhenStable.bind(testabilityDelegate, callback)); } }); }; testabilityDelegate.whenStable = newWhenStable; return testabilityDelegate; }, ]); } if ($injector.has($INTERVAL)) { $provide.decorator($INTERVAL, [ $DELEGATE, (intervalDelegate) => { // Wrap the $interval service so that setInterval is called outside NgZone, // but the callback is still invoked within it. This is so that $interval // won't block stability, which preserves the behavior from AngularJS. let wrappedInterval = (fn, delay, count, invokeApply, ...pass) => { return this.ngZone.runOutsideAngular(() => { return intervalDelegate((...args) => { // Run callback in the next VM turn - $interval calls // $rootScope.$apply, and running the callback in NgZone will // cause a '$digest already in progress' error if it's in the // same vm turn. setTimeout(() => { this.ngZone.run(() => fn(...args)); }); }, delay, count, invokeApply, ...pass); }); }; Object.keys(intervalDelegate).forEach((prop) => (wrappedInterval[prop] = intervalDelegate[prop])); // the `flush` method will be present when ngMocks is used if (intervalDelegate.hasOwnProperty('flush')) { wrappedInterval['flush'] = () => { intervalDelegate['flush'](); return wrappedInterval; }; } return wrappedInterval; }, ]); } }, ]) .run([ $INJECTOR, ($injector) => { this.$injector = $injector; const $rootScope = $injector.get('$rootScope'); // Initialize the ng1 $injector provider setTempInjectorRef($injector); this.injector.get($INJECTOR); // Put the injector on the DOM, so that it can be "required" element(element$1).data(controllerKey(INJECTOR_KEY), this.injector); // Destroy the AngularJS app once the Angular `PlatformRef` is destroyed. // This does not happen in a typical SPA scenario, but it might be useful for // other use-cases where disposing of an Angular/AngularJS app is necessary // (such as Hot Module Replacement (HMR)). // See https://github.com/angular/angular/issues/39935. this.platformRef.onDestroy(() => destroyApp($injector)); // Wire up the ng1 rootScope to run a digest cycle whenever the zone settles // We need to do this in the next tick so that we don't prevent the bootup stabilizing setTimeout(() => { const synchronize = () => { this.ngZone.run(() => { if ($rootScope.$$phase) { if (typeof ngDevMode === 'undefined' || ngDevMode) { console.warn('A digest was triggered while one was already in progress. This may mean that something is triggering digests outside the Angular zone.'); } $rootScope.$evalAsync(); } else { $rootScope.$digest(); } }); }; const subscription = // We _DO NOT_ usually want to have any code that does one thing for zoneless and another for ZoneJS. // This is only here because there is not enough coverage for hybrid apps anymore so we cannot // be confident that making UpgradeModule work with zoneless is a non-breaking change. this.ngZone instanceof _NoopNgZone ? this.applicationRef.afterTick.subscribe(() => synchronize()) : this.ngZone.onMicrotaskEmpty.subscribe(() => synchronize()); $rootScope.$on('$destroy', () => { subscription.unsubscribe(); }); }, 0); }, ]); const upgradeModule = module_(UPGRADE_MODULE_NAME, [INIT_MODULE_NAME].concat(modules)); // Make sure resumeBootstrap() only exists if the current bootstrap is deferred const windowAngular = window['angular']; windowAngular.resumeBootstrap = undefined; // Bootstrap the AngularJS application inside our zone const returnValue = this.ngZone.run(() => bootstrap(element$1, [upgradeModule.name], config)); // Patch resumeBootstrap() to run inside the ngZone if (windowAngular.resumeBootstrap) { const originalResumeBootstrap = windowAngular.resumeBootstrap; const ngZone = this.ngZone; windowAngular.resumeBootstrap = function () { let args = arguments; windowAngular.resumeBootstrap = originalResumeBootstrap; return ngZone.run(() => windowAngular.resumeBootstrap.apply(this, args)); }; } return returnValue; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: UpgradeModule, deps: [{ token: i0.Injector }, { token: i0.NgZone }, { token: i0.PlatformRef }], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.1.6", ngImport: i0, type: UpgradeModule }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: UpgradeModule, providers: [angular1Providers] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.6", ngImport: i0, type: UpgradeModule, decorators: [{ type: NgModule, args: [{ providers: [angular1Providers] }] }], ctorParameters: () => [{ type: i0.Injector }, { type: i0.NgZone }, { type: i0.PlatformRef }] }); export { UpgradeComponent, UpgradeModule, downgradeModule }; //# sourceMappingURL=static.mjs.map