refactor(upgrade): use Bazel packages to avoid symlinks in the source (#29466)

Previously we had to share code between upgrade/dynamic and upgrade/static
by symlinking the `src` folder, which allowed both packages to access
the upgrade/common files.

These symlinks are always problematic on Windows, where we had to run
a script to re-link them, and restore them.

This change uses Bazel packages to share the `upgrade/common` code,
which avoids the need for symlinking the `src` folder.

Also, the Windows specific scripts that fixup the symlinks have also
been removed as there is no more need for them.

PR Close #29466
This commit is contained in:
Pete Bacon Darwin
2019-03-22 09:42:52 +00:00
committed by Jason Aden
parent e5201f92fc
commit 9f54d76ef5
49 changed files with 219 additions and 162 deletions

View File

@ -0,0 +1,16 @@
load("//tools:defaults.bzl", "ts_library")
package(default_visibility = [
"//packages/upgrade:__subpackages__",
"//tools/public_api_guard:__subpackages__",
])
ts_library(
name = "common",
srcs = glob([
"src/**/*.ts",
]),
deps = [
"//packages/core",
],
)

View File

@ -0,0 +1,23 @@
load("//tools:defaults.bzl", "ts_library", "ts_web_test_suite")
ts_library(
name = "test_lib",
testonly = True,
srcs = glob(["**/*.ts"]),
deps = [
"//packages/core",
"//packages/core/testing",
"//packages/upgrade/src/common",
"//packages/upgrade/src/common/test/helpers",
],
)
ts_web_test_suite(
name = "test",
static_files = [
"//:angularjs_scripts",
],
deps = [
":test_lib",
],
)

View File

@ -0,0 +1,37 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {PropertyBinding} from '../src/component_info';
{
describe('PropertyBinding', () => {
it('should process a simple binding', () => {
const binding = new PropertyBinding('someBinding', 'someBinding');
expect(binding.prop).toEqual('someBinding');
expect(binding.attr).toEqual('someBinding');
expect(binding.bracketAttr).toEqual('[someBinding]');
expect(binding.bracketParenAttr).toEqual('[(someBinding)]');
expect(binding.parenAttr).toEqual('(someBinding)');
expect(binding.onAttr).toEqual('onSomeBinding');
expect(binding.bindAttr).toEqual('bindSomeBinding');
expect(binding.bindonAttr).toEqual('bindonSomeBinding');
});
it('should process a two-part binding', () => {
const binding = new PropertyBinding('someProp', 'someAttr');
expect(binding.prop).toEqual('someProp');
expect(binding.attr).toEqual('someAttr');
expect(binding.bracketAttr).toEqual('[someAttr]');
expect(binding.bracketParenAttr).toEqual('[(someAttr)]');
expect(binding.parenAttr).toEqual('(someAttr)');
expect(binding.onAttr).toEqual('onSomeAttr');
expect(binding.bindAttr).toEqual('bindSomeAttr');
expect(binding.bindonAttr).toEqual('bindonSomeAttr');
});
});
}

View File

@ -0,0 +1,203 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Compiler, Component, ComponentFactory, Injector, NgModule, TestabilityRegistry} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import * as angular from '../src/angular1';
import {DowngradeComponentAdapter, groupNodesBySelector} from '../src/downgrade_component_adapter';
import {nodes, withEachNg1Version} from './helpers/common_test_helpers';
withEachNg1Version(() => {
describe('DowngradeComponentAdapter', () => {
describe('groupNodesBySelector', () => {
it('should return an array of node collections for each selector', () => {
const contentNodes = nodes(
'<div class="x"><span>div-1 content</span></div>' +
'<input type="number" name="myNum">' +
'<input type="date" name="myDate">' +
'<span>span content</span>' +
'<div class="x"><span>div-2 content</span></div>');
const selectors = ['input[type=date]', 'span', '.x'];
const projectableNodes = groupNodesBySelector(selectors, contentNodes);
expect(projectableNodes[0]).toEqual(nodes('<input type="date" name="myDate">'));
expect(projectableNodes[1]).toEqual(nodes('<span>span content</span>'));
expect(projectableNodes[2])
.toEqual(nodes(
'<div class="x"><span>div-1 content</span></div>' +
'<div class="x"><span>div-2 content</span></div>'));
});
it('should collect up unmatched nodes for the wildcard selector', () => {
const contentNodes = nodes(
'<div class="x"><span>div-1 content</span></div>' +
'<input type="number" name="myNum">' +
'<input type="date" name="myDate">' +
'<span>span content</span>' +
'<div class="x"><span>div-2 content</span></div>');
const selectors = ['.x', '*', 'input[type=date]'];
const projectableNodes = groupNodesBySelector(selectors, contentNodes);
expect(projectableNodes[0])
.toEqual(nodes(
'<div class="x"><span>div-1 content</span></div>' +
'<div class="x"><span>div-2 content</span></div>'));
expect(projectableNodes[1])
.toEqual(nodes(
'<input type="number" name="myNum">' +
'<span>span content</span>'));
expect(projectableNodes[2]).toEqual(nodes('<input type="date" name="myDate">'));
});
it('should return an array of empty arrays if there are no nodes passed in', () => {
const selectors = ['.x', '*', 'input[type=date]'];
const projectableNodes = groupNodesBySelector(selectors, []);
expect(projectableNodes).toEqual([[], [], []]);
});
it('should return an empty array for each selector that does not match', () => {
const contentNodes = nodes(
'<div class="x"><span>div-1 content</span></div>' +
'<input type="number" name="myNum">' +
'<input type="date" name="myDate">' +
'<span>span content</span>' +
'<div class="x"><span>div-2 content</span></div>');
const projectableNodes = groupNodesBySelector([], contentNodes);
expect(projectableNodes).toEqual([]);
const noMatchSelectorNodes = groupNodesBySelector(['.not-there'], contentNodes);
expect(noMatchSelectorNodes).toEqual([[]]);
});
});
describe('testability', () => {
let adapter: DowngradeComponentAdapter;
let content: string;
let compiler: Compiler;
let registry: TestabilityRegistry;
let element: angular.IAugmentedJQuery;
class mockScope implements angular.IScope {
private destroyListeners: (() => void)[] = [];
$new() { return this; }
$watch(exp: angular.Ng1Expression, fn?: (a1?: any, a2?: any) => void) {
return () => {};
}
$on(event: string, fn?: (event?: any, ...args: any[]) => void) {
if (event === '$destroy' && fn) {
this.destroyListeners.push(fn);
}
return () => {};
}
$destroy() {
let listener: (() => void)|undefined;
while ((listener = this.destroyListeners.shift())) listener();
}
$apply(exp?: angular.Ng1Expression) {
return () => {};
}
$digest() {
return () => {};
}
$evalAsync(exp: angular.Ng1Expression, locals?: any) {
return () => {};
}
// TODO(issue/24571): remove '!'.
$$childTail !: angular.IScope;
// TODO(issue/24571): remove '!'.
$$childHead !: angular.IScope;
// TODO(issue/24571): remove '!'.
$$nextSibling !: angular.IScope;
[key: string]: any;
$id = 'mockScope';
// TODO(issue/24571): remove '!'.
$parent !: angular.IScope;
// TODO(issue/24571): remove '!'.
$root !: angular.IScope;
}
function getAdaptor(): DowngradeComponentAdapter {
let attrs = undefined as any;
let scope: angular.IScope; // mock
let ngModel = undefined as any;
let parentInjector: Injector; // testbed
let $injector = undefined as any;
let $compile = undefined as any;
let $parse = undefined as any;
let componentFactory: ComponentFactory<any>; // testbed
let wrapCallback = (cb: any) => cb;
content = `
<h1> new component </h1>
<div> a great component </div>
<comp></comp>
`;
element = angular.element(content);
scope = new mockScope();
@Component({
selector: 'comp',
template: '',
})
class NewComponent {
}
@NgModule({
providers: [{provide: 'hello', useValue: 'component'}],
declarations: [NewComponent],
entryComponents: [NewComponent],
})
class NewModule {
}
const modFactory = compiler.compileModuleSync(NewModule);
const module = modFactory.create(TestBed);
componentFactory = module.componentFactoryResolver.resolveComponentFactory(NewComponent) !;
parentInjector = TestBed;
return new DowngradeComponentAdapter(
element, attrs, scope, ngModel, parentInjector, $injector, $compile, $parse,
componentFactory, wrapCallback);
}
beforeEach(() => {
compiler = TestBed.get(Compiler);
registry = TestBed.get(TestabilityRegistry);
adapter = getAdaptor();
});
beforeEach(() => registry.unregisterAllApplications());
afterEach(() => registry.unregisterAllApplications());
it('should add testabilities hook when creating components', () => {
let registry = TestBed.get(TestabilityRegistry);
adapter.createComponent([]);
expect(registry.getAllTestabilities().length).toEqual(1);
adapter = getAdaptor(); // get a new adaptor to creat a new component
adapter.createComponent([]);
expect(registry.getAllTestabilities().length).toEqual(2);
});
it('should remove the testability hook when destroy a component', () => {
const registry = TestBed.get(TestabilityRegistry);
expect(registry.getAllTestabilities().length).toEqual(0);
adapter.createComponent([]);
expect(registry.getAllTestabilities().length).toEqual(1);
adapter.registerCleanup();
element.remove !();
expect(registry.getAllTestabilities().length).toEqual(0);
});
});
});
});

View File

@ -0,0 +1,51 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Injector} from '@angular/core';
import * as angular from '../src/angular1';
import {$INJECTOR, INJECTOR_KEY, UPGRADE_APP_TYPE_KEY} from '../src/constants';
import {downgradeInjectable} from '../src/downgrade_injectable';
import {UpgradeAppType} from '../src/util';
describe('downgradeInjectable', () => {
const setupMockInjectors = (downgradedModule = '') => {
const mockNg1Injector = jasmine.createSpyObj<angular.IInjectorService>(['get', 'has']);
mockNg1Injector.get.and.callFake((key: string) => mockDependencies[key]);
mockNg1Injector.has.and.callFake((key: string) => mockDependencies.hasOwnProperty(key));
const mockNg2Injector = jasmine.createSpyObj<Injector>(['get']);
mockNg2Injector.get.and.returnValue('service value');
const mockDependencies: {[key: string]: any} = {
[UPGRADE_APP_TYPE_KEY]: downgradedModule ? UpgradeAppType.Lite : UpgradeAppType.Static,
[`${INJECTOR_KEY}${downgradedModule}`]: mockNg2Injector,
};
return {mockNg1Injector, mockNg2Injector};
};
it('should return an AngularJS annotated factory for the token', () => {
const factory = downgradeInjectable('someToken');
expect(factory).toEqual(jasmine.any(Function));
expect((factory as any).$inject).toEqual([$INJECTOR]);
const {mockNg1Injector, mockNg2Injector} = setupMockInjectors();
expect(factory(mockNg1Injector)).toEqual('service value');
expect(mockNg2Injector.get).toHaveBeenCalledWith('someToken');
});
it('should inject the specified module\'s injector when specifying a module name', () => {
const factory = downgradeInjectable('someToken', 'someModule');
expect(factory).toEqual(jasmine.any(Function));
expect((factory as any).$inject).toEqual([$INJECTOR]);
const {mockNg1Injector, mockNg2Injector} = setupMockInjectors('someModule');
expect(factory(mockNg1Injector)).toEqual('service value');
expect(mockNg2Injector.get).toHaveBeenCalledWith('someToken');
});
});

View File

@ -0,0 +1,14 @@
load("//tools:defaults.bzl", "ts_library")
package(default_visibility = ["//packages/upgrade:__subpackages__"])
ts_library(
name = "helpers",
srcs = glob([
"*.ts",
]),
deps = [
"//packages/core/testing",
"//packages/upgrade/src/common",
],
)

View File

@ -0,0 +1,165 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {setAngularJSGlobal} from '../../src/angular1';
// Whether the upgrade tests should run against AngularJS minified or not. This can be
// temporarily switched to "false" in order to make it easy to debug AngularJS locally.
const TEST_MINIFIED = true;
const ANGULARJS_FILENAME = TEST_MINIFIED ? 'angular.min.js' : 'angular.js';
const ng1Versions = [
{
label: '1.5',
files: [`angular-1.5/${ANGULARJS_FILENAME}`, 'angular-mocks-1.5/angular-mocks.js'],
},
{
label: '1.6',
files: [`angular-1.6/${ANGULARJS_FILENAME}`, 'angular-mocks-1.6/angular-mocks.js'],
},
{
label: '1.7',
files: [`angular/${ANGULARJS_FILENAME}`, 'angular-mocks/angular-mocks.js'],
},
];
export function createWithEachNg1VersionFn(setNg1: typeof setAngularJSGlobal) {
return (specSuite: () => void) => ng1Versions.forEach(({label, files}) => {
describe(`[AngularJS v${label}]`, () => {
// Problem:
// As soon as `angular-mocks.js` is loaded, it runs `beforeEach` and `afterEach` to register
// setup/tear down callbacks. Jasmine 2.9+ does not allow `beforeEach`/`afterEach` to be
// nested inside a `beforeAll` call (only inside `describe`).
// Hacky work-around:
// Patch the affected jasmine methods while loading `angular-mocks.js` (inside `beforeAll`) to
// capture the registered callbacks. Also, inside the `describe` call register a callback with
// each affected method that runs all captured callbacks.
// (Note: Currently, async callbacks are not supported, but that should be OK, since
// `angular-mocks.js` does not use them.)
const methodsToPatch = ['beforeAll', 'beforeEach', 'afterEach', 'afterAll'];
const methodCallbacks = methodsToPatch.reduce<{[name: string]: any[]}>(
(aggr, method) => ({...aggr, [method]: []}), {});
const win = window as any;
function patchJasmineMethods(): () => void {
const originalMethods: {[name: string]: any} = {};
methodsToPatch.forEach(method => {
originalMethods[method] = win[method];
win[method] = (cb: any) => methodCallbacks[method].push(cb);
});
return () => methodsToPatch.forEach(method => win[method] = originalMethods[method]);
}
function loadScript(scriptUrl: string, retry = 0): Promise<void> {
return new Promise<void>((resolve, reject) => {
const script = document.createElement('script');
script.async = true;
script.onerror = (retry > 0) ? () => {
// Sometimes (especially on mobile browsers on SauceLabs) the script may fail to load
// due to a temporary issue with the internet connection. To avoid flakes on CI when
// this happens, we retry the download after some delay.
const delay = 5000;
win.console.warn(
`\n[${new Date().toISOString()}] Retrying to load "${scriptUrl}" in ${delay}ms...`);
document.body.removeChild(script);
setTimeout(() => loadScript(scriptUrl, --retry).then(resolve, reject), delay);
} : () => {
// Whenever the script failed loading, browsers will just pass an "ErrorEvent" which
// does not contain useful information on most browsers we run tests against. In order
// to avoid writing logic to convert the event into a readable error and since just
// passing the event might cause people to spend unnecessary time debugging the
// "ErrorEvent", we create a simple error that doesn't imply that there is a lot of
// information within the "ErrorEvent".
reject(`An error occurred while loading "${scriptUrl}".`);
};
script.onload = () => {
document.body.removeChild(script);
resolve();
};
script.src = `base/npm/node_modules/${scriptUrl}`;
document.body.appendChild(script);
});
}
beforeAll(done => {
const restoreJasmineMethods = patchJasmineMethods();
const onSuccess = () => {
restoreJasmineMethods();
done();
};
const onError = (err: any) => {
restoreJasmineMethods();
done.fail(err);
};
// Load AngularJS before running tests.
files.reduce((prev, file) => prev.then(() => loadScript(file, 1)), Promise.resolve())
.then(() => setNg1(win.angular))
.then(onSuccess, onError);
// When Saucelabs is flaky, some browsers (esp. mobile) take some time to load and execute
// the AngularJS scripts. Specifying a higher timeout here, reduces flaky-ness.
}, 60000);
afterAll(() => {
// `win.angular` will not be defined if loading the script in `berofeAll()` failed. In that
// case, avoid causing another error in `afterAll()`, because the reporter only shows the
// most recent error (thus hiding the original, possibly more informative, error message).
if (win.angular) {
// In these tests we are loading different versions of AngularJS on the same window.
// AngularJS leaves an "expandoId" property on `document`, which can trick subsequent
// `window.angular` instances into believing an app is already bootstrapped.
win.angular.element.cleanData([document]);
}
// Remove AngularJS to leave a clean state for subsequent tests.
setNg1(undefined);
delete win.angular;
});
methodsToPatch.forEach(method => win[method](function() {
// Run the captured callbacks. (Async callbacks not supported.)
methodCallbacks[method].forEach(cb => cb.call(this));
}));
specSuite();
});
});
}
export function html(html: string): Element {
// Don't return `body` itself, because using it as a `$rootElement` for ng1
// will attach `$injector` to it and that will affect subsequent tests.
const body = document.body;
body.innerHTML = `<div>${html.trim()}</div>`;
const div = document.body.firstChild as Element;
if (div.childNodes.length === 1 && div.firstChild instanceof HTMLElement) {
return div.firstChild;
}
return div;
}
export function multiTrim(text: string | null | undefined, allSpace = false): string {
if (typeof text == 'string') {
const repl = allSpace ? '' : ' ';
return text.replace(/\n/g, '').replace(/\s+/g, repl).trim();
}
throw new Error('Argument can not be undefined.');
}
export function nodes(html: string) {
const div = document.createElement('div');
div.innerHTML = html.trim();
return Array.prototype.slice.call(div.childNodes);
}
export const withEachNg1Version = createWithEachNg1VersionFn(setAngularJSGlobal);

View File

@ -9,11 +9,11 @@
import {Compiler, CompilerOptions, Injector, NgModule, NgModuleRef, NgZone, StaticProvider, Testability, Type, resolveForwardRef} from '@angular/core';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {IAngularBootstrapConfig, IAugmentedJQuery, IInjectorService, IModule, IProvideService, IRootScopeService, ITestabilityService, bootstrap, element as angularElement, module as angularModule} from '../common/angular1';
import {$$TESTABILITY, $COMPILE, $INJECTOR, $ROOT_SCOPE, COMPILER_KEY, INJECTOR_KEY, LAZY_MODULE_REF, NG_ZONE_KEY, UPGRADE_APP_TYPE_KEY} from '../common/constants';
import {downgradeComponent} from '../common/downgrade_component';
import {downgradeInjectable} from '../common/downgrade_injectable';
import {Deferred, LazyModuleRef, UpgradeAppType, controllerKey, onError} from '../common/util';
import {IAngularBootstrapConfig, IAugmentedJQuery, IInjectorService, IModule, IProvideService, IRootScopeService, ITestabilityService, bootstrap, element as angularElement, module as angularModule} from '../../common/src/angular1';
import {$$TESTABILITY, $COMPILE, $INJECTOR, $ROOT_SCOPE, COMPILER_KEY, INJECTOR_KEY, LAZY_MODULE_REF, NG_ZONE_KEY, UPGRADE_APP_TYPE_KEY} from '../../common/src/constants';
import {downgradeComponent} from '../../common/src/downgrade_component';
import {downgradeInjectable} from '../../common/src/downgrade_injectable';
import {Deferred, LazyModuleRef, UpgradeAppType, controllerKey, onError} from '../../common/src/util';
import {UpgradeNg1ComponentAdapterBuilder} from './upgrade_ng1_adapter';

View File

@ -8,10 +8,10 @@
import {Directive, DoCheck, ElementRef, EventEmitter, Inject, Injector, OnChanges, OnDestroy, OnInit, SimpleChange, SimpleChanges, Type} from '@angular/core';
import {IAttributes, IDirective, IDirectivePrePost, IInjectorService, ILinkFn, IScope, ITranscludeFunction} from '../common/angular1';
import {$SCOPE} from '../common/constants';
import {IBindingDestination, IControllerInstance, UpgradeHelper} from '../common/upgrade_helper';
import {isFunction, strictEquals} from '../common/util';
import {IAttributes, IDirective, IDirectivePrePost, IInjectorService, ILinkFn, IScope, ITranscludeFunction} from '../../common/src/angular1';
import {$SCOPE} from '../../common/src/constants';
import {IBindingDestination, IControllerInstance, UpgradeHelper} from '../../common/src/upgrade_helper';
import {isFunction, strictEquals} from '../../common/src/util';
const CAMEL_CASE = /([A-Z])/g;

View File

@ -0,0 +1,28 @@
load("//tools:defaults.bzl", "ts_library", "ts_web_test_suite")
ts_library(
name = "test_lib",
testonly = True,
srcs = glob([
"**/*.ts",
]),
deps = [
"//packages/core",
"//packages/core/testing",
"//packages/platform-browser",
"//packages/platform-browser-dynamic",
"//packages/upgrade",
"//packages/upgrade/src/common",
"//packages/upgrade/src/common/test/helpers",
],
)
ts_web_test_suite(
name = "test",
static_files = [
"//:angularjs_scripts",
],
deps = [
":test_lib",
],
)

File diff suppressed because it is too large Load Diff

View File

@ -1,51 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {IInjectorService} from '../common/angular1';
// 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: IInjectorService|null = null;
export function setTempInjectorRef(injector: IInjectorService) {
tempInjectorRef = injector;
}
export function injectorFactory() {
if (!tempInjectorRef) {
throw new Error('Trying to get the AngularJS injector before it being set.');
}
const injector: IInjectorService = tempInjectorRef;
tempInjectorRef = null; // clear the value to prevent memory leaks
return injector;
}
export function rootScopeFactory(i: IInjectorService) {
return i.get('$rootScope');
}
export function compileFactory(i: IInjectorService) {
return i.get('$compile');
}
export function parseFactory(i: IInjectorService) {
return i.get('$parse');
}
export 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']}
];

View File

@ -1,189 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Injector, NgModuleFactory, NgModuleRef, StaticProvider} from '@angular/core';
import {platformBrowser} from '@angular/platform-browser';
import {IInjectorService, IProvideService, module as angularModule} from '../common/angular1';
import {$INJECTOR, $PROVIDE, DOWNGRADED_MODULE_COUNT_KEY, INJECTOR_KEY, LAZY_MODULE_REF, UPGRADE_APP_TYPE_KEY, UPGRADE_MODULE_NAME} from '../common/constants';
import {LazyModuleRef, UpgradeAppType, getDowngradedModuleCount, isFunction} from '../common/util';
import {angular1Providers, setTempInjectorRef} from './angular1_providers';
import {NgAdapterInjector} from './util';
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` or a function:
* - `NgModuleFactory`: If you pass an `NgModuleFactory`, it will be used to instantiate a module
* using `platformBrowser`'s {@link PlatformRef#bootstrapModuleFactory bootstrapModuleFactory()}.
* - `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](guide/upgrade-performance).
*
* @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="alert is-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](guide/upgrade-performance).
*
* <div class="alert is-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 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
*/
export function downgradeModule<T>(
moduleFactoryOrBootstrapFn: NgModuleFactory<T>|
((extraProviders: StaticProvider[]) => Promise<NgModuleRef<T>>)): string {
const lazyModuleName = `${UPGRADE_MODULE_NAME}.lazy${++moduleUid}`;
const lazyModuleRefKey = `${LAZY_MODULE_REF}${lazyModuleName}`;
const lazyInjectorKey = `${INJECTOR_KEY}${lazyModuleName}`;
const bootstrapFn = isFunction(moduleFactoryOrBootstrapFn) ?
moduleFactoryOrBootstrapFn :
(extraProviders: StaticProvider[]) =>
platformBrowser(extraProviders).bootstrapModuleFactory(moduleFactoryOrBootstrapFn);
let injector: Injector;
// Create an ng1 module to bootstrap.
angularModule(lazyModuleName, [])
.constant(UPGRADE_APP_TYPE_KEY, 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: IInjectorService) => {
setTempInjectorRef($injector);
const result: LazyModuleRef = {
promise: bootstrapFn(angular1Providers).then(ref => {
injector = result.injector = new NgAdapterInjector(ref.injector);
injector.get($INJECTOR);
return injector;
})
};
return result;
}
])
.config([
$INJECTOR, $PROVIDE,
($injector: IInjectorService, $provide: IProvideService) => {
$provide.constant(DOWNGRADED_MODULE_COUNT_KEY, getDowngradedModuleCount($injector) + 1);
}
]);
return lazyModuleName;
}
function identity<T = any>(x: T): T {
return x;
}

View File

@ -1,297 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {DoCheck, ElementRef, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, SimpleChanges, ɵlooseIdentical as looseIdentical} from '@angular/core';
import {IAttributes, IAugmentedJQuery, IDirective, IDirectivePrePost, IInjectorService, ILinkFn, IScope, ITranscludeFunction} from '../common/angular1';
import {$SCOPE} from '../common/constants';
import {IBindingDestination, IControllerInstance, UpgradeHelper} from '../common/upgrade_helper';
import {isFunction} from '../common/util';
const NOT_SUPPORTED: any = 'NOT_SUPPORTED';
const INITIAL_VALUE = {
__UNINITIALIZED__: true
};
class Bindings {
twoWayBoundProperties: string[] = [];
twoWayBoundLastValues: any[] = [];
expressionBoundProperties: string[] = [];
propertyToOutputMap: {[propName: string]: string} = {};
}
/**
* @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
*/
export class UpgradeComponent implements OnInit, OnChanges, DoCheck, OnDestroy {
private helper: UpgradeHelper;
private $injector: IInjectorService;
private element: Element;
private $element: IAugmentedJQuery;
private $componentScope: IScope;
private directive: IDirective;
private bindings: Bindings;
// TODO(issue/24571): remove '!'.
private controllerInstance !: IControllerInstance;
// TODO(issue/24571): remove '!'.
private bindingDestination !: IBindingDestination;
// 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.
// TODO(issue/24571): remove '!'.
private pendingChanges !: SimpleChanges | null;
// TODO(issue/24571): remove '!'.
private unregisterDoCheckWatcher !: Function;
/**
* 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(private name: string, private elementRef: ElementRef, private injector: Injector) {
this.helper = new UpgradeHelper(injector, name, elementRef);
this.$injector = this.helper.$injector;
this.element = this.helper.element;
this.$element = this.helper.$element;
this.directive = this.helper.directive;
this.bindings = this.initializeBindings(this.directive);
// 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();
}
ngOnInit() {
// Collect contents, insert and compile template
const attachChildNodes: ILinkFn|undefined = this.helper.prepareTransclusion();
const linkFn = this.helper.compileTemplate();
// Instantiate controller
const controllerType = this.directive.controller;
const bindToController = this.directive.bindToController;
if (controllerType) {
this.controllerInstance = this.helper.buildController(controllerType, this.$componentScope);
} else if (bindToController) {
throw new Error(
`Upgraded directive '${this.directive.name}' specifies 'bindToController' but no controller.`);
}
// Set up outputs
this.bindingDestination = bindToController ? this.controllerInstance : this.$componentScope;
this.bindOutputs();
// Require other controllers
const requiredControllers =
this.helper.resolveAndBindRequiredControllers(this.controllerInstance);
// Hook: $onChanges
if (this.pendingChanges) {
this.forwardChanges(this.pendingChanges);
this.pendingChanges = null;
}
// Hook: $onInit
if (this.controllerInstance && isFunction(this.controllerInstance.$onInit)) {
this.controllerInstance.$onInit();
}
// Hook: $doCheck
if (this.controllerInstance && isFunction(this.controllerInstance.$doCheck)) {
const callDoCheck = () => this.controllerInstance.$doCheck !();
this.unregisterDoCheckWatcher = this.$componentScope.$parent.$watch(callDoCheck);
callDoCheck();
}
// Linking
const link = this.directive.link;
const preLink = (typeof link == 'object') && (link as IDirectivePrePost).pre;
const postLink = (typeof link == 'object') ? (link as IDirectivePrePost).post : link;
const attrs: IAttributes = NOT_SUPPORTED;
const transcludeFn: ITranscludeFunction = 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();
}
}
ngOnChanges(changes: SimpleChanges) {
if (!this.bindingDestination) {
this.pendingChanges = changes;
} else {
this.forwardChanges(changes);
}
}
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 (!looseIdentical(newValue, oldValue)) {
const outputName = propertyToOutputMap[propName];
const eventEmitter: EventEmitter<any> = (this as any)[outputName];
eventEmitter.emit(newValue);
twoWayBoundLastValues[idx] = newValue;
}
});
}
ngOnDestroy() {
if (isFunction(this.unregisterDoCheckWatcher)) {
this.unregisterDoCheckWatcher();
}
this.helper.onDestroy(this.$componentScope, this.controllerInstance);
}
private initializeBindings(directive: IDirective) {
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 '${this.name}' directive.`);
}
});
}
return bindings;
}
private initializeOutputs() {
// Initialize the outputs for `=` and `&` bindings
this.bindings.twoWayBoundProperties.concat(this.bindings.expressionBoundProperties)
.forEach(propName => {
const outputName = this.bindings.propertyToOutputMap[propName];
(this as any)[outputName] = new EventEmitter();
});
}
private bindOutputs() {
// Bind `&` bindings to the corresponding outputs
this.bindings.expressionBoundProperties.forEach(propName => {
const outputName = this.bindings.propertyToOutputMap[propName];
const emitter = (this as any)[outputName];
this.bindingDestination[propName] = (value: any) => emitter.emit(value);
});
}
private forwardChanges(changes: SimpleChanges) {
// Forward input changes to `bindingDestination`
Object.keys(changes).forEach(
propName => this.bindingDestination[propName] = changes[propName].currentValue);
if (isFunction(this.bindingDestination.$onChanges)) {
this.bindingDestination.$onChanges(changes);
}
}
}

View File

@ -1,285 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Injector, NgModule, NgZone, Testability} from '@angular/core';
import {IInjectorService, IIntervalService, IProvideService, ITestabilityService, bootstrap, element as angularElement, module as angularModule} from '../common/angular1';
import {$$TESTABILITY, $DELEGATE, $INJECTOR, $INTERVAL, $PROVIDE, INJECTOR_KEY, LAZY_MODULE_REF, UPGRADE_APP_TYPE_KEY, UPGRADE_MODULE_NAME} from '../common/constants';
import {LazyModuleRef, UpgradeAppType, controllerKey} from '../common/util';
import {angular1Providers, setTempInjectorRef} from './angular1_providers';
import {NgAdapterInjector} from './util';
/**
* @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`.
* 3. 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
* Importing this `NgModule` will add providers for the core
* [AngularJS services](https://docs.angularjs.org/api/ng/service) to the root injector.
*
* * Bootstrap
* 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 {@link NgModule Angular `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 bootstraping your top level Angular `NgModule`.
*
* {@example upgrade/static/ts/full/module.ts region='bootstrap-ng2'}
*
* {@a upgrading-an-angular-1-service}
* ### 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
*/
@NgModule({providers: [angular1Providers]})
export class UpgradeModule {
/**
* The AngularJS `$injector` for the upgrade application.
*/
public $injector: any /*angular.IInjectorService*/;
/** The Angular Injector **/
public injector: Injector;
constructor(
/** The root `Injector` for the upgrade application. */
injector: Injector,
/** The bootstrap zone for the upgrade application */
public ngZone: NgZone) {
this.injector = new NgAdapterInjector(injector);
}
/**
* 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
*/
bootstrap(
element: Element, modules: string[] = [], config?: any /*angular.IAngularBootstrapConfig*/) {
const INIT_MODULE_NAME = UPGRADE_MODULE_NAME + '.init';
// Create an ng1 module to bootstrap
const initModule =
angularModule(INIT_MODULE_NAME, [])
.constant(UPGRADE_APP_TYPE_KEY, UpgradeAppType.Static)
.value(INJECTOR_KEY, this.injector)
.factory(
LAZY_MODULE_REF,
[INJECTOR_KEY, (injector: Injector) => ({ injector } as LazyModuleRef)])
.config([
$PROVIDE, $INJECTOR,
($provide: IProvideService, $injector: IInjectorService) => {
if ($injector.has($$TESTABILITY)) {
$provide.decorator($$TESTABILITY, [
$DELEGATE,
(testabilityDelegate: ITestabilityService) => {
const originalWhenStable: Function = testabilityDelegate.whenStable;
const injector = this.injector;
// Cannot use arrow function below because we need the context
const newWhenStable = function(callback: Function) {
originalWhenStable.call(testabilityDelegate, function() {
const ng2Testability: Testability = 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: IIntervalService) => {
// 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: Function, delay: number, count?: number, invokeApply?: boolean,
...pass: any[]) => {
return this.ngZone.runOutsideAngular(() => {
return intervalDelegate((...args: any[]) => {
// 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);
});
};
(wrappedInterval as any)['cancel'] = intervalDelegate.cancel;
return wrappedInterval;
}
]);
}
}
])
.run([
$INJECTOR,
($injector: IInjectorService) => {
this.$injector = $injector;
// Initialize the ng1 $injector provider
setTempInjectorRef($injector);
this.injector.get($INJECTOR);
// Put the injector on the DOM, so that it can be "required"
angularElement(element).data !(controllerKey(INJECTOR_KEY), this.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 $rootScope = $injector.get('$rootScope');
const subscription =
this.ngZone.onMicrotaskEmpty.subscribe(() => $rootScope.$digest());
$rootScope.$on('$destroy', () => { subscription.unsubscribe(); });
}, 0);
}
]);
const upgradeModule = angularModule(UPGRADE_MODULE_NAME, [INIT_MODULE_NAME].concat(modules));
// Make sure resumeBootstrap() only exists if the current bootstrap is deferred
const windowAngular = (window as any)['angular'];
windowAngular.resumeBootstrap = undefined;
// Bootstrap the AngularJS application inside our zone
this.ngZone.run(() => { bootstrap(element, [upgradeModule.name], config); });
// Patch resumeBootstrap() to run inside the ngZone
if (windowAngular.resumeBootstrap) {
const originalResumeBootstrap: () => void = windowAngular.resumeBootstrap;
const ngZone = this.ngZone;
windowAngular.resumeBootstrap = function() {
let args = arguments;
windowAngular.resumeBootstrap = originalResumeBootstrap;
return ngZone.run(() => windowAngular.resumeBootstrap.apply(this, args));
};
}
}
}

View File

@ -1,26 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Injector, ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from '@angular/core';
export class NgAdapterInjector implements Injector {
constructor(private modInjector: Injector) {}
// 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: any, notFoundValue?: any): any {
if (notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) {
return notFoundValue;
}
return this.modInjector.get(token, notFoundValue);
}
}