Skip to content

Commit

Permalink
feat(upgrade): return a function (instead of array) from `downgradeIn…
Browse files Browse the repository at this point in the history
…jectable()` (#14037)

This makes it more consistent with the dynamic version of `upgrade` and makes it
possible to share code between the dynamic and static versions.

This commit also refactors the file layout, moving common and dynamic-specific
files to `common/` and `dynamic/` directories respectively and renaming `aot/`
to `static/`.

Some private keys, used as AngularJS DI tokens, have also been renamed, but this
should  not affect apps, since these keys are undocumented and not supposed to
be used externally.

BREAKING CHANGE:

Previously, `upgrade/static/downgradeInjectable` returned an array of the form:

```js
['dep1', 'dep2', ..., function factory(dep1, dep2, ...) { ... }]
```

Now it returns a function with an `$inject` property:

```js
factory.$inject = ['dep1', 'dep2', ...];
function factory(dep1, dep2, ...) { ... }
```

It shouldn't affect the behavior of apps, since both forms are equally suitable
to be used for registering AngularJS injectable services, but it is possible
that type-checking might fail or that current code breaks if it relies on the
returned value being an array.
  • Loading branch information
gkalpak authored and mhevery committed Feb 1, 2017
1 parent 49fce37 commit 1f90f29
Show file tree
Hide file tree
Showing 35 changed files with 169 additions and 181 deletions.
9 changes: 6 additions & 3 deletions modules/@angular/upgrade/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
/**
* @module
* @description
* Entry point for all public APIs of the upgrade package.
* Entry point for all public APIs of the upgrade/dynamic package, allowing
* Angular 1 and Angular 2+ to run side by side in the same application.
*/
export * from './src/upgrade';
// This file only reexports content of the `src` folder. Keep it that way.
export {VERSION} from './src/common/version';
export {UpgradeAdapter, UpgradeAdapterRef} from './src/dynamic/upgrade_adapter';

// This file only re-exports content of the `src` folder. Keep it that way.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

export type Ng1Token = string;

export type Ng1Expression = string | Function;

export interface IAnnotatedFunction extends Function { $inject?: Ng1Token[]; }

export type IInjectable = (Ng1Token | Function)[] | IAnnotatedFunction;
Expand Down Expand Up @@ -42,12 +44,10 @@ export interface IRootScopeService {
$id: string;
$parent: IScope;
$root: IScope;
$watch(expr: any, fn?: (a1?: any, a2?: any) => void): Function;
$watch(exp: Ng1Expression, fn?: (a1?: any, a2?: any) => void): Function;
$on(event: string, fn?: (event?: any, ...args: any[]) => void): Function;
$destroy(): any;
$apply(): any;
$apply(exp: string): any;
$apply(exp: Function): any;
$apply(exp?: Ng1Expression): any;
$digest(): any;
$evalAsync(): any;
$on(event: string, fn?: (event?: any, ...args: any[]) => void): Function;
Expand Down Expand Up @@ -142,6 +142,10 @@ export interface ICacheObject {
get(key: string): any;
}
export interface ITemplateCacheService extends ICacheObject {}
export interface ITemplateRequestService {
(template: string|any /* TrustedResourceUrl */, ignoreRequestError?: boolean): Promise<string>;
totalPendingRequests: number;
}
export type IController = string | IInjectable;
export interface IControllerService {
(controllerConstructor: IController, locals?: any, later?: any, ident?: any): any;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,26 @@
* found in the LICENSE file at https://angular.io/license
*/

export const UPGRADE_MODULE_NAME = '$$UpgradeModule';
export const INJECTOR_KEY = '$$angularInjector';
export const REQUIRE_NG1_MODEL = '?ngModel';

export const $COMPILE = '$compile';
export const $CONTROLLER = '$controller';
export const $DELEGATE = '$delegate';
export const $HTTP_BACKEND = '$httpBackend';
export const $INJECTOR = '$injector';
export const $PARSE = '$parse';
export const $PROVIDE = '$provide';
export const $ROOT_SCOPE = '$rootScope';
export const $SCOPE = '$scope';
export const $PROVIDE = '$provide';
export const $DELEGATE = '$delegate';
export const $TEMPLATE_CACHE = '$templateCache';
export const $TEMPLATE_REQUEST = '$templateRequest';

export const $$TESTABILITY = '$$testability';

export const $COMPILE = '$compile';
export const $TEMPLATE_CACHE = '$templateCache';
export const $HTTP_BACKEND = '$httpBackend';
export const $CONTROLLER = '$controller';
export const COMPILER_KEY = '$$angularCompiler';
export const COMPONENT_FACTORY_REF_MAP_KEY = '$$angularComponentFactoryRefMap';
export const INJECTOR_KEY = '$$angularInjector';
export const NG_ZONE_KEY = '$$angularNgZone';

export const REQUIRE_INJECTOR = '?^^' + INJECTOR_KEY;
export const REQUIRE_NG_MODEL = '?ngModel';

export const UPGRADE_MODULE_NAME = '$$UpgradeModule';
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ import {INJECTOR_KEY} from './constants';
*
* @experimental
*/
export function downgradeInjectable(token: any) {
return [INJECTOR_KEY, (i: Injector) => i.get(token)];
}
export function downgradeInjectable(token: any): Function {
const factory = function(i: Injector) { return i.get(token); };
(factory as any).$inject = [INJECTOR_KEY];

return factory;
}
File renamed without changes.
File renamed without changes.
25 changes: 0 additions & 25 deletions modules/@angular/upgrade/src/constants.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@

import {ChangeDetectorRef, ComponentFactory, ComponentRef, EventEmitter, Injector, OnChanges, ReflectiveInjector, SimpleChange, SimpleChanges} from '@angular/core';

import * as angular from './angular_js';
import {NG1_SCOPE} from './constants';
import * as angular from '../common/angular1';
import {$SCOPE} from '../common/constants';

import {ComponentInfo} from './metadata';
import {hookupNgModel} from './util';

Expand All @@ -35,7 +36,7 @@ export class DowngradeNg2ComponentAdapter {

bootstrapNg2(projectableNodes: Node[][]) {
const childInjector = ReflectiveInjector.resolveAndCreate(
[{provide: NG1_SCOPE, useValue: this.componentScope}], this.parentInjector);
[{provide: $SCOPE, useValue: this.componentScope}], this.parentInjector);

this.componentRef =
this.componentFactory.create(childInjector, projectableNodes, this.element[0]);
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import {CssSelector, SelectorMatcher, createElementCssSelector} from '@angular/c
import {Compiler, CompilerOptions, ComponentFactory, Injector, NgModule, NgModuleRef, NgZone, Provider, Testability, Type} from '@angular/core';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';

import * as angular from './angular_js';
import {NG1_COMPILE, NG1_INJECTOR, NG1_PARSE, NG1_ROOT_SCOPE, NG1_TESTABILITY, NG2_COMPILER, NG2_COMPONENT_FACTORY_REF_MAP, NG2_INJECTOR, NG2_ZONE, REQUIRE_INJECTOR, REQUIRE_NG1_MODEL} from './constants';
import * as angular from '../common/angular1';
import {$$TESTABILITY, $COMPILE, $INJECTOR, $PARSE, $ROOT_SCOPE, COMPILER_KEY, COMPONENT_FACTORY_REF_MAP_KEY, INJECTOR_KEY, NG_ZONE_KEY, REQUIRE_INJECTOR, REQUIRE_NG_MODEL} from '../common/constants';
import {downgradeInjectable} from '../common/downgrade_injectable';
import {Deferred, controllerKey, getAttributesAsArray, onError} from '../common/util';

import {DowngradeNg2ComponentAdapter} from './downgrade_ng2_adapter';
import {ComponentInfo, getComponentInfo} from './metadata';
import {UpgradeNg1ComponentAdapterBuilder} from './upgrade_ng1_adapter';
import {Deferred, controllerKey, getAttributesAsArray, onError} from './util';

let upgradeCount: number = 0;

Expand Down Expand Up @@ -138,8 +140,8 @@ export class UpgradeAdapter {
* 2. Even thought the component is instantiated in AngularJS, it will be using Angular
* syntax. This has to be done, this way because we must follow Angular components do not
* declare how the attributes should be interpreted.
* 3. ng-model is controlled by AngularJS v1 and communicates with the downgraded Ng2 component
* by way of the ControlValueAccessor interface from @angular/forms. Only components that
* 3. `ng-model` is controlled by AngularJS and communicates with the downgraded Angular component
* by way of the `ControlValueAccessor` interface from @angular/forms. Only components that
* implement this interface are eligible.
*
* ## Supported Features
Expand Down Expand Up @@ -397,7 +399,7 @@ export class UpgradeAdapter {
});

Promise.all([this.ng2BootstrapDeferred.promise, ng1BootstrapPromise]).then(([ng1Injector]) => {
angular.element(element).data(controllerKey(NG2_INJECTOR), this.moduleRef.injector);
angular.element(element).data(controllerKey(INJECTOR_KEY), this.moduleRef.injector);
this.moduleRef.injector.get(NgZone).run(
() => { (<any>upgrade)._bootstrapDone(this.moduleRef, ng1Injector); });
}, onError);
Expand Down Expand Up @@ -440,7 +442,7 @@ export class UpgradeAdapter {
this.providers.push({
provide: token,
useFactory: (ng1Injector: angular.IInjectorService) => ng1Injector.get(name),
deps: [NG1_INJECTOR]
deps: [$INJECTOR]
});
}

Expand All @@ -465,12 +467,7 @@ export class UpgradeAdapter {
*
* ```
*/
public downgradeNg2Provider(token: any): Function {
const factory = function(injector: Injector) { return injector.get(token); };
(<any>factory).$inject = [NG2_INJECTOR];
return factory;
}

downgradeNg2Provider(token: any): Function { return downgradeInjectable(token); }

/**
* Declare the AngularJS upgrade module for this adapter without bootstrapping the whole
Expand Down Expand Up @@ -500,14 +497,14 @@ export class UpgradeAdapter {

this.ngZone = new NgZone({enableLongStackTrace: Zone.hasOwnProperty('longStackTraceZoneSpec')});
this.ng2BootstrapDeferred = new Deferred();
ng1Module.factory(NG2_INJECTOR, () => this.moduleRef.injector.get(Injector))
.constant(NG2_ZONE, this.ngZone)
.constant(NG2_COMPONENT_FACTORY_REF_MAP, componentFactoryRefMap)
.factory(NG2_COMPILER, () => this.moduleRef.injector.get(Compiler))
ng1Module.factory(INJECTOR_KEY, () => this.moduleRef.injector.get(Injector))
.constant(NG_ZONE_KEY, this.ngZone)
.constant(COMPONENT_FACTORY_REF_MAP_KEY, componentFactoryRefMap)
.factory(COMPILER_KEY, () => this.moduleRef.injector.get(Compiler))
.config([
'$provide', '$injector',
(provide: angular.IProvideService, ng1Injector: angular.IInjectorService) => {
provide.decorator(NG1_ROOT_SCOPE, [
provide.decorator($ROOT_SCOPE, [
'$delegate',
function(rootScopeDelegate: angular.IRootScopeService) {
// Capture the root apply so that we can delay first call to $apply until we
Expand All @@ -522,8 +519,8 @@ export class UpgradeAdapter {
return rootScope = rootScopeDelegate;
}
]);
if (ng1Injector.has(NG1_TESTABILITY)) {
provide.decorator(NG1_TESTABILITY, [
if (ng1Injector.has($$TESTABILITY)) {
provide.decorator($$TESTABILITY, [
'$delegate',
function(testabilityDelegate: angular.ITestabilityService) {
const originalWhenStable: Function = testabilityDelegate.whenStable;
Expand Down Expand Up @@ -558,8 +555,8 @@ export class UpgradeAdapter {
const DynamicNgUpgradeModule =
NgModule({
providers: [
{provide: NG1_INJECTOR, useFactory: () => ng1Injector},
{provide: NG1_COMPILE, useFactory: () => ng1Injector.get(NG1_COMPILE)},
{provide: $INJECTOR, useFactory: () => ng1Injector},
{provide: $COMPILE, useFactory: () => ng1Injector.get($COMPILE)},
this.providers
],
imports: [this.ng2AppModule]
Expand Down Expand Up @@ -620,7 +617,7 @@ class ParentInjectorPromise {

constructor(private element: angular.IAugmentedJQuery) {
// store the promise on the element
element.data(controllerKey(NG2_INJECTOR), this);
element.data(controllerKey(INJECTOR_KEY), this);
}

then(callback: (injector: Injector) => any) {
Expand All @@ -635,7 +632,7 @@ class ParentInjectorPromise {
this.injector = injector;

// reset the element data to point to the real injector
this.element.data(controllerKey(NG2_INJECTOR), injector);
this.element.data(controllerKey(INJECTOR_KEY), injector);

// clean out the element to prevent memory leaks
this.element = null;
Expand All @@ -648,8 +645,7 @@ class ParentInjectorPromise {


function ng1ComponentDirective(info: ComponentInfo, idPrefix: string): Function {
(<any>directiveFactory).$inject =
[NG1_INJECTOR, NG1_COMPILE, NG2_COMPONENT_FACTORY_REF_MAP, NG1_PARSE];
(<any>directiveFactory).$inject = [$INJECTOR, $COMPILE, COMPONENT_FACTORY_REF_MAP_KEY, $PARSE];
function directiveFactory(
ng1Injector: angular.IInjectorService, ng1Compile: angular.ICompileService,
componentFactoryRefMap: ComponentFactoryRefMap,
Expand All @@ -659,7 +655,7 @@ function ng1ComponentDirective(info: ComponentInfo, idPrefix: string): Function
return {
restrict: 'E',
terminal: true,
require: [REQUIRE_INJECTOR, REQUIRE_NG1_MODEL],
require: [REQUIRE_INJECTOR, REQUIRE_NG_MODEL],
compile: (templateElement: angular.IAugmentedJQuery, templateAttributes: angular.IAttributes,
transclude: angular.ITranscludeFunction) => {
// We might have compile the contents lazily, because this might have been triggered by the
Expand All @@ -671,11 +667,12 @@ function ng1ComponentDirective(info: ComponentInfo, idPrefix: string): Function
let id = idPrefix + (idCount++);
(<any>element[0]).id = id;

let parentInjector: Injector|ParentInjectorPromise = required[0];
const ngModel: angular.INgModelController = required[1];
let parentInjector: Injector | ParentInjectorPromise = required[0];
let injectorPromise = new ParentInjectorPromise(element);

const ng2Compiler = ng1Injector.get(NG2_COMPILER) as Compiler;
const ngModel: angular.INgModelController = required[1];

const ng2Compiler = ng1Injector.get(COMPILER_KEY) as Compiler;
const ngContentSelectors = ng2Compiler.getNgContentSelectors(info.type);
const linkFns = compileProjectedNodes(templateElement, ngContentSelectors);

Expand All @@ -693,7 +690,7 @@ function ng1ComponentDirective(info: ComponentInfo, idPrefix: string): Function
return projectedClone;
});

parentInjector = parentInjector || ng1Injector.get(NG2_INJECTOR);
parentInjector = parentInjector || ng1Injector.get(INJECTOR_KEY);

if (parentInjector instanceof ParentInjectorPromise) {
parentInjector.then((resolvedInjector: Injector) => downgrade(resolvedInjector));
Expand Down Expand Up @@ -748,7 +745,7 @@ export class UpgradeAdapterRef {
this.ng2ModuleRef = ngModuleRef;
this.ng2Injector = ngModuleRef.injector;
this.ng1Injector = ng1Injector;
this.ng1RootScope = ng1Injector.get(NG1_ROOT_SCOPE);
this.ng1RootScope = ng1Injector.get($ROOT_SCOPE);
this._readyFn && this._readyFn(this);
}

Expand All @@ -765,7 +762,7 @@ export class UpgradeAdapterRef {
* Dispose of running hybrid AngularJS / Angular application.
*/
public dispose() {
this.ng1Injector.get(NG1_ROOT_SCOPE).$destroy();
this.ng1Injector.get($ROOT_SCOPE).$destroy();
this.ng2ModuleRef.destroy();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@

import {Directive, DoCheck, ElementRef, EventEmitter, Inject, OnChanges, OnInit, SimpleChange, SimpleChanges, Type} from '@angular/core';

import * as angular from './angular_js';
import {NG1_COMPILE, NG1_CONTROLLER, NG1_HTTP_BACKEND, NG1_SCOPE, NG1_TEMPLATE_CACHE} from './constants';
import {controllerKey} from './util';
import * as angular from '../common/angular1';
import {$COMPILE, $CONTROLLER, $HTTP_BACKEND, $SCOPE, $TEMPLATE_CACHE} from '../common/constants';
import {controllerKey} from '../common/util';


interface IBindingDestination {
[key: string]: any;
Expand Down Expand Up @@ -55,7 +56,7 @@ export class UpgradeNg1ComponentAdapterBuilder {
Directive({selector: selector, inputs: this.inputsRename, outputs: this.outputsRename})
.Class({
constructor: [
new Inject(NG1_SCOPE), ElementRef,
new Inject($SCOPE), ElementRef,
function(scope: angular.IScope, elementRef: ElementRef) {
return new UpgradeNg1ComponentAdapter(
self.linkFn, scope, self.directive, elementRef, self.$controller, self.inputs,
Expand Down Expand Up @@ -188,10 +189,10 @@ export class UpgradeNg1ComponentAdapterBuilder {
exportedComponents: {[name: string]: UpgradeNg1ComponentAdapterBuilder},
injector: angular.IInjectorService): Promise<angular.ILinkFn[]> {
const promises: Promise<angular.ILinkFn>[] = [];
const compile: angular.ICompileService = injector.get(NG1_COMPILE);
const templateCache: angular.ITemplateCacheService = injector.get(NG1_TEMPLATE_CACHE);
const httpBackend: angular.IHttpBackendService = injector.get(NG1_HTTP_BACKEND);
const $controller: angular.IControllerService = injector.get(NG1_CONTROLLER);
const compile: angular.ICompileService = injector.get($COMPILE);
const templateCache: angular.ITemplateCacheService = injector.get($TEMPLATE_CACHE);
const httpBackend: angular.IHttpBackendService = injector.get($HTTP_BACKEND);
const $controller: angular.IControllerService = injector.get($CONTROLLER);
for (const name in exportedComponents) {
if ((<any>exportedComponents).hasOwnProperty(name)) {
const exportedComponent = exportedComponents[name];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import * as angular from '../angular_js';
import * as angular 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
Expand Down
Loading

0 comments on commit 1f90f29

Please sign in to comment.