Skip to content

Commit

Permalink
feat(upgrade): additional ng2 features for downgraded ng2 components
Browse files Browse the repository at this point in the history
- Full use of core Angular 2 projection for downgraded Angular 2
  components. In particular, this enables multi-slot projection and
  other features on <ng-content>.
- Hierarchical injectors for downgraded Angular 2 components: downgraded
  components inherit the injector of the first other downgraded Angular
  2 component they find up the DOM tree.
- declareNg1Module() and initForNg1Tests() methods on the UpgradeAdapter
  to allow testing hybrid applications through Angular 1 without having
  to redeclare the adapter module and recompile every downgraded component
  for every test.

Closes angular#6629, angular#7727, angular#8729, angular#5462, angular#9643, angular#9649
  • Loading branch information
youdz committed Oct 14, 2016
1 parent 606e518 commit e9aca16
Show file tree
Hide file tree
Showing 13 changed files with 570 additions and 157 deletions.
13 changes: 13 additions & 0 deletions modules/@angular/compiler/src/private_export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import * as compiler from './compiler';
import * as directive_normalizer from './directive_normalizer';
import * as lexer from './expression_parser/lexer';
import * as parser from './expression_parser/parser';
Expand All @@ -16,6 +17,7 @@ import * as ng_module_compiler from './ng_module_compiler';
import * as path_util from './output/path_util';
import * as ts_emitter from './output/ts_emitter';
import * as parse_util from './parse_util';
import * as runtime_compiler from './runtime_compiler';
import * as dom_element_schema_registry from './schema/dom_element_schema_registry';
import * as selector from './selector';
import * as style_compiler from './style_compiler';
Expand Down Expand Up @@ -72,9 +74,17 @@ export const __compiler_private__: {

_TemplateParseResult?: template_parser.TemplateParseResult;

createElementCssSelector?: typeof template_parser.createElementCssSelector;

_DomElementSchemaRegistry?: dom_element_schema_registry.DomElementSchemaRegistry;
DomElementSchemaRegistry: typeof dom_element_schema_registry.DomElementSchemaRegistry;

_RuntimeCompiler?: runtime_compiler.RuntimeCompiler;
RuntimeCompiler: typeof runtime_compiler.RuntimeCompiler;

_RuntimeCompilerFactory?: compiler.RuntimeCompilerFactory;
RuntimeCompilerFactory: typeof compiler.RuntimeCompilerFactory;

_StyleCompiler?: style_compiler.StyleCompiler;
StyleCompiler: typeof style_compiler.StyleCompiler;

Expand Down Expand Up @@ -104,7 +114,10 @@ export const __compiler_private__: {
ParseSourceFile: parse_util.ParseSourceFile,
ParseSourceSpan: parse_util.ParseSourceSpan,
TemplateParser: template_parser.TemplateParser,
createElementCssSelector: template_parser.createElementCssSelector,
DomElementSchemaRegistry: dom_element_schema_registry.DomElementSchemaRegistry,
RuntimeCompiler: runtime_compiler.RuntimeCompiler,
RuntimeCompilerFactory: compiler.RuntimeCompilerFactory,
StyleCompiler: style_compiler.StyleCompiler,
ViewCompiler: view_compiler.ViewCompiler,
NgModuleCompiler: ng_module_compiler.NgModuleCompiler,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1162,7 +1162,8 @@ class ElementContext {
}
}

function createElementCssSelector(elementName: string, matchableAttrs: string[][]): CssSelector {
export function createElementCssSelector(
elementName: string, matchableAttrs: string[][]): CssSelector {
const cssSelector = new CssSelector();
const elNameNoNs = splitNsName(elementName)[1];

Expand Down
5 changes: 4 additions & 1 deletion modules/@angular/upgrade/src/angular_js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface IRootScopeService {
$new(isolate?: boolean): IScope;
$id: string;
$watch(expr: any, 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;
Expand Down Expand Up @@ -96,6 +97,8 @@ export interface IAugmentedJQuery {
inheritedData(name: string, value?: any): any;
contents(): IAugmentedJQuery;
parent(): IAugmentedJQuery;
empty(): void;
append(nodes: Node[]): IAugmentedJQuery;
length: number;
[index: number]: Node;
}
Expand Down Expand Up @@ -133,7 +136,7 @@ function noNg() {
}

var angular: {
bootstrap: (e: Element, modules: string[], config: IAngularBootstrapConfig) => void,
bootstrap: (e: Element, modules: (string | any[])[], config: IAngularBootstrapConfig) => void,
module: (prefix: string, dependencies?: string[]) => IModule,
element: (e: Element) => IAugmentedJQuery,
version: {major: number}, resumeBootstrap?: () => void,
Expand Down
4 changes: 3 additions & 1 deletion modules/@angular/upgrade/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
*/

export const NG2_COMPILER = 'ng2.Compiler';
export const NG2_COMPILER_SPY = 'ng2.RuntimeCompilerSpy';
export const NG2_INJECTOR = 'ng2.Injector';
export const NG2_COMPONENT_FACTORY_REF_MAP = 'ng2.ComponentFactoryRefMap';
export const NG2_ZONE = 'ng2.NgZone';
export const NG2_INIT_PROMISE_COMPLETER = 'ng2.InitPromiseCompleter';

export const NG1_CONTROLLER = '$controller';
export const NG1_SCOPE = '$scope';
Expand All @@ -20,4 +22,4 @@ export const NG1_INJECTOR = '$injector';
export const NG1_PARSE = '$parse';
export const NG1_TEMPLATE_CACHE = '$templateCache';
export const NG1_TESTABILITY = '$$testability';
export const REQUIRE_INJECTOR = '?^' + NG2_INJECTOR;
export const REQUIRE_INJECTOR = '?^^' + NG2_INJECTOR;
25 changes: 6 additions & 19 deletions modules/@angular/upgrade/src/downgrade_ng2_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,23 @@ export class DowngradeNg2ComponentAdapter {
componentRef: ComponentRef<any> = null;
changeDetector: ChangeDetectorRef = null;
componentScope: angular.IScope;
childNodes: Node[];
contentInsertionPoint: Node = null;
elementInjector: Injector;

constructor(
private id: string, private info: ComponentInfo, private element: angular.IAugmentedJQuery,
private info: ComponentInfo, private element: angular.IAugmentedJQuery,
private attrs: angular.IAttributes, private scope: angular.IScope,
private parentInjector: Injector, private parse: angular.IParseService,
private componentFactory: ComponentFactory<any>) {
(<any>this.element[0]).id = id;
this.componentScope = scope.$new();
this.childNodes = <Node[]><any>element.contents();
}

bootstrapNg2() {
bootstrapNg2(projectableNodes: Node[][]) {
var childInjector = ReflectiveInjector.resolveAndCreate(
[{provide: NG1_SCOPE, useValue: this.componentScope}], this.parentInjector);
this.contentInsertionPoint = document.createComment('ng1 insertion point');

this.componentRef = this.componentFactory.create(
childInjector, [[this.contentInsertionPoint]], this.element[0]);
this.componentRef =
this.componentFactory.create(childInjector, projectableNodes, this.element[0]);
this.elementInjector = this.componentRef.injector;
this.changeDetector = this.componentRef.changeDetectorRef;
this.component = this.componentRef.instance;
}
Expand Down Expand Up @@ -103,16 +100,6 @@ export class DowngradeNg2ComponentAdapter {
this.componentScope.$watch(() => this.changeDetector && this.changeDetector.detectChanges());
}

projectContent() {
var childNodes = this.childNodes;
var parent = this.contentInsertionPoint.parentNode;
if (parent) {
for (var i = 0, ii = childNodes.length; i < ii; i++) {
parent.insertBefore(childNodes[i], this.contentInsertionPoint);
}
}
}

setupOutputs() {
var attrs = this.attrs;
var outputs = this.info.outputs;
Expand Down
24 changes: 24 additions & 0 deletions modules/@angular/upgrade/src/platform-upgrade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @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 {CompilerFactory, PlatformRef, Provider, createPlatformFactory} from '@angular/core';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';

import {NG2_COMPILER_SPY} from './constants';
import {RuntimeCompilerSpy, RuntimeCompilerSpyFactory} from './spies';

const INTERNAL_UPGRADE_PLATFORM_PROVIDERS: Provider[] = [
{provide: NG2_COMPILER_SPY, useClass: RuntimeCompilerSpy},
{provide: CompilerFactory, useClass: RuntimeCompilerSpyFactory}
];

/**
* @experimental
*/
export const platformUpgrade =
createPlatformFactory(platformBrowserDynamic, 'upgrade', INTERNAL_UPGRADE_PLATFORM_PROVIDERS);
21 changes: 21 additions & 0 deletions modules/@angular/upgrade/src/private_import_compiler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* @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_private__ as _c} from '@angular/compiler';

export type RuntimeCompilerFactory = typeof _c._RuntimeCompilerFactory;
export var RuntimeCompilerFactory: typeof _c.RuntimeCompilerFactory = _c.RuntimeCompilerFactory;

export type SelectorMatcher = typeof _c.SelectorMatcher;
export var SelectorMatcher: typeof _c.SelectorMatcher = _c.SelectorMatcher;

export type CssSelector = typeof _c.CssSelector;
export var CssSelector: typeof _c.CssSelector = _c.CssSelector;

export var createElementCssSelector: typeof _c.createElementCssSelector =
_c.createElementCssSelector;
50 changes: 50 additions & 0 deletions modules/@angular/upgrade/src/spies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* @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_OPTIONS, Compiler, CompilerOptions, Inject, Injectable} from '@angular/core';

import {NG2_COMPILER_SPY} from './constants';
import {RuntimeCompilerFactory} from './private_import_compiler';
import {isPresent} from './util';

@Injectable()
export class RuntimeCompilerSpyFactory extends RuntimeCompilerFactory {
constructor(
@Inject(COMPILER_OPTIONS) defaultOptions: CompilerOptions[],
@Inject(NG2_COMPILER_SPY) private _spy: RuntimeCompilerSpy) {
super(defaultOptions);
}
createCompiler(options: CompilerOptions[] = []): Compiler {
// Very ugly hack to get access to the compiler's internal objects.
// Better here than in the compiler module itself, though.
let runtimeCompiler: any = <any>super.createCompiler(options);
let spy = this._spy;
let originalCompileTemplate = runtimeCompiler._compileTemplate;
runtimeCompiler._compileTemplate = function(template: any) {
if (!template.isCompiled) {
spy.add(template);
}
return originalCompileTemplate.call(this, template);
};
return runtimeCompiler;
}
}

@Injectable()
export class RuntimeCompilerSpy {
ngContentSelectors: {[componentSelector: string]: string[]} = {};

add(template: any) {
if (!template.isHost) {
let metadata = template.normalizedCompMeta;
if (isPresent(metadata.template) && isPresent(metadata.template.ngContentSelectors)) {
this.ngContentSelectors[metadata.selector] = metadata.template.ngContentSelectors;
}
}
}
}
1 change: 1 addition & 0 deletions modules/@angular/upgrade/src/upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
* @description
* Adapter allowing AngularJS v1 and Angular v2 to run side by side in the same application.
*/
export {platformUpgrade} from './platform-upgrade';
export {UpgradeAdapter, UpgradeAdapterRef} from './upgrade_adapter';
Loading

0 comments on commit e9aca16

Please sign in to comment.