refactor(core): remove deprecated @Component.directives and @Component.pipes

BREAKING CHANGE: previously deprecated @Component.directives and @Component.pipes support was removed.

All the components and pipes now must be declarated via an NgModule. NgModule is the basic
compilation block passed into the Angular compiler via Compiler#compileModuleSync or #compileModuleAsync.

Because of this change, the Compiler#compileComponentAsync and #compileComponentSync were removed as well -
any code doing compilation should compile module instead using the apis mentioned above.

Lastly, since modules are the basic compilation unit, the ngUpgrade module was modified to always require
an NgModule to be passed into the UpgradeAdapter's constructor - previously this was optional.
This commit is contained in:
Igor Minar 2016-08-19 13:51:45 -07:00
parent a782232ca3
commit 4a740f23a4
18 changed files with 389 additions and 665 deletions

View File

@ -410,7 +410,7 @@ export class CompileTemplateMetadata {
export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier { export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
static create( static create(
{type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host, providers, {type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host, providers,
viewProviders, queries, viewQueries, entryComponents, viewDirectives, viewPipes, template}: { viewProviders, queries, viewQueries, entryComponents, template}: {
type?: CompileTypeMetadata, type?: CompileTypeMetadata,
isComponent?: boolean, isComponent?: boolean,
selector?: string, selector?: string,
@ -479,8 +479,6 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
queries, queries,
viewQueries, viewQueries,
entryComponents, entryComponents,
viewDirectives,
viewPipes,
template, template,
}); });
} }
@ -500,17 +498,13 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
viewQueries: CompileQueryMetadata[]; viewQueries: CompileQueryMetadata[];
// Note: Need to keep types here to prevent cycles! // Note: Need to keep types here to prevent cycles!
entryComponents: CompileTypeMetadata[]; entryComponents: CompileTypeMetadata[];
// Note: Need to keep types here to prevent cycles!
viewDirectives: CompileTypeMetadata[];
// Note: Need to keep types here to prevent cycles!
viewPipes: CompileTypeMetadata[];
template: CompileTemplateMetadata; template: CompileTemplateMetadata;
constructor( constructor(
{type, isComponent, selector, exportAs, changeDetection, inputs, outputs, hostListeners, {type, isComponent, selector, exportAs, changeDetection, inputs, outputs, hostListeners,
hostProperties, hostAttributes, providers, viewProviders, queries, viewQueries, hostProperties, hostAttributes, providers, viewProviders, queries, viewQueries,
entryComponents, viewDirectives, viewPipes, template}: { entryComponents, template}: {
type?: CompileTypeMetadata, type?: CompileTypeMetadata,
isComponent?: boolean, isComponent?: boolean,
selector?: string, selector?: string,
@ -547,8 +541,6 @@ export class CompileDirectiveMetadata implements CompileMetadataWithIdentifier {
this.queries = _normalizeArray(queries); this.queries = _normalizeArray(queries);
this.viewQueries = _normalizeArray(viewQueries); this.viewQueries = _normalizeArray(viewQueries);
this.entryComponents = _normalizeArray(entryComponents); this.entryComponents = _normalizeArray(entryComponents);
this.viewDirectives = _normalizeArray(viewDirectives);
this.viewPipes = _normalizeArray(viewPipes);
this.template = template; this.template = template;
} }

View File

@ -142,8 +142,6 @@ export class DirectiveResolver {
providers: dm.providers, providers: dm.providers,
viewProviders: dm.viewProviders, viewProviders: dm.viewProviders,
entryComponents: dm.entryComponents, entryComponents: dm.entryComponents,
directives: dm.directives,
pipes: dm.pipes,
template: dm.template, template: dm.template,
templateUrl: dm.templateUrl, templateUrl: dm.templateUrl,
styles: dm.styles, styles: dm.styles,

View File

@ -8,12 +8,11 @@
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, AttributeMetadata, ChangeDetectionStrategy, ComponentMetadata, HostMetadata, InjectMetadata, Injectable, ModuleWithProviders, OptionalMetadata, Provider, QueryMetadata, SchemaMetadata, SelfMetadata, SkipSelfMetadata, Type, ViewQueryMetadata, resolveForwardRef} from '@angular/core'; import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, AttributeMetadata, ChangeDetectionStrategy, ComponentMetadata, HostMetadata, InjectMetadata, Injectable, ModuleWithProviders, OptionalMetadata, Provider, QueryMetadata, SchemaMetadata, SelfMetadata, SkipSelfMetadata, Type, ViewQueryMetadata, resolveForwardRef} from '@angular/core';
import {Console, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from '../core_private'; import {LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from '../core_private';
import {StringMapWrapper} from '../src/facade/collection'; import {StringMapWrapper} from '../src/facade/collection';
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions'; import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
import * as cpl from './compile_metadata'; import * as cpl from './compile_metadata';
import {CompilerConfig} from './config';
import {DirectiveResolver} from './directive_resolver'; import {DirectiveResolver} from './directive_resolver';
import {BaseException} from './facade/exceptions'; import {BaseException} from './facade/exceptions';
import {isArray, isBlank, isPresent, isString, stringify} from './facade/lang'; import {isArray, isBlank, isPresent, isString, stringify} from './facade/lang';
@ -36,8 +35,7 @@ export class CompileMetadataResolver {
constructor( constructor(
private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver, private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver,
private _pipeResolver: PipeResolver, private _config: CompilerConfig, private _pipeResolver: PipeResolver, private _schemaRegistry: ElementSchemaRegistry,
private _console: Console, private _schemaRegistry: ElementSchemaRegistry,
private _reflector: ReflectorReader = reflector) {} private _reflector: ReflectorReader = reflector) {}
private sanitizeTokenName(token: any): string { private sanitizeTokenName(token: any): string {
@ -124,9 +122,7 @@ export class CompileMetadataResolver {
var changeDetectionStrategy: ChangeDetectionStrategy = null; var changeDetectionStrategy: ChangeDetectionStrategy = null;
var viewProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = []; var viewProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
var moduleUrl = staticTypeModuleUrl(directiveType); var moduleUrl = staticTypeModuleUrl(directiveType);
var viewDirectiveTypes: cpl.CompileTypeMetadata[] = []; var entryComponentMetadata: cpl.CompileTypeMetadata[] = [];
var viewPipeTypes: cpl.CompileTypeMetadata[] = [];
var entryComponentTypes: cpl.CompileTypeMetadata[] = [];
let selector = dirMeta.selector; let selector = dirMeta.selector;
if (dirMeta instanceof ComponentMetadata) { if (dirMeta instanceof ComponentMetadata) {
var cmpMeta = <ComponentMetadata>dirMeta; var cmpMeta = <ComponentMetadata>dirMeta;
@ -150,32 +146,15 @@ export class CompileMetadataResolver {
changeDetectionStrategy = cmpMeta.changeDetection; changeDetectionStrategy = cmpMeta.changeDetection;
if (isPresent(dirMeta.viewProviders)) { if (isPresent(dirMeta.viewProviders)) {
viewProviders = this.getProvidersMetadata( viewProviders = this.getProvidersMetadata(
verifyNonBlankProviders(directiveType, dirMeta.viewProviders, 'viewProviders'), []); verifyNonBlankProviders(directiveType, dirMeta.viewProviders, 'viewProviders'),
entryComponentMetadata);
} }
moduleUrl = componentModuleUrl(this._reflector, directiveType, cmpMeta); moduleUrl = componentModuleUrl(this._reflector, directiveType, cmpMeta);
if (cmpMeta.entryComponents) { if (cmpMeta.entryComponents) {
entryComponentTypes = entryComponentMetadata =
flattenArray(cmpMeta.entryComponents) flattenArray(cmpMeta.entryComponents)
.map((type) => this.getTypeMetadata(type, staticTypeModuleUrl(type))); .map((type) => this.getTypeMetadata(type, staticTypeModuleUrl(type)))
} .concat(entryComponentMetadata);
if (cmpMeta.directives) {
viewDirectiveTypes = flattenArray(cmpMeta.directives).map((type) => {
if (!type) {
throw new BaseException(
`Unexpected directive value '${type}' on the View of component '${stringify(directiveType)}'`);
}
return this.getTypeMetadata(type, staticTypeModuleUrl(type));
});
}
if (cmpMeta.pipes) {
viewPipeTypes = flattenArray(cmpMeta.pipes).map((type) => {
if (!type) {
throw new BaseException(
`Unexpected pipe value '${type}' on the View of component '${stringify(directiveType)}'`);
}
return this.getTypeMetadata(type, staticTypeModuleUrl(type));
});
} }
if (!selector) { if (!selector) {
selector = this._schemaRegistry.getDefaultComponentElementName(); selector = this._schemaRegistry.getDefaultComponentElementName();
@ -191,7 +170,7 @@ export class CompileMetadataResolver {
if (isPresent(dirMeta.providers)) { if (isPresent(dirMeta.providers)) {
providers = this.getProvidersMetadata( providers = this.getProvidersMetadata(
verifyNonBlankProviders(directiveType, dirMeta.providers, 'providers'), verifyNonBlankProviders(directiveType, dirMeta.providers, 'providers'),
entryComponentTypes); entryComponentMetadata);
} }
var queries: cpl.CompileQueryMetadata[] = []; var queries: cpl.CompileQueryMetadata[] = [];
var viewQueries: cpl.CompileQueryMetadata[] = []; var viewQueries: cpl.CompileQueryMetadata[] = [];
@ -213,9 +192,7 @@ export class CompileMetadataResolver {
viewProviders: viewProviders, viewProviders: viewProviders,
queries: queries, queries: queries,
viewQueries: viewQueries, viewQueries: viewQueries,
viewDirectives: viewDirectiveTypes, entryComponents: entryComponentMetadata
viewPipes: viewPipeTypes,
entryComponents: entryComponentTypes
}); });
this._directiveCache.set(directiveType, meta); this._directiveCache.set(directiveType, meta);
} }
@ -359,19 +336,6 @@ export class CompileMetadataResolver {
return compileMeta; return compileMeta;
} }
addComponentToModule(moduleType: Type<any>, compType: Type<any>) {
const moduleMeta = this.getNgModuleMetadata(moduleType);
// Collect @Component.directives/pipes/entryComponents into our declared directives/pipes.
const compMeta = this.getDirectiveMetadata(compType, false);
this._addDirectiveToModule(
compMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule,
moduleMeta.declaredDirectives);
moduleMeta.transitiveModule.entryComponents.push(compMeta.type);
moduleMeta.entryComponents.push(compMeta.type);
this._verifyModule(moduleMeta);
}
private _verifyModule(moduleMeta: cpl.CompileNgModuleMetadata) { private _verifyModule(moduleMeta: cpl.CompileNgModuleMetadata) {
moduleMeta.exportedDirectives.forEach((dirMeta) => { moduleMeta.exportedDirectives.forEach((dirMeta) => {
@ -386,17 +350,6 @@ export class CompileMetadataResolver {
`Can't export pipe ${stringify(pipeMeta.type.runtime)} from ${stringify(moduleMeta.type.runtime)} as it was neither declared nor imported!`); `Can't export pipe ${stringify(pipeMeta.type.runtime)} from ${stringify(moduleMeta.type.runtime)} as it was neither declared nor imported!`);
} }
}); });
moduleMeta.entryComponents.forEach((entryComponentType) => {
if (!moduleMeta.transitiveModule.directivesSet.has(entryComponentType.runtime)) {
throw new BaseException(
`NgModule ${stringify(moduleMeta.type.runtime)} uses ${stringify(entryComponentType.runtime)} via "entryComponents" but it was neither declared nor imported! If ${stringify(entryComponentType.runtime)} is declared in an imported module, make sure it is exported.`);
}
});
// Collect @Component.directives/pipes/entryComponents into our declared
// directives/pipes. Do this last so that directives added by previous steps
// are considered as well!
moduleMeta.declaredDirectives.forEach(
(dirMeta) => { this._getTransitiveViewDirectivesAndPipes(dirMeta, moduleMeta); });
} }
private _getTypeDescriptor(type: Type<any>): string { private _getTypeDescriptor(type: Type<any>): string {
@ -422,41 +375,6 @@ export class CompileMetadataResolver {
this._ngModuleOfTypes.set(type, moduleType); this._ngModuleOfTypes.set(type, moduleType);
} }
private _getTransitiveViewDirectivesAndPipes(
compMeta: cpl.CompileDirectiveMetadata, moduleMeta: cpl.CompileNgModuleMetadata) {
if (!compMeta.isComponent) {
return;
}
const addPipe = (pipeType: Type<any>) => {
const pipeMeta = this.getPipeMetadata(pipeType);
this._addPipeToModule(
pipeMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule, moduleMeta.declaredPipes);
};
const addDirective = (dirType: Type<any>) => {
const dirMeta = this.getDirectiveMetadata(dirType);
if (this._addDirectiveToModule(
dirMeta, moduleMeta.type.runtime, moduleMeta.transitiveModule,
moduleMeta.declaredDirectives)) {
this._getTransitiveViewDirectivesAndPipes(dirMeta, moduleMeta);
}
};
if (compMeta.viewPipes) {
compMeta.viewPipes.forEach((cplType) => addPipe(cplType.runtime));
}
if (compMeta.viewDirectives) {
compMeta.viewDirectives.forEach((cplType) => addDirective(cplType.runtime));
}
compMeta.entryComponents.forEach((entryComponentType) => {
if (!moduleMeta.transitiveModule.directivesSet.has(entryComponentType.runtime)) {
this._console.warn(
`Component ${stringify(compMeta.type.runtime)} in NgModule ${stringify(moduleMeta.type.runtime)} uses ${stringify(entryComponentType.runtime)} via "entryComponents" but it was neither declared nor imported into the module! This warning will become an error after final.`);
addDirective(entryComponentType.runtime);
}
});
}
private _getTransitiveNgModuleMetadata( private _getTransitiveNgModuleMetadata(
importedModules: cpl.CompileNgModuleMetadata[], importedModules: cpl.CompileNgModuleMetadata[],
exportedModules: cpl.CompileNgModuleMetadata[]): cpl.TransitiveCompileNgModuleMetadata { exportedModules: cpl.CompileNgModuleMetadata[]): cpl.TransitiveCompileNgModuleMetadata {

View File

@ -46,8 +46,7 @@ export class RuntimeCompiler implements Compiler {
private _injector: Injector, private _metadataResolver: CompileMetadataResolver, private _injector: Injector, private _metadataResolver: CompileMetadataResolver,
private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser, private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler, private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
private _ngModuleCompiler: NgModuleCompiler, private _compilerConfig: CompilerConfig, private _ngModuleCompiler: NgModuleCompiler, private _compilerConfig: CompilerConfig) {}
private _console: Console) {}
get injector(): Injector { return this._injector; } get injector(): Injector { return this._injector; }
@ -68,23 +67,6 @@ export class RuntimeCompiler implements Compiler {
return this._compileModuleAndAllComponents(moduleType, false).asyncResult; return this._compileModuleAndAllComponents(moduleType, false).asyncResult;
} }
compileComponentAsync<T>(compType: Type<T>, ngModule: Type<any> = null):
Promise<ComponentFactory<T>> {
if (!ngModule) {
throw new BaseException(
`Calling compileComponentAsync on the root compiler without a module is not allowed! (Compiling component ${stringify(compType)})`);
}
return this._compileComponentInModule(compType, false, ngModule).asyncResult;
}
compileComponentSync<T>(compType: Type<T>, ngModule: Type<any> = null): ComponentFactory<T> {
if (!ngModule) {
throw new BaseException(
`Calling compileComponentSync on the root compiler without a module is not allowed! (Compiling component ${stringify(compType)})`);
}
return this._compileComponentInModule(compType, true, ngModule).syncResult;
}
private _compileModuleAndComponents<T>(moduleType: Type<T>, isSync: boolean): private _compileModuleAndComponents<T>(moduleType: Type<T>, isSync: boolean):
SyncAsyncResult<NgModuleFactory<T>> { SyncAsyncResult<NgModuleFactory<T>> {
const componentPromise = this._compileComponents(moduleType, isSync); const componentPromise = this._compileComponents(moduleType, isSync);
@ -146,17 +128,6 @@ export class RuntimeCompiler implements Compiler {
return ngModuleFactory; return ngModuleFactory;
} }
private _compileComponentInModule<T>(compType: Type<T>, isSync: boolean, moduleType: Type<any>):
SyncAsyncResult<ComponentFactory<T>> {
this._metadataResolver.addComponentToModule(moduleType, compType);
const componentPromise = this._compileComponents(moduleType, isSync);
const componentFactory: ComponentFactory<T> =
this._assertComponentKnown(compType, true).proxyComponentFactory;
return new SyncAsyncResult(componentFactory, componentPromise.then(() => componentFactory));
}
/** /**
* @internal * @internal
*/ */
@ -172,10 +143,13 @@ export class RuntimeCompiler implements Compiler {
dirMeta.entryComponents.forEach((entryComponentType) => { dirMeta.entryComponents.forEach((entryComponentType) => {
templates.add(this._createCompiledHostTemplate(entryComponentType.runtime)); templates.add(this._createCompiledHostTemplate(entryComponentType.runtime));
}); });
// TODO: what about entryComponents of entryComponents? maybe skip here and just do the
// below?
} }
}); });
localModuleMeta.entryComponents.forEach((entryComponentType) => { localModuleMeta.entryComponents.forEach((entryComponentType) => {
templates.add(this._createCompiledHostTemplate(entryComponentType.runtime)); templates.add(this._createCompiledHostTemplate(entryComponentType.runtime));
// TODO: what about entryComponents of entryComponents?
}); });
}); });
templates.forEach((template) => { templates.forEach((template) => {
@ -248,8 +222,13 @@ export class RuntimeCompiler implements Compiler {
const compiledTemplate = isHost ? this._compiledHostTemplateCache.get(compType) : const compiledTemplate = isHost ? this._compiledHostTemplateCache.get(compType) :
this._compiledTemplateCache.get(compType); this._compiledTemplateCache.get(compType);
if (!compiledTemplate) { if (!compiledTemplate) {
throw new BaseException( if (isHost) {
`Illegal state: CompiledTemplate for ${stringify(compType)} (isHost: ${isHost}) does not exist!`); throw new BaseException(
`Illegal state: Compiled view for component ${stringify(compType)} does not exist!`);
} else {
throw new BaseException(
`Component ${stringify(compType)} is not part of any NgModule or the module has not been imported into your module.`);
}
} }
return compiledTemplate; return compiledTemplate;
} }
@ -403,15 +382,6 @@ class ModuleBoundCompiler implements Compiler {
get _injector(): Injector { return this._delegate.injector; } get _injector(): Injector { return this._delegate.injector; }
compileComponentAsync<T>(compType: Type<T>, ngModule: Type<any> = null):
Promise<ComponentFactory<T>> {
return this._delegate.compileComponentAsync(compType, ngModule ? ngModule : this._ngModule);
}
compileComponentSync<T>(compType: Type<T>, ngModule: Type<any> = null): ComponentFactory<T> {
return this._delegate.compileComponentSync(compType, ngModule ? ngModule : this._ngModule);
}
compileModuleSync<T>(moduleType: Type<T>): NgModuleFactory<T> { compileModuleSync<T>(moduleType: Type<T>): NgModuleFactory<T> {
return this._delegate.compileModuleSync(moduleType); return this._delegate.compileModuleSync(moduleType);
} }

View File

@ -6,17 +6,21 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Component, ComponentMetadata, Injector} from '@angular/core'; import {Component, ComponentMetadata, Directive, Injector} from '@angular/core';
import {beforeEach, ddescribe, describe, expect, iit, inject, it} from '@angular/core/testing/testing_internal'; import {TestBed, inject} from '@angular/core/testing';
import {ViewMetadata} from '../core_private'; import {ViewMetadata} from '../core_private';
import {isBlank, stringify} from '../src/facade/lang';
import {MockDirectiveResolver} from '../testing'; import {MockDirectiveResolver} from '../testing';
export function main() { export function main() {
describe('MockDirectiveResolver', () => { describe('MockDirectiveResolver', () => {
var dirResolver: MockDirectiveResolver; var dirResolver: MockDirectiveResolver;
beforeEach(() => {
TestBed.configureTestingModule(
{declarations: [SomeDirective, SomeOtherDirective, SomeComponent]});
});
beforeEach(inject([Injector], (injector: Injector) => { beforeEach(inject([Injector], (injector: Injector) => {
dirResolver = new MockDirectiveResolver(injector); dirResolver = new MockDirectiveResolver(injector);
})); }));
@ -40,14 +44,12 @@ export function main() {
it('should fallback to the default ViewResolver when templates are not overridden', () => { it('should fallback to the default ViewResolver when templates are not overridden', () => {
var view = <ComponentMetadata>dirResolver.resolve(SomeComponent); var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.template).toEqual('template'); expect(view.template).toEqual('template');
expect(view.directives).toEqual([SomeDirective]);
}); });
it('should allow overriding the @View', () => { it('should allow overriding the @View', () => {
dirResolver.setView(SomeComponent, new ViewMetadata({template: 'overridden template'})); dirResolver.setView(SomeComponent, new ViewMetadata({template: 'overridden template'}));
var view = <ComponentMetadata>dirResolver.resolve(SomeComponent); var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.template).toEqual('overridden template'); expect(view.template).toEqual('overridden template');
expect(isBlank(view.directives)).toBe(true);
}); });
it('should allow overriding a view after it has been resolved', () => { it('should allow overriding a view after it has been resolved', () => {
@ -55,7 +57,6 @@ export function main() {
dirResolver.setView(SomeComponent, new ViewMetadata({template: 'overridden template'})); dirResolver.setView(SomeComponent, new ViewMetadata({template: 'overridden template'}));
var view = <ComponentMetadata>dirResolver.resolve(SomeComponent); var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.template).toEqual('overridden template'); expect(view.template).toEqual('overridden template');
expect(isBlank(view.directives)).toBe(true);
}); });
}); });
@ -64,7 +65,6 @@ export function main() {
dirResolver.setInlineTemplate(SomeComponent, 'overridden template'); dirResolver.setInlineTemplate(SomeComponent, 'overridden template');
var view = <ComponentMetadata>dirResolver.resolve(SomeComponent); var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.template).toEqual('overridden template'); expect(view.template).toEqual('overridden template');
expect(view.directives).toEqual([SomeDirective]);
}); });
it('should allow overriding an overridden @View', () => { it('should allow overriding an overridden @View', () => {
@ -81,50 +81,17 @@ export function main() {
expect(view.template).toEqual('overridden template'); expect(view.template).toEqual('overridden template');
}); });
}); });
describe('Directive overriding', () => {
it('should allow overriding a directive from the default view', () => {
dirResolver.overrideViewDirective(SomeComponent, SomeDirective, SomeOtherDirective);
var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.directives.length).toEqual(1);
expect(view.directives[0]).toBe(SomeOtherDirective);
});
it('should allow overriding a directive from an overridden @View', () => {
dirResolver.setView(SomeComponent, new ViewMetadata({directives: [SomeOtherDirective]}));
dirResolver.overrideViewDirective(SomeComponent, SomeOtherDirective, SomeComponent);
var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.directives.length).toEqual(1);
expect(view.directives[0]).toBe(SomeComponent);
});
it('should throw when the overridden directive is not present', () => {
dirResolver.overrideViewDirective(SomeComponent, SomeOtherDirective, SomeDirective);
expect(() => { dirResolver.resolve(SomeComponent); })
.toThrowError(
`Overriden directive ${stringify(SomeOtherDirective)} not found in the template of ${stringify(SomeComponent)}`);
});
it('should allow overriding a directive after its view has been resolved', () => {
dirResolver.resolve(SomeComponent);
dirResolver.overrideViewDirective(SomeComponent, SomeDirective, SomeOtherDirective);
var view = <ComponentMetadata>dirResolver.resolve(SomeComponent);
expect(view.directives.length).toEqual(1);
expect(view.directives[0]).toBe(SomeOtherDirective);
});
});
}); });
} }
class SomeDirective {} @Directive({selector: 'some-directive'})
class SomeDirective {
}
@Component({ @Component({selector: 'cmp', template: 'template'})
selector: 'cmp',
template: 'template',
directives: [SomeDirective],
})
class SomeComponent { class SomeComponent {
} }
class SomeOtherDirective {} @Directive({selector: 'some-other-directive'})
class SomeOtherDirective {
}

View File

@ -104,16 +104,7 @@ class SomeDirectiveWithViewChild {
c: any; c: any;
} }
class SomeDir {} @Component({selector: 'sample', template: 'some template', styles: ['some styles']})
class SomePipe {}
@Component({
selector: 'sample',
template: 'some template',
directives: [SomeDir],
pipes: [SomePipe],
styles: ['some styles']
})
class ComponentWithTemplate { class ComponentWithTemplate {
} }
@ -236,8 +227,6 @@ export function main() {
it('should read out the template related metadata from the Component metadata', () => { it('should read out the template related metadata from the Component metadata', () => {
var compMetadata = <ComponentMetadata>resolver.resolve(ComponentWithTemplate); var compMetadata = <ComponentMetadata>resolver.resolve(ComponentWithTemplate);
expect(compMetadata.template).toEqual('some template'); expect(compMetadata.template).toEqual('some template');
expect(compMetadata.directives).toEqual([SomeDir]);
expect(compMetadata.pipes).toEqual([SomePipe]);
expect(compMetadata.styles).toEqual(['some styles']); expect(compMetadata.styles).toEqual(['some styles']);
}); });
}); });

View File

@ -140,12 +140,12 @@ export function main() {
it('should throw when using a templateUrl in a nested component that has not been compiled before', it('should throw when using a templateUrl in a nested component that has not been compiled before',
() => { () => {
@NgModule({declarations: [SomeComp], entryComponents: [SomeComp]}) @NgModule({declarations: [SomeComp, ChildComp], entryComponents: [SomeComp]})
class SomeModule { class SomeModule {
} }
resourceLoader.spy('get').andCallFake(() => Promise.resolve('')); resourceLoader.spy('get').andCallFake(() => Promise.resolve(''));
dirResolver.setView(SomeComp, new ViewMetadata({template: '', directives: [ChildComp]})); dirResolver.setView(SomeComp, new ViewMetadata({template: ''}));
dirResolver.setView(ChildComp, new ViewMetadata({templateUrl: '/someTpl.html'})); dirResolver.setView(ChildComp, new ViewMetadata({templateUrl: '/someTpl.html'}));
expect(() => compiler.compileModuleSync(SomeModule)) expect(() => compiler.compileModuleSync(SomeModule))
.toThrowError( .toThrowError(

View File

@ -7,7 +7,6 @@
*/ */
export * from './testing/schema_registry_mock'; export * from './testing/schema_registry_mock';
export * from './testing/test_component_builder';
export * from './testing/directive_resolver_mock'; export * from './testing/directive_resolver_mock';
export * from './testing/ng_module_resolver_mock'; export * from './testing/ng_module_resolver_mock';
export * from './testing/pipe_resolver_mock'; export * from './testing/pipe_resolver_mock';
@ -39,13 +38,7 @@ export class TestingCompilerImpl implements TestingCompiler {
private _compiler: RuntimeCompiler, private _directiveResolver: MockDirectiveResolver, private _compiler: RuntimeCompiler, private _directiveResolver: MockDirectiveResolver,
private _pipeResolver: MockPipeResolver, private _moduleResolver: MockNgModuleResolver) {} private _pipeResolver: MockPipeResolver, private _moduleResolver: MockNgModuleResolver) {}
get injector(): Injector { return this._compiler.injector; } get injector(): Injector { return this._compiler.injector; }
compileComponentAsync<T>(component: Type<T>, ngModule: Type<any> = null):
Promise<ComponentFactory<T>> {
return this._compiler.compileComponentAsync(component, <any>ngModule);
}
compileComponentSync<T>(component: Type<T>, ngModule: Type<any> = null): ComponentFactory<T> {
return this._compiler.compileComponentSync(component, <any>ngModule);
}
compileModuleSync<T>(moduleType: Type<T>): NgModuleFactory<T> { compileModuleSync<T>(moduleType: Type<T>): NgModuleFactory<T> {
return this._compiler.compileModuleSync(moduleType); return this._compiler.compileModuleSync(moduleType);
} }

View File

@ -11,8 +11,7 @@ import {ViewMetadata} from '../core_private';
import {DirectiveResolver} from '../src/directive_resolver'; import {DirectiveResolver} from '../src/directive_resolver';
import {Map} from '../src/facade/collection'; import {Map} from '../src/facade/collection';
import {BaseException} from '../src/facade/exceptions'; import {isArray, isPresent} from '../src/facade/lang';
import {isArray, isPresent, stringify} from '../src/facade/lang';
@ -28,7 +27,6 @@ export class MockDirectiveResolver extends DirectiveResolver {
private _views = new Map<Type<any>, ViewMetadata>(); private _views = new Map<Type<any>, ViewMetadata>();
private _inlineTemplates = new Map<Type<any>, string>(); private _inlineTemplates = new Map<Type<any>, string>();
private _animations = new Map<Type<any>, AnimationEntryMetadata[]>(); private _animations = new Map<Type<any>, AnimationEntryMetadata[]>();
private _directiveOverrides = new Map<Type<any>, Map<Type<any>, Type<any>>>();
constructor(private _injector: Injector) { super(); } constructor(private _injector: Injector) { super(); }
@ -67,13 +65,8 @@ export class MockDirectiveResolver extends DirectiveResolver {
view = metadata; view = metadata;
} }
const directives: any[] = [];
if (isPresent(view.directives)) {
flattenArray(view.directives, directives);
}
let animations = view.animations; let animations = view.animations;
let templateUrl = view.templateUrl; let templateUrl = view.templateUrl;
const directiveOverrides = this._directiveOverrides.get(type);
const inlineAnimations = this._animations.get(type); const inlineAnimations = this._animations.get(type);
if (isPresent(inlineAnimations)) { if (isPresent(inlineAnimations)) {
@ -87,17 +80,6 @@ export class MockDirectiveResolver extends DirectiveResolver {
inlineTemplate = view.template; inlineTemplate = view.template;
} }
if (isPresent(directiveOverrides) && isPresent(view.directives)) {
directiveOverrides.forEach((to, from) => {
var srcIndex = directives.indexOf(from);
if (srcIndex == -1) {
throw new BaseException(
`Overriden directive ${stringify(from)} not found in the template of ${stringify(type)}`);
}
directives[srcIndex] = to;
});
}
return new ComponentMetadata({ return new ComponentMetadata({
selector: metadata.selector, selector: metadata.selector,
inputs: metadata.inputs, inputs: metadata.inputs,
@ -112,11 +94,9 @@ export class MockDirectiveResolver extends DirectiveResolver {
entryComponents: metadata.entryComponents, entryComponents: metadata.entryComponents,
template: inlineTemplate, template: inlineTemplate,
templateUrl: templateUrl, templateUrl: templateUrl,
directives: directives.length > 0 ? directives : null,
animations: animations, animations: animations,
styles: view.styles, styles: view.styles,
styleUrls: view.styleUrls, styleUrls: view.styleUrls,
pipes: view.pipes,
encapsulation: view.encapsulation, encapsulation: view.encapsulation,
interpolation: view.interpolation interpolation: view.interpolation
}); });
@ -170,21 +150,6 @@ export class MockDirectiveResolver extends DirectiveResolver {
this._animations.set(component, animations); this._animations.set(component, animations);
this._clearCacheFor(component); this._clearCacheFor(component);
} }
/**
* Overrides a directive from the component {@link ViewMetadata}.
*/
overrideViewDirective(component: Type<any>, from: Type<any>, to: Type<any>): void {
var overrides = this._directiveOverrides.get(component);
if (!overrides) {
overrides = new Map<Type<any>, Type<any>>();
this._directiveOverrides.set(component, overrides);
}
overrides.set(from, to);
this._clearCacheFor(component);
}
} }
function flattenArray(tree: any[], out: Array<Type<any>|any[]>): void { function flattenArray(tree: any[], out: Array<Type<any>|any[]>): void {

View File

@ -304,11 +304,23 @@ export class PlatformRef_ extends PlatformRef {
} }
private _bootstrapModuleWithZone<M>( private _bootstrapModuleWithZone<M>(
moduleType: Type<M>, compilerOptions: CompilerOptions|CompilerOptions[] = [], moduleType: Type<M>, compilerOptions: CompilerOptions|CompilerOptions[] = [], ngZone: NgZone,
ngZone: NgZone): Promise<NgModuleRef<M>> { componentFactoryCallback?: any): Promise<NgModuleRef<M>> {
const compilerFactory: CompilerFactory = this.injector.get(CompilerFactory); const compilerFactory: CompilerFactory = this.injector.get(CompilerFactory);
const compiler = compilerFactory.createCompiler( const compiler = compilerFactory.createCompiler(
compilerOptions instanceof Array ? compilerOptions : [compilerOptions]); compilerOptions instanceof Array ? compilerOptions : [compilerOptions]);
// ugly internal api hack: generate host component factories for all declared components and
// pass the factories into the callback - this is used by UpdateAdapter to get hold of all
// factories.
if (componentFactoryCallback) {
return compiler.compileModuleAndAllComponentsAsync(moduleType)
.then(({ngModuleFactory, componentFactories}) => {
componentFactoryCallback(componentFactories);
return this._bootstrapModuleFactoryWithZone(ngModuleFactory, ngZone);
});
}
return compiler.compileModuleAsync(moduleType) return compiler.compileModuleAsync(moduleType)
.then((moduleFactory) => this._bootstrapModuleFactoryWithZone(moduleFactory, ngZone)); .then((moduleFactory) => this._bootstrapModuleFactoryWithZone(moduleFactory, ngZone));
} }

View File

@ -45,7 +45,7 @@ function _throwError() {
} }
/** /**
* Low-level service for running the angular compiler duirng runtime * Low-level service for running the angular compiler during runtime
* to create {@link ComponentFactory}s, which * to create {@link ComponentFactory}s, which
* can later be used to create and render a Component instance. * can later be used to create and render a Component instance.
* *
@ -55,20 +55,6 @@ function _throwError() {
* @stable * @stable
*/ */
export class Compiler { export class Compiler {
/**
* Loads the template and styles of a component and returns the associated `ComponentFactory`.
*/
compileComponentAsync<T>(component: Type<T>, ngModule: Type<any> = null):
Promise<ComponentFactory<T>> {
throw _throwError();
}
/**
* Compiles the given component. All templates have to be either inline or compiled via
* `compileComponentAsync` before. Otherwise throws a {@link ComponentStillLoadingError}.
*/
compileComponentSync<T>(component: Type<T>, ngModule: Type<any> = null): ComponentFactory<T> {
throw _throwError();
}
/** /**
* Compiles the given NgModule and all of its components. All templates of the components listed * Compiles the given NgModule and all of its components. All templates of the components listed
* in `entryComponents` * in `entryComponents`

View File

@ -481,8 +481,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* selector: 'app', * selector: 'app',
* template: ` * template: `
* <bank-account bank-name="RBC" account-id="4747"></bank-account> * <bank-account bank-name="RBC" account-id="4747"></bank-account>
* `, * `
* directives: [BankAccount]
* }) * })
* class App {} * class App {}
* ``` * ```
@ -525,8 +524,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* template: ` * template: `
* <interval-dir (everySecond)="everySecond()" (everyFiveSeconds)="everyFiveSeconds()"> * <interval-dir (everySecond)="everySecond()" (everyFiveSeconds)="everyFiveSeconds()">
* </interval-dir> * </interval-dir>
* `, * `
* directives: [IntervalDir]
* }) * })
* class App { * class App {
* everySecond() { console.log('second'); } * everySecond() { console.log('second'); }
@ -578,8 +576,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* *
* @Component({ * @Component({
* selector: 'app', * selector: 'app',
* template: `<button counting>Increment</button>`, * template: `<button counting>Increment</button>`
* directives: [CountClicks]
* }) * })
* class App {} * class App {}
* ``` * ```
@ -612,8 +609,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* *
* @Component({ * @Component({
* selector: 'app', * selector: 'app',
* template: `<input [(ngModel)]="prop">`, * template: `<input [(ngModel)]="prop">`
* directives: [FORM_DIRECTIVES, NgModelStatus]
* }) * })
* class App { * class App {
* prop; * prop;
@ -690,8 +686,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* *
* @Component({ * @Component({
* selector: 'main', * selector: 'main',
* template: `<child-dir #c="child"></child-dir>`, * template: `<child-dir #c="child"></child-dir>`
* directives: [ChildDir]
* }) * })
* class MainComponent { * class MainComponent {
* } * }
@ -716,8 +711,7 @@ export class DirectiveMetadata extends InjectableMetadata implements DirectiveMe
* contentChildren: new ContentChildren(ChildDirective), * contentChildren: new ContentChildren(ChildDirective),
* viewChildren: new ViewChildren(ChildDirective) * viewChildren: new ViewChildren(ChildDirective)
* }, * },
* template: '<child-directive></child-directive>', * template: '<child-directive></child-directive>'
* directives: [ChildDirective]
* }) * })
* class SomeDir { * class SomeDir {
* contentChildren: QueryList<ChildDirective>, * contentChildren: QueryList<ChildDirective>,
@ -761,8 +755,6 @@ export interface ComponentMetadataType extends DirectiveMetadataType {
styleUrls?: string[]; styleUrls?: string[];
styles?: string[]; styles?: string[];
animations?: AnimationEntryMetadata[]; animations?: AnimationEntryMetadata[];
directives?: Array<Type<any>|any[]>;
pipes?: Array<Type<any>|any[]>;
encapsulation?: ViewEncapsulation; encapsulation?: ViewEncapsulation;
interpolation?: [string, string]; interpolation?: [string, string];
entryComponents?: Array<Type<any>|any[]>; entryComponents?: Array<Type<any>|any[]>;
@ -836,8 +828,7 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
* viewProviders: [ * viewProviders: [
* Greeter * Greeter
* ], * ],
* template: `<needs-greeter></needs-greeter>`, * template: `<needs-greeter></needs-greeter>`
* directives: [NeedsGreeter]
* }) * })
* class HelloWorld { * class HelloWorld {
* } * }
@ -969,10 +960,6 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
*/ */
animations: AnimationEntryMetadata[]; animations: AnimationEntryMetadata[];
directives: Array<Type<any>|any[]>;
pipes: Array<Type<any>|any[]>;
/** /**
* Specify how the template and the styles should be encapsulated. * Specify how the template and the styles should be encapsulated.
* The default is {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} if the view * The default is {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} if the view
@ -991,13 +978,11 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
*/ */
entryComponents: Array<Type<any>|any[]>; entryComponents: Array<Type<any>|any[]>;
constructor({selector, inputs, outputs, constructor(
host, exportAs, moduleId, {selector, inputs, outputs, host, exportAs, moduleId, providers, viewProviders,
providers, viewProviders, changeDetection = ChangeDetectionStrategy.Default, changeDetection = ChangeDetectionStrategy.Default, queries, templateUrl, template, styleUrls,
queries, templateUrl, template, styles, animations, encapsulation, interpolation,
styleUrls, styles, animations, entryComponents}: ComponentMetadataType = {}) {
directives, pipes, encapsulation,
interpolation, entryComponents}: ComponentMetadataType = {}) {
super({ super({
selector: selector, selector: selector,
inputs: inputs, inputs: inputs,
@ -1014,8 +999,6 @@ export class ComponentMetadata extends DirectiveMetadata implements ComponentMet
this.template = template; this.template = template;
this.styleUrls = styleUrls; this.styleUrls = styleUrls;
this.styles = styles; this.styles = styles;
this.directives = directives;
this.pipes = pipes;
this.encapsulation = encapsulation; this.encapsulation = encapsulation;
this.moduleId = moduleId; this.moduleId = moduleId;
this.animations = animations; this.animations = animations;
@ -1092,9 +1075,9 @@ export class PipeMetadata extends InjectableMetadata implements PipeMetadataType
* selector: 'app', * selector: 'app',
* template: ` * template: `
* <bank-account bank-name="RBC" account-id="4747"></bank-account> * <bank-account bank-name="RBC" account-id="4747"></bank-account>
* `, * `
* directives: [BankAccount]
* }) * })
*
* class App {} * class App {}
* ``` * ```
* @stable * @stable
@ -1138,8 +1121,7 @@ export class InputMetadata {
* template: ` * template: `
* <interval-dir (everySecond)="everySecond()" (everyFiveSeconds)="everyFiveSeconds()"> * <interval-dir (everySecond)="everySecond()" (everyFiveSeconds)="everyFiveSeconds()">
* </interval-dir> * </interval-dir>
* `, * `
* directives: [IntervalDir]
* }) * })
* class App { * class App {
* everySecond() { console.log('second'); } * everySecond() { console.log('second'); }
@ -1177,8 +1159,7 @@ export class OutputMetadata {
* *
* @Component({ * @Component({
* selector: 'app', * selector: 'app',
* template: `<input [(ngModel)]="prop">`, * template: `<input [(ngModel)]="prop">`
* directives: [FORM_DIRECTIVES, NgModelStatus]
* }) * })
* class App { * class App {
* prop; * prop;
@ -1216,8 +1197,7 @@ export class HostBindingMetadata {
* *
* @Component({ * @Component({
* selector: 'app', * selector: 'app',
* template: `<button counting>Increment</button>`, * template: `<button counting>Increment</button>`
* directives: [CountClicks]
* }) * })
* class App {} * class App {}
* ``` * ```

View File

@ -103,30 +103,6 @@ export class ViewMetadata {
*/ */
styles: string[]; styles: string[];
/**
* Specifies a list of directives that can be used within a template.
*
* Directives must be listed explicitly to provide proper component encapsulation.
*
* ### Example
*
* ```javascript
* @Component({
* selector: 'my-component',
* directives: [NgFor]
* template: '
* <ul>
* <li *ngFor="let item of items">{{item}}</li>
* </ul>'
* })
* class MyComponent {
* }
* ```
*/
directives: Array<Type<any>|any[]>;
pipes: Array<Type<any>|any[]>;
/** /**
* Specify how the template and the styles should be encapsulated. * Specify how the template and the styles should be encapsulated.
* The default is {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} if the view * The default is {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} if the view
@ -140,12 +116,9 @@ export class ViewMetadata {
interpolation: [string, string]; interpolation: [string, string];
constructor( constructor(
{templateUrl, template, directives, pipes, encapsulation, styles, styleUrls, animations, {templateUrl, template, encapsulation, styles, styleUrls, animations, interpolation}: {
interpolation}: {
templateUrl?: string, templateUrl?: string,
template?: string, template?: string,
directives?: Array<Type<any>|any[]>,
pipes?: Array<Type<any>|any[]>,
encapsulation?: ViewEncapsulation, encapsulation?: ViewEncapsulation,
styles?: string[], styles?: string[],
styleUrls?: string[], styleUrls?: string[],
@ -156,8 +129,6 @@ export class ViewMetadata {
this.template = template; this.template = template;
this.styleUrls = styleUrls; this.styleUrls = styleUrls;
this.styles = styles; this.styles = styles;
this.directives = directives;
this.pipes = pipes;
this.encapsulation = encapsulation; this.encapsulation = encapsulation;
this.animations = animations; this.animations = animations;
this.interpolation = interpolation; this.interpolation = interpolation;

View File

@ -8,8 +8,7 @@
import {ANALYZE_FOR_ENTRY_COMPONENTS, CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, ComponentFactoryResolver, Directive, HostBinding, Inject, Injectable, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, SelfMetadata, Type, forwardRef} from '@angular/core'; import {ANALYZE_FOR_ENTRY_COMPONENTS, CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, ComponentFactoryResolver, Directive, HostBinding, Inject, Injectable, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, SelfMetadata, Type, forwardRef} from '@angular/core';
import {Console} from '@angular/core/src/console'; import {Console} from '@angular/core/src/console';
import {ComponentFixture, TestBed} from '@angular/core/testing'; import {ComponentFixture, TestBed, inject} from '@angular/core/testing';
import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
import {expect} from '@angular/platform-browser/testing/matchers'; import {expect} from '@angular/platform-browser/testing/matchers';
import {BaseException} from '../../src/facade/exceptions'; import {BaseException} from '../../src/facade/exceptions';
@ -263,20 +262,35 @@ function declareTests({useJit}: {useJit: boolean}) {
.toBe(SomeComp); .toBe(SomeComp);
}); });
it('should throw when using an entryComponent that was neither declared nor imported', () => { it('should throw if we cannot find a module associated with a module-level entryComponent', () => {
@Component({template: '', entryComponents: [SomeComp]}) @Component({template: ''})
class SomeCompWithEntryComponents { class SomeCompWithEntryComponents {
} }
@NgModule({entryComponents: [SomeCompWithEntryComponents]}) @NgModule({declarations: [], entryComponents: [SomeCompWithEntryComponents]})
class SomeModule { class SomeModule {
} }
expect(() => createModule(SomeModule)) expect(() => createModule(SomeModule))
.toThrowError( .toThrowError(
`NgModule ${stringify(SomeModule)} uses ${stringify(SomeCompWithEntryComponents)} via "entryComponents" but it was neither declared nor imported! If ${stringify(SomeCompWithEntryComponents)} is declared in an imported module, make sure it is exported.`); 'Component SomeCompWithEntryComponents is not part of any NgModule or the module has not been imported into your module.');
}); });
it('should throw if we cannot find a module associated with a component-level entryComponent',
() => {
@Component({template: '', entryComponents: [SomeComp]})
class SomeCompWithEntryComponents {
}
@NgModule({declarations: [SomeCompWithEntryComponents]})
class SomeModule {
}
expect(() => createModule(SomeModule))
.toThrowError(
'Component SomeComp is not part of any NgModule or the module has not been imported into your module.');
});
it('should create ComponentFactories via ANALYZE_FOR_ENTRY_COMPONENTS', () => { it('should create ComponentFactories via ANALYZE_FOR_ENTRY_COMPONENTS', () => {
@NgModule({ @NgModule({
declarations: [SomeComp], declarations: [SomeComp],
@ -417,61 +431,6 @@ function declareTests({useJit}: {useJit: boolean}) {
expect(compFixture.debugElement.children[0].children[0].properties['title']) expect(compFixture.debugElement.children[0].children[0].properties['title'])
.toBe('transformed someValue'); .toBe('transformed someValue');
}); });
it('should hoist @Component.directives/pipes into the module', () => {
@Component({
selector: 'parent',
template: '<comp></comp>',
directives: [CompUsingModuleDirectiveAndPipe, SomeDirective],
pipes: [SomePipe]
})
class ParentCompUsingModuleDirectiveAndPipe {
}
@NgModule({
declarations: [ParentCompUsingModuleDirectiveAndPipe],
entryComponents: [ParentCompUsingModuleDirectiveAndPipe]
})
class SomeModule {
}
const compFixture = createComp(ParentCompUsingModuleDirectiveAndPipe, SomeModule);
compFixture.detectChanges();
expect(compFixture.debugElement.children[0].children[0].properties['title'])
.toBe('transformed someValue');
});
it('should allow to use directives/pipes via @Component.directives/pipes that were already imported from another module',
() => {
@Component({
selector: 'parent',
template: '<comp></comp>',
directives: [CompUsingModuleDirectiveAndPipe, SomeDirective],
pipes: [SomePipe]
})
class ParentCompUsingModuleDirectiveAndPipe {
}
@NgModule({
declarations: [SomeDirective, SomePipe, CompUsingModuleDirectiveAndPipe],
exports: [SomeDirective, SomePipe, CompUsingModuleDirectiveAndPipe]
})
class SomeImportedModule {
}
@NgModule({
declarations: [ParentCompUsingModuleDirectiveAndPipe],
imports: [SomeImportedModule],
entryComponents: [ParentCompUsingModuleDirectiveAndPipe]
})
class SomeModule {
}
const compFixture = createComp(ParentCompUsingModuleDirectiveAndPipe, SomeModule);
compFixture.detectChanges();
expect(compFixture.debugElement.children[0].children[0].properties['title'])
.toBe('transformed someValue');
});
}); });
describe('import/export', () => { describe('import/export', () => {
@ -603,24 +562,6 @@ function declareTests({useJit}: {useJit: boolean}) {
}); });
}); });
describe('bound compiler', () => {
it('should provide a Compiler instance that uses the directives/pipes of the module', () => {
@NgModule({declarations: [SomeDirective, SomePipe]})
class SomeModule {
}
const ngModule = createModule(SomeModule);
const boundCompiler: Compiler = ngModule.injector.get(Compiler);
const cf = boundCompiler.compileComponentSync(CompUsingModuleDirectiveAndPipe);
const compFixture = new ComponentFixture(cf.create(injector), null, false);
compFixture.detectChanges();
expect(compFixture.debugElement.children[0].properties['title'])
.toBe('transformed someValue');
// compile again should produce the same result
expect(boundCompiler.compileComponentSync(CompUsingModuleDirectiveAndPipe)).toBe(cf);
});
});
describe('providers', function() { describe('providers', function() {
let moduleType: any = null; let moduleType: any = null;

View File

@ -6,8 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {BaseException, Compiler, ComponentFactory, Injector, NgModule, NgModuleRef, NgZone, Provider, Testability, Type} from '@angular/core'; import {BaseException, Compiler, ComponentFactory, ComponentFactoryResolver, Injector, NgModule, NgModuleRef, NgZone, Provider, Testability, Type} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import * as angular from './angular_js'; import * as angular from './angular_js';
@ -117,10 +116,10 @@ export class UpgradeAdapter {
// the ng2AppModule param should be required once the deprecated @Component.directives prop is // the ng2AppModule param should be required once the deprecated @Component.directives prop is
// removed // removed
constructor(private ng2AppModule?: Type<any>) { constructor(private ng2AppModule: Type<any>) {
if (arguments.length && !ng2AppModule) { if (!ng2AppModule) {
throw new BaseException( throw new BaseException(
'UpgradeAdapter constructor called with undefined instead of a ng module type'); 'UpgradeAdapter cannot be instantiated without an NgModule of the Angular 2 app.');
} }
} }
@ -394,10 +393,21 @@ export class UpgradeAdapter {
{provide: NG1_COMPILE, useFactory: () => ng1Injector.get(NG1_COMPILE)}, {provide: NG1_COMPILE, useFactory: () => ng1Injector.get(NG1_COMPILE)},
this.providers this.providers
], ],
imports: this.ng2AppModule ? [this.ng2AppModule] : [BrowserModule] imports: [this.ng2AppModule]
}).Class({constructor: function() {}, ngDoBootstrap: function() {}}); }).Class({
constructor: function DynamicNgUpgradeModule() {},
ngDoBootstrap: function() {}
});
(platformBrowserDynamic() as any) (platformBrowserDynamic() as any)
._bootstrapModuleWithZone(DynamicNgUpgradeModule, undefined, ngZone) ._bootstrapModuleWithZone(
DynamicNgUpgradeModule, undefined, ngZone,
(componentFactories: ComponentFactory<any>[]) => {
componentFactories.forEach((componentFactory) => {
componentFactoryRefMap[getComponentInfo(componentFactory.componentType)
.selector] = componentFactory;
});
})
.then((ref: NgModuleRef<any>) => { .then((ref: NgModuleRef<any>) => {
moduleRef = ref; moduleRef = ref;
angular.element(element).data( angular.element(element).data(
@ -417,7 +427,7 @@ export class UpgradeAdapter {
windowAngular.resumeBootstrap = undefined; windowAngular.resumeBootstrap = undefined;
ngZone.run(() => { angular.bootstrap(element, [this.idPrefix], config); }); ngZone.run(() => { angular.bootstrap(element, [this.idPrefix], config); });
ng1BootstrapPromise = new Promise((resolve, reject) => { ng1BootstrapPromise = new Promise((resolve) => {
if (windowAngular.resumeBootstrap) { if (windowAngular.resumeBootstrap) {
var originalResumeBootstrap: () => void = windowAngular.resumeBootstrap; var originalResumeBootstrap: () => void = windowAngular.resumeBootstrap;
windowAngular.resumeBootstrap = function() { windowAngular.resumeBootstrap = function() {
@ -430,23 +440,18 @@ export class UpgradeAdapter {
} }
}); });
Promise.all([ng1BootstrapPromise, ng1compilePromise]) Promise.all([ng1BootstrapPromise, ng1compilePromise]).then(() => {
.then(() => { moduleRef.injector.get(NgZone).run(() => {
return this.compileNg2Components( if (rootScopePrototype) {
moduleRef.injector.get(Compiler), componentFactoryRefMap); rootScopePrototype.$apply = original$applyFn; // restore original $apply
}) while (delayApplyExps.length) {
.then(() => { rootScope.$apply(delayApplyExps.shift());
moduleRef.injector.get(NgZone).run(() => { }
if (rootScopePrototype) { (<any>upgrade)._bootstrapDone(moduleRef, ng1Injector);
rootScopePrototype.$apply = original$applyFn; // restore original $apply rootScopePrototype = null;
while (delayApplyExps.length) { }
rootScope.$apply(delayApplyExps.shift()); });
} }, onError);
(<any>upgrade)._bootstrapDone(moduleRef, ng1Injector);
rootScopePrototype = null;
}
});
}, onError);
return upgrade; return upgrade;
} }
@ -518,23 +523,6 @@ export class UpgradeAdapter {
(<any>factory).$inject = [NG2_INJECTOR]; (<any>factory).$inject = [NG2_INJECTOR];
return factory; return factory;
} }
/* @internal */
private compileNg2Components(compiler: Compiler, componentFactoryRefMap: ComponentFactoryRefMap):
Promise<ComponentFactoryRefMap> {
var promises: Array<Promise<ComponentFactory<any>>> = [];
var types = this.upgradedComponents;
for (var i = 0; i < types.length; i++) {
promises.push(compiler.compileComponentAsync(<any>types[i], this.ng2AppModule));
}
return Promise.all(promises).then((componentFactories: Array<ComponentFactory<any>>) => {
var types = this.upgradedComponents;
for (var i = 0; i < componentFactories.length; i++) {
componentFactoryRefMap[getComponentInfo(types[i]).selector] = componentFactories[i];
}
return componentFactoryRefMap;
}, onError);
}
} }
interface ComponentFactoryRefMap { interface ComponentFactoryRefMap {

View File

@ -6,9 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Class, Component, EventEmitter, Inject, NgModule, OpaqueToken, Testability, destroyPlatform} from '@angular/core'; import {Class, Component, EventEmitter, Inject, NgModule, OpaqueToken, Testability, destroyPlatform, forwardRef} from '@angular/core';
import {async} from '@angular/core/testing'; import {async} from '@angular/core/testing';
import {AsyncTestCompleter, ddescribe, describe, expect, iit, inject, it} from '@angular/core/testing/testing_internal';
import {BrowserModule} from '@angular/platform-browser'; import {BrowserModule} from '@angular/platform-browser';
import {UpgradeAdapter} from '@angular/upgrade'; import {UpgradeAdapter} from '@angular/upgrade';
import * as angular from '@angular/upgrade/src/angular_js'; import * as angular from '@angular/upgrade/src/angular_js';
@ -20,34 +19,40 @@ export function main() {
it('should have angular 1 loaded', () => expect(angular.version.major).toBe(1)); it('should have angular 1 loaded', () => expect(angular.version.major).toBe(1));
it('should instantiate ng2 in ng1 template and project content', it('should instantiate ng2 in ng1 template and project content', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var Ng2 = Component({selector: 'ng2', template: `{{ 'NG2' }}(<ng-content></ng-content>)`}) var Ng2 = Component({selector: 'ng2', template: `{{ 'NG2' }}(<ng-content></ng-content>)`})
.Class({constructor: function() {}}); .Class({constructor: function() {}});
var Ng2Module = NgModule({declarations: [Ng2], imports: [BrowserModule]}).Class({
constructor: function() {}
});
var element = var element =
html('<div>{{ \'ng1[\' }}<ng2>~{{ \'ng-content\' }}~</ng2>{{ \']\' }}</div>'); html('<div>{{ \'ng1[\' }}<ng2>~{{ \'ng-content\' }}~</ng2>{{ \']\' }}</div>');
var adapter: UpgradeAdapter = new UpgradeAdapter(); const adapter: UpgradeAdapter = new UpgradeAdapter(Ng2Module);
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(document.body.textContent).toEqual('ng1[NG2(~ng-content~)]'); expect(document.body.textContent).toEqual('ng1[NG2(~ng-content~)]');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
it('should instantiate ng1 in ng2 template and project content', it('should instantiate ng1 in ng2 template and project content', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter: UpgradeAdapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var Ng2 = Component({ var Ng2 = Component({
selector: 'ng2', selector: 'ng2',
template: `{{ 'ng2(' }}<ng1>{{'transclude'}}</ng1>{{ ')' }}`, template: `{{ 'ng2(' }}<ng1>{{'transclude'}}</ng1>{{ ')' }}`,
directives: [adapter.upgradeNg1Component('ng1')] }).Class({constructor: function Ng2() {}});
}).Class({constructor: function() {}});
var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function Ng2Module() {}});
ng1Module.directive('ng1', () => { ng1Module.directive('ng1', () => {
return {transclude: true, template: '{{ "ng1" }}(<ng-transclude></ng-transclude>)'}; return {transclude: true, template: '{{ "ng1" }}(<ng-transclude></ng-transclude>)'};
@ -59,19 +64,17 @@ export function main() {
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(document.body.textContent).toEqual('ng1(ng2(ng1(transclude)))'); expect(document.body.textContent).toEqual('ng1(ng2(ng1(transclude)))');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
describe('scope/component change-detection', () => { describe('scope/component change-detection', () => {
it('should interleave scope and component expressions', it('should interleave scope and component expressions', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var log: any[] /** TODO #9100 */ = []; var log: any[] /** TODO #9100 */ = [];
var l = function(value: any /** TODO #9100 */) { var l = function(value: any /** TODO #9100 */) {
log.push(value); log.push(value);
return value + ';'; return value + ';';
}; };
var adapter: UpgradeAdapter = new UpgradeAdapter(); const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
ng1Module.directive('ng1a', () => { return {template: '{{ l(\'ng1a\') }}'}; }); ng1Module.directive('ng1a', () => { return {template: '{{ l(\'ng1a\') }}'}; });
ng1Module.directive('ng1b', () => { return {template: '{{ l(\'ng1b\') }}'}; }); ng1Module.directive('ng1b', () => { return {template: '{{ l(\'ng1b\') }}'}; });
@ -80,13 +83,18 @@ export function main() {
$rootScope.reset = () => log.length = 0; $rootScope.reset = () => log.length = 0;
}); });
var Ng2 = var Ng2 = Component({
Component({ selector: 'ng2',
selector: 'ng2', template: `{{l('2A')}}<ng1a></ng1a>{{l('2B')}}<ng1b></ng1b>{{l('2C')}}`
template: `{{l('2A')}}<ng1a></ng1a>{{l('2B')}}<ng1b></ng1b>{{l('2C')}}`, }).Class({constructor: function() { this.l = l; }});
directives:
[adapter.upgradeNg1Component('ng1a'), adapter.upgradeNg1Component('ng1b')] var Ng2Module =
}).Class({constructor: function() { this.l = l; }}); NgModule({
declarations: [
adapter.upgradeNg1Component('ng1a'), adapter.upgradeNg1Component('ng1b'), Ng2
],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
@ -97,15 +105,13 @@ export function main() {
// https://github.com/angular/angular.js/issues/12983 // https://github.com/angular/angular.js/issues/12983
expect(log).toEqual(['1A', '1B', '1C', '2A', '2B', '2C', 'ng1a', 'ng1b']); expect(log).toEqual(['1A', '1B', '1C', '2A', '2B', '2C', 'ng1a', 'ng1b']);
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
}); });
describe('downgrade ng2 component', () => { describe('downgrade ng2 component', () => {
it('should bind properties, events', it('should bind properties, events', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter: UpgradeAdapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
ng1Module.run(($rootScope: any /** TODO #9100 */) => { ng1Module.run(($rootScope: any /** TODO #9100 */) => {
@ -189,6 +195,11 @@ export function main() {
} }
}); });
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var Ng2Module = NgModule({declarations: [Ng2], imports: [BrowserModule]}).Class({
constructor: function() {}
});
var element = html(`<div> var element = html(`<div>
<ng2 literal="Text" interpolate="Hello {{'world'}}" <ng2 literal="Text" interpolate="Hello {{'world'}}"
bind-one-way-a="dataA" [one-way-b]="dataB" bind-one-way-a="dataA" [one-way-b]="dataB"
@ -204,14 +215,12 @@ export function main() {
'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (2) | ' + 'oneWayA: A; oneWayB: B; twoWayA: newA; twoWayB: newB; (2) | ' +
'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;'); 'modelA: newA; modelB: newB; eventA: aFired; eventB: bFired;');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
it('should properly run cleanup when ng1 directive is destroyed', it('should properly run cleanup when ng1 directive is destroyed', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter: UpgradeAdapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var onDestroyed: EventEmitter<string> = new EventEmitter<string>(); var onDestroyed: EventEmitter<string> = new EventEmitter<string>();
@ -229,20 +238,21 @@ export function main() {
constructor: function() {}, constructor: function() {},
ngOnDestroy: function() { onDestroyed.emit('destroyed'); } ngOnDestroy: function() { onDestroyed.emit('destroyed'); }
}); });
var Ng2Module = NgModule({declarations: [Ng2], imports: [BrowserModule]}).Class({
constructor: function() {}
});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html('<ng1></ng1>'); var element = html('<ng1></ng1>');
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
onDestroyed.subscribe(() => { onDestroyed.subscribe(() => { ref.dispose(); });
ref.dispose();
async.done();
});
}); });
})); }));
it('should fallback to the root ng2.injector when compiled outside the dom', it('should fallback to the root ng2.injector when compiled outside the dom', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter: UpgradeAdapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
ng1Module.directive('ng1', [ ng1Module.directive('ng1', [
@ -262,12 +272,16 @@ export function main() {
var Ng2 = var Ng2 =
Component({selector: 'ng2', template: 'test'}).Class({constructor: function() {}}); Component({selector: 'ng2', template: 'test'}).Class({constructor: function() {}});
var Ng2Module = NgModule({declarations: [Ng2], imports: [BrowserModule]}).Class({
constructor: function() {}
});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html('<ng1></ng1>'); var element = html('<ng1></ng1>');
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(multiTrim(document.body.textContent)).toEqual('test'); expect(multiTrim(document.body.textContent)).toEqual('test');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
@ -308,9 +322,8 @@ export function main() {
}); });
describe('upgrade ng1 component', () => { describe('upgrade ng1 component', () => {
it('should bind properties, events', it('should bind properties, events', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var ng1 = function() { var ng1 = function() {
@ -338,8 +351,7 @@ export function main() {
'<ng1 fullName="{{last}}, {{first}}" [modelA]="first" [(modelB)]="last" ' + '<ng1 fullName="{{last}}, {{first}}" [modelA]="first" [(modelB)]="last" ' +
'(event)="event=$event"></ng1>' + '(event)="event=$event"></ng1>' +
'<ng1 fullName="{{\'TEST\'}}" modelA="First" modelB="Last"></ng1>' + '<ng1 fullName="{{\'TEST\'}}" modelA="First" modelB="Last"></ng1>' +
'{{event}}-{{last}}, {{first}}', '{{event}}-{{last}}, {{first}}'
directives: [adapter.upgradeNg1Component('ng1')]
}).Class({ }).Class({
constructor: function() { constructor: function() {
this.first = 'Victor'; this.first = 'Victor';
@ -347,6 +359,12 @@ export function main() {
this.event = '?'; this.event = '?';
} }
}); });
var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><ng2></ng2></div>`); var element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
@ -357,14 +375,13 @@ export function main() {
.toEqual( .toEqual(
'Hello SAVKIN, Victor; A: VICTOR; B: SAVKIN; | Hello TEST; A: First; B: Last; | WORKS-SAVKIN, Victor'); 'Hello SAVKIN, Victor; A: VICTOR; B: SAVKIN; | Hello TEST; A: First; B: Last; | WORKS-SAVKIN, Victor');
ref.dispose(); ref.dispose();
async.done();
}, 0); }, 0);
}); });
})); }));
it('should bind properties, events in controller when bindToController is not used', it('should bind properties, events in controller when bindToController is not used',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { async(() => {
var adapter = new UpgradeAdapter(); const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var ng1 = function() { var ng1 = function() {
@ -383,8 +400,7 @@ export function main() {
Component({ Component({
selector: 'ng2', selector: 'ng2',
template: template:
'{{someText}} - Length: {{dataList.length}} | <ng1 [(data)]="dataList"></ng1>', '{{someText}} - Length: {{dataList.length}} | <ng1 [(data)]="dataList"></ng1>'
directives: [adapter.upgradeNg1Component('ng1')],
}).Class({ }).Class({
constructor: function() { constructor: function() {
@ -392,6 +408,12 @@ export function main() {
this.someText = 'ng2'; this.someText = 'ng2';
} }
}); });
var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><ng2></ng2></div>`); var element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
@ -401,14 +423,12 @@ export function main() {
expect(multiTrim(document.body.textContent)) expect(multiTrim(document.body.textContent))
.toEqual('ng2 - Length: 3 | ng1 - Data: 1,2,3 - Length: 3'); .toEqual('ng2 - Length: 3 | ng1 - Data: 1,2,3 - Length: 3');
ref.dispose(); ref.dispose();
async.done();
}, 0); }, 0);
}); });
})); }));
it('should bind properties, events in link function', it('should bind properties, events in link function', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var ng1 = function() { var ng1 = function() {
@ -427,8 +447,7 @@ export function main() {
Component({ Component({
selector: 'ng2', selector: 'ng2',
template: template:
'{{someText}} - Length: {{dataList.length}} | <ng1 [(data)]="dataList"></ng1>', '{{someText}} - Length: {{dataList.length}} | <ng1 [(data)]="dataList"></ng1>'
directives: [adapter.upgradeNg1Component('ng1')],
}).Class({ }).Class({
constructor: function() { constructor: function() {
@ -436,6 +455,12 @@ export function main() {
this.someText = 'ng2'; this.someText = 'ng2';
} }
}); });
var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><ng2></ng2></div>`); var element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
@ -445,14 +470,12 @@ export function main() {
expect(multiTrim(document.body.textContent)) expect(multiTrim(document.body.textContent))
.toEqual('ng2 - Length: 3 | ng1 - Data: 1,2,3 - Length: 3'); .toEqual('ng2 - Length: 3 | ng1 - Data: 1,2,3 - Length: 3');
ref.dispose(); ref.dispose();
async.done();
}, 0); }, 0);
}); });
})); }));
it('should support templateUrl fetched from $httpBackend', it('should support templateUrl fetched from $httpBackend', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
ng1Module.value( ng1Module.value(
'$httpBackend', (method: any /** TODO #9100 */, url: any /** TODO #9100 */, '$httpBackend', (method: any /** TODO #9100 */, url: any /** TODO #9100 */,
@ -461,23 +484,25 @@ export function main() {
var ng1 = function() { return {templateUrl: 'url.html'}; }; var ng1 = function() { return {templateUrl: 'url.html'}; };
ng1Module.directive('ng1', ng1); ng1Module.directive('ng1', ng1);
var Ng2 = Component({ var Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
selector: 'ng2', constructor: function() {}
template: '<ng1></ng1>', });
directives: [adapter.upgradeNg1Component('ng1')]
}).Class({constructor: function() {}}); var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><ng2></ng2></div>`); var element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(multiTrim(document.body.textContent)).toEqual('GET:url.html'); expect(multiTrim(document.body.textContent)).toEqual('GET:url.html');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
it('should support templateUrl as a function', it('should support templateUrl as a function', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
ng1Module.value( ng1Module.value(
'$httpBackend', (method: any /** TODO #9100 */, url: any /** TODO #9100 */, '$httpBackend', (method: any /** TODO #9100 */, url: any /** TODO #9100 */,
@ -486,88 +511,100 @@ export function main() {
var ng1 = function() { return {templateUrl() { return 'url.html'; }}; }; var ng1 = function() { return {templateUrl() { return 'url.html'; }}; };
ng1Module.directive('ng1', ng1); ng1Module.directive('ng1', ng1);
var Ng2 = Component({
selector: 'ng2', var Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
template: '<ng1></ng1>', constructor: function() {}
directives: [adapter.upgradeNg1Component('ng1')] });
}).Class({constructor: function() {}});
var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><ng2></ng2></div>`); var element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(multiTrim(document.body.textContent)).toEqual('GET:url.html'); expect(multiTrim(document.body.textContent)).toEqual('GET:url.html');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
it('should support empty template', it('should support empty template', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var ng1 = function() { return {template: ''}; }; var ng1 = function() { return {template: ''}; };
ng1Module.directive('ng1', ng1); ng1Module.directive('ng1', ng1);
var Ng2 = Component({
selector: 'ng2', var Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
template: '<ng1></ng1>', constructor: function() {}
directives: [adapter.upgradeNg1Component('ng1')] });
}).Class({constructor: function() {}});
var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><ng2></ng2></div>`); var element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(multiTrim(document.body.textContent)).toEqual(''); expect(multiTrim(document.body.textContent)).toEqual('');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
it('should support template as a function', it('should support template as a function', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var ng1 = function() { return {template() { return ''; }}; }; var ng1 = function() { return {template() { return ''; }}; };
ng1Module.directive('ng1', ng1); ng1Module.directive('ng1', ng1);
var Ng2 = Component({
selector: 'ng2', var Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
template: '<ng1></ng1>', constructor: function() {}
directives: [adapter.upgradeNg1Component('ng1')] });
}).Class({constructor: function() {}});
var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><ng2></ng2></div>`); var element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(multiTrim(document.body.textContent)).toEqual(''); expect(multiTrim(document.body.textContent)).toEqual('');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
it('should support templateUrl fetched from $templateCache', it('should support templateUrl fetched from $templateCache', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
ng1Module.run( ng1Module.run(
($templateCache: any /** TODO #9100 */) => $templateCache.put('url.html', 'WORKS')); ($templateCache: any /** TODO #9100 */) => $templateCache.put('url.html', 'WORKS'));
var ng1 = function() { return {templateUrl: 'url.html'}; }; var ng1 = function() { return {templateUrl: 'url.html'}; };
ng1Module.directive('ng1', ng1); ng1Module.directive('ng1', ng1);
var Ng2 = Component({
selector: 'ng2', var Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
template: '<ng1></ng1>', constructor: function() {}
directives: [adapter.upgradeNg1Component('ng1')] });
}).Class({constructor: function() {}});
var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><ng2></ng2></div>`); var element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(multiTrim(document.body.textContent)).toEqual('WORKS'); expect(multiTrim(document.body.textContent)).toEqual('WORKS');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
it('should support controller with controllerAs', it('should support controller with controllerAs', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var ng1 = function() { var ng1 = function() {
@ -592,23 +629,26 @@ export function main() {
}; };
}; };
ng1Module.directive('ng1', ng1); ng1Module.directive('ng1', ng1);
var Ng2 = Component({
selector: 'ng2', var Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
template: '<ng1></ng1>', constructor: function() {}
directives: [adapter.upgradeNg1Component('ng1')] });
}).Class({constructor: function() {}});
var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><ng2></ng2></div>`); var element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(multiTrim(document.body.textContent)).toEqual('scope; isClass; NG1; published'); expect(multiTrim(document.body.textContent)).toEqual('scope; isClass; NG1; published');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
it('should support bindToController', it('should support bindToController', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var ng1 = function() { var ng1 = function() {
@ -621,23 +661,26 @@ export function main() {
}; };
}; };
ng1Module.directive('ng1', ng1); ng1Module.directive('ng1', ng1);
var Ng2 = Component({
selector: 'ng2', var Ng2 = Component({selector: 'ng2', template: '<ng1 title="WORKS"></ng1>'}).Class({
template: '<ng1 title="WORKS"></ng1>', constructor: function() {}
directives: [adapter.upgradeNg1Component('ng1')] });
}).Class({constructor: function() {}});
var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><ng2></ng2></div>`); var element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(multiTrim(document.body.textContent)).toEqual('WORKS'); expect(multiTrim(document.body.textContent)).toEqual('WORKS');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
it('should support bindToController with bindings', it('should support bindToController with bindings', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var ng1 = function() { var ng1 = function() {
@ -650,23 +693,26 @@ export function main() {
}; };
}; };
ng1Module.directive('ng1', ng1); ng1Module.directive('ng1', ng1);
var Ng2 = Component({
selector: 'ng2', var Ng2 = Component({selector: 'ng2', template: '<ng1 title="WORKS"></ng1>'}).Class({
template: '<ng1 title="WORKS"></ng1>', constructor: function() {}
directives: [adapter.upgradeNg1Component('ng1')] });
}).Class({constructor: function() {}});
var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><ng2></ng2></div>`); var element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(multiTrim(document.body.textContent)).toEqual('WORKS'); expect(multiTrim(document.body.textContent)).toEqual('WORKS');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
it('should support single require in linking fn', it('should support single require in linking fn', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var ng1 = function($rootScope: any /** TODO #9100 */) { var ng1 = function($rootScope: any /** TODO #9100 */) {
@ -688,23 +734,26 @@ export function main() {
}; };
}; };
ng1Module.directive('ng1', ng1); ng1Module.directive('ng1', ng1);
var Ng2 = Component({
selector: 'ng2', var Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
template: '<ng1></ng1>', constructor: function() {}
directives: [adapter.upgradeNg1Component('ng1')] });
}).Class({constructor: function() {}});
var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><ng2></ng2></div>`); var element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(multiTrim(document.body.textContent)).toEqual('WORKS'); expect(multiTrim(document.body.textContent)).toEqual('WORKS');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
it('should support array require in linking fn', it('should support array require in linking fn', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var parent = function() { var parent = function() {
@ -731,23 +780,26 @@ export function main() {
}; };
ng1Module.directive('parent', parent); ng1Module.directive('parent', parent);
ng1Module.directive('ng1', ng1); ng1Module.directive('ng1', ng1);
var Ng2 = Component({
selector: 'ng2', var Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
template: '<ng1></ng1>', constructor: function() {}
directives: [adapter.upgradeNg1Component('ng1')] });
}).Class({constructor: function() {}});
var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><parent><ng2></ng2></parent></div>`); var element = html(`<div><parent><ng2></ng2></parent></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(multiTrim(document.body.textContent)).toEqual('PARENT:WORKS'); expect(multiTrim(document.body.textContent)).toEqual('PARENT:WORKS');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
it('should call $onInit of components', it('should call $onInit of components', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var valueToFind = '$onInit'; var valueToFind = '$onInit';
@ -759,24 +811,26 @@ export function main() {
}; };
ng1Module.component('ng1', ng1); ng1Module.component('ng1', ng1);
var Ng2 = Component({ var Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
selector: 'ng2', constructor: function() {}
template: '<ng1></ng1>', });
directives: [adapter.upgradeNg1Component('ng1')]
}).Class({constructor: function() {}}); var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><ng2></ng2></div>`); var element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(multiTrim(document.body.textContent)).toEqual(valueToFind); expect(multiTrim(document.body.textContent)).toEqual(valueToFind);
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
it('should bind input properties (<) of components', it('should bind input properties (<) of components', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var ng1 = { var ng1 = {
@ -786,26 +840,27 @@ export function main() {
}; };
ng1Module.component('ng1', ng1); ng1Module.component('ng1', ng1);
var Ng2 = Component({ var Ng2 =
selector: 'ng2', Component({selector: 'ng2', template: '<ng1 [personProfile]="goku"></ng1>'}).Class({
template: '<ng1 [personProfile]="goku"></ng1>', constructor: function() { this.goku = {firstName: 'GOKU', lastName: 'SAN'}; }
directives: [adapter.upgradeNg1Component('ng1')] });
}).Class({
constructor: function() { this.goku = {firstName: 'GOKU', lastName: 'SAN'}; } var Ng2Module = NgModule({
}); declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2)); ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
var element = html(`<div><ng2></ng2></div>`); var element = html(`<div><ng2></ng2></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(multiTrim(document.body.textContent)).toEqual(`Hello GOKU SAN`); expect(multiTrim(document.body.textContent)).toEqual(`Hello GOKU SAN`);
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
it('should support ng2 > ng1 > ng2', it('should support ng2 > ng1 > ng2', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var ng1 = { var ng1 = {
@ -813,22 +868,23 @@ export function main() {
}; };
ng1Module.component('ng1', ng1); ng1Module.component('ng1', ng1);
var Ng2a = Component({ var Ng2a = Component({selector: 'ng2a', template: 'ng2a(<ng1></ng1>)'}).Class({
selector: 'ng2a',
template: 'ng2a(<ng1></ng1>)',
directives: [adapter.upgradeNg1Component('ng1')]
}).Class({constructor: function() {}});
ng1Module.directive('ng2a', adapter.downgradeNg2Component(Ng2a));
var Ng2b = Component({selector: 'ng2b', template: 'ng2b', directives: []}).Class({
constructor: function() {} constructor: function() {}
}); });
ng1Module.directive('ng2a', adapter.downgradeNg2Component(Ng2a));
var Ng2b =
Component({selector: 'ng2b', template: 'ng2b'}).Class({constructor: function() {}});
ng1Module.directive('ng2b', adapter.downgradeNg2Component(Ng2b)); ng1Module.directive('ng2b', adapter.downgradeNg2Component(Ng2b));
var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2a, Ng2b],
imports: [BrowserModule]
}).Class({constructor: function() {}});
var element = html(`<div><ng2a></ng2a></div>`); var element = html(`<div><ng2a></ng2a></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(multiTrim(document.body.textContent)).toEqual('ng2a(ng1(ng2b))'); expect(multiTrim(document.body.textContent)).toEqual('ng2a(ng1(ng2b))');
async.done();
}); });
})); }));
}); });
@ -837,12 +893,12 @@ export function main() {
function SomeToken() {} function SomeToken() {}
it('should export ng2 instance to ng1', async(() => { it('should export ng2 instance to ng1', async(() => {
var MyModule = NgModule({ var MyNg2Module = NgModule({
providers: [{provide: SomeToken, useValue: 'correct_value'}], providers: [{provide: SomeToken, useValue: 'correct_value'}],
imports: [BrowserModule] imports: [BrowserModule]
}).Class({constructor: function() {}}); }).Class({constructor: function() {}});
var adapter = new UpgradeAdapter(MyModule); const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module);
var module = angular.module('myExample', []); var module = angular.module('myExample', []);
module.factory('someToken', adapter.downgradeNg2Provider(SomeToken)); module.factory('someToken', adapter.downgradeNg2Provider(SomeToken));
adapter.bootstrap(html('<div>'), ['myExample']).ready((ref) => { adapter.bootstrap(html('<div>'), ['myExample']).ready((ref) => {
@ -851,9 +907,11 @@ export function main() {
}); });
})); }));
it('should export ng1 instance to ng2', it('should export ng1 instance to ng2', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { var MyNg2Module =
var adapter = new UpgradeAdapter(); NgModule({imports: [BrowserModule]}).Class({constructor: function() {}});
const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module);
var module = angular.module('myExample', []); var module = angular.module('myExample', []);
module.value('testValue', 'secreteToken'); module.value('testValue', 'secreteToken');
adapter.upgradeNg1Provider('testValue'); adapter.upgradeNg1Provider('testValue');
@ -864,15 +922,16 @@ export function main() {
expect(ref.ng2Injector.get(String)).toBe('secreteToken'); expect(ref.ng2Injector.get(String)).toBe('secreteToken');
expect(ref.ng2Injector.get('testToken')).toBe('secreteToken'); expect(ref.ng2Injector.get('testToken')).toBe('secreteToken');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
}); });
describe('testability', () => { describe('testability', () => {
it('should handle deferred bootstrap', it('should handle deferred bootstrap', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { var MyNg2Module =
var adapter: UpgradeAdapter = new UpgradeAdapter(); NgModule({imports: [BrowserModule]}).Class({constructor: function() {}});
const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module);
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var bootstrapResumed: boolean = false; var bootstrapResumed: boolean = false;
@ -882,7 +941,6 @@ export function main() {
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
expect(bootstrapResumed).toEqual(true); expect(bootstrapResumed).toEqual(true);
ref.dispose(); ref.dispose();
async.done();
}); });
setTimeout(() => { setTimeout(() => {
@ -891,9 +949,11 @@ export function main() {
}, 100); }, 100);
})); }));
it('should wait for ng2 testability', it('should wait for ng2 testability', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { var MyNg2Module =
var adapter: UpgradeAdapter = new UpgradeAdapter(); NgModule({imports: [BrowserModule]}).Class({constructor: function() {}});
const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module);
var ng1Module = angular.module('ng1', []); var ng1Module = angular.module('ng1', []);
var element = html('<div></div>'); var element = html('<div></div>');
adapter.bootstrap(element, ['ng1']).ready((ref) => { adapter.bootstrap(element, ['ng1']).ready((ref) => {
@ -904,7 +964,6 @@ export function main() {
angular.getTestability(element).whenStable(function() { angular.getTestability(element).whenStable(function() {
expect(ng2Stable).toEqual(true); expect(ng2Stable).toEqual(true);
ref.dispose(); ref.dispose();
async.done();
}); });
setTimeout(() => { setTimeout(() => {
@ -916,9 +975,8 @@ export function main() {
}); });
describe('examples', () => { describe('examples', () => {
it('should verify UpgradeAdapter example', it('should verify UpgradeAdapter example', async(() => {
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
var adapter = new UpgradeAdapter();
var module = angular.module('myExample', []); var module = angular.module('myExample', []);
module.directive('ng1', function() { module.directive('ng1', function() {
@ -929,15 +987,18 @@ export function main() {
}; };
}); });
var Ng2 = var Ng2 =
Component({ Component({
selector: 'ng2', selector: 'ng2',
inputs: ['name'], inputs: ['name'],
template: 'ng2[<ng1 [title]="name">transclude</ng1>](<ng-content></ng-content>)', template: 'ng2[<ng1 [title]="name">transclude</ng1>](<ng-content></ng-content>)'
directives: [adapter.upgradeNg1Component('ng1')]
}).Class({constructor: function() {}}); }).Class({constructor: function() {}});
var Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule]
}).Class({constructor: function() {}});
module.directive('ng2', adapter.downgradeNg2Component(Ng2)); module.directive('ng2', adapter.downgradeNg2Component(Ng2));
document.body.innerHTML = '<ng2 name="World">project</ng2>'; document.body.innerHTML = '<ng2 name="World">project</ng2>';
@ -946,7 +1007,6 @@ export function main() {
expect(multiTrim(document.body.textContent)) expect(multiTrim(document.body.textContent))
.toEqual('ng2[ng1[Hello World!](transclude)](project)'); .toEqual('ng2[ng1[Hello World!](transclude)](project)');
ref.dispose(); ref.dispose();
async.done();
}); });
})); }));
}); });

View File

@ -227,8 +227,6 @@ export declare class CollectionChangeRecord {
export declare class Compiler { export declare class Compiler {
clearCache(): void; clearCache(): void;
clearCacheFor(type: Type<any>): void; clearCacheFor(type: Type<any>): void;
compileComponentAsync<T>(component: Type<T>, ngModule?: Type<any>): Promise<ComponentFactory<T>>;
compileComponentSync<T>(component: Type<T>, ngModule?: Type<any>): ComponentFactory<T>;
compileModuleAndAllComponentsAsync<T>(moduleType: Type<T>): Promise<ModuleWithComponentFactories<T>>; compileModuleAndAllComponentsAsync<T>(moduleType: Type<T>): Promise<ModuleWithComponentFactories<T>>;
compileModuleAndAllComponentsSync<T>(moduleType: Type<T>): ModuleWithComponentFactories<T>; compileModuleAndAllComponentsSync<T>(moduleType: Type<T>): ModuleWithComponentFactories<T>;
compileModuleAsync<T>(moduleType: Type<T>): Promise<NgModuleFactory<T>>; compileModuleAsync<T>(moduleType: Type<T>): Promise<NgModuleFactory<T>>;
@ -276,18 +274,16 @@ export declare abstract class ComponentFactoryResolver {
export declare class ComponentMetadata extends DirectiveMetadata implements ComponentMetadataType { export declare class ComponentMetadata extends DirectiveMetadata implements ComponentMetadataType {
animations: AnimationEntryMetadata[]; animations: AnimationEntryMetadata[];
changeDetection: ChangeDetectionStrategy; changeDetection: ChangeDetectionStrategy;
directives: Array<Type<any> | any[]>;
encapsulation: ViewEncapsulation; encapsulation: ViewEncapsulation;
entryComponents: Array<Type<any> | any[]>; entryComponents: Array<Type<any> | any[]>;
interpolation: [string, string]; interpolation: [string, string];
moduleId: string; moduleId: string;
pipes: Array<Type<any> | any[]>;
styleUrls: string[]; styleUrls: string[];
styles: string[]; styles: string[];
template: string; template: string;
templateUrl: string; templateUrl: string;
viewProviders: any[]; viewProviders: any[];
constructor({selector, inputs, outputs, host, exportAs, moduleId, providers, viewProviders, changeDetection, queries, templateUrl, template, styleUrls, styles, animations, directives, pipes, encapsulation, interpolation, entryComponents}?: ComponentMetadataType); constructor({selector, inputs, outputs, host, exportAs, moduleId, providers, viewProviders, changeDetection, queries, templateUrl, template, styleUrls, styles, animations, encapsulation, interpolation, entryComponents}?: ComponentMetadataType);
} }
/** @stable */ /** @stable */
@ -300,12 +296,10 @@ export interface ComponentMetadataFactory {
export interface ComponentMetadataType extends DirectiveMetadataType { export interface ComponentMetadataType extends DirectiveMetadataType {
animations?: AnimationEntryMetadata[]; animations?: AnimationEntryMetadata[];
changeDetection?: ChangeDetectionStrategy; changeDetection?: ChangeDetectionStrategy;
directives?: Array<Type<any> | any[]>;
encapsulation?: ViewEncapsulation; encapsulation?: ViewEncapsulation;
entryComponents?: Array<Type<any> | any[]>; entryComponents?: Array<Type<any> | any[]>;
interpolation?: [string, string]; interpolation?: [string, string];
moduleId?: string; moduleId?: string;
pipes?: Array<Type<any> | any[]>;
styleUrls?: string[]; styleUrls?: string[];
styles?: string[]; styles?: string[];
template?: string; template?: string;

View File

@ -1,6 +1,6 @@
/** @experimental */ /** @experimental */
export declare class UpgradeAdapter { export declare class UpgradeAdapter {
constructor(ng2AppModule?: Type<any>); constructor(ng2AppModule: Type<any>);
bootstrap(element: Element, modules?: any[], config?: angular.IAngularBootstrapConfig): UpgradeAdapterRef; bootstrap(element: Element, modules?: any[], config?: angular.IAngularBootstrapConfig): UpgradeAdapterRef;
downgradeNg2Component(type: Type<any>): Function; downgradeNg2Component(type: Type<any>): Function;
downgradeNg2Provider(token: any): Function; downgradeNg2Provider(token: any): Function;