perf(core): Remove decorator DSL which depends on Reflect

BREAKING CHANGE

It is no longer possible to declare classes in this format.

```
Component({...}).
Class({
  constructor: function() {...}
})
```

This format would only work with JIT and with ES5. This mode doesn’t
allow build tools like Webpack to process and optimize the code, which
results in prohibitively large bundles. We are removing this API
because we are trying to ensure that everyone is on the fast path by
default, and it is not possible to get on the fast path using the ES5
DSL. The replacement is to use TypeScript and `@Decorator` format.

```
@Component({...})
class {
  constructor() {...}
}
```
This commit is contained in:
Miško Hevery 2017-08-08 14:03:27 -07:00 committed by Hans
parent 679608db65
commit cac130eff9
12 changed files with 456 additions and 755 deletions

View File

@ -13,7 +13,7 @@
*/
export * from './metadata';
export * from './version';
export {Class, ClassDefinition, TypeDecorator} from './util/decorators';
export {TypeDecorator} from './util/decorators';
export * from './di';
export {createPlatform, assertPlatform, destroyPlatform, getPlatform, PlatformRef, ApplicationRef, enableProdMode, isDevMode, createPlatformFactory, NgProbeToken} from './application_ref';
export {APP_ID, PACKAGE_ROOT_URL, PLATFORM_INITIALIZER, PLATFORM_ID, APP_BOOTSTRAP_LISTENER} from './application_tokens';

View File

@ -277,6 +277,7 @@ export abstract class ReflectiveInjector implements Injector {
}
export class ReflectiveInjector_ implements ReflectiveInjector {
private static INJECTOR_KEY = ReflectiveKey.get(Injector);
/** @internal */
_constructionCounter: number = 0;
/** @internal */
@ -389,7 +390,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
}
private _getByKey(key: ReflectiveKey, visibility: Self|SkipSelf|null, notFoundValue: any): any {
if (key === INJECTOR_KEY) {
if (key === ReflectiveInjector_.INJECTOR_KEY) {
return this;
}
@ -463,8 +464,6 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
toString(): string { return this.displayName; }
}
const INJECTOR_KEY = ReflectiveKey.get(Injector);
function _mapProviders(injector: ReflectiveInjector_, fn: Function): any[] {
const res: any[] = new Array(injector._providers.length);
for (let i = 0; i < injector._providers.length; ++i) {

View File

@ -73,18 +73,6 @@ export interface AttributeDecorator {
*
* {@example core/ts/metadata/metadata.ts region='attributeFactory'}
*
* ### Example as ES5 DSL
*
* ```
* var MyComponent = ng
* .Component({...})
* .Class({
* constructor: [new ng.Attribute('title'), function(title) {
* ...
* }]
* })
* ```
*
* ### Example as ES5 annotation
*
* ```

View File

@ -13,8 +13,20 @@ export interface PlatformReflectionCapabilities {
isReflectionEnabled(): boolean;
factory(type: Type<any>): Function;
hasLifecycleHook(type: any, lcProperty: string): boolean;
/**
* Return a list of annotations/types for constructor parameters
*/
parameters(type: Type<any>): any[][];
/**
* Return a list of annotations declared on the class
*/
annotations(type: Type<any>): any[];
/**
* Return a object literal which describes the annotations on Class fields/properties.
*/
propMetadata(typeOrFunc: Type<any>): {[key: string]: any[]};
getter(name: string): GetterFn;
setter(name: string): SetterFn;

View File

@ -8,9 +8,12 @@
import {Type, isType} from '../type';
import {global, stringify} from '../util';
import {ANNOTATIONS, PARAMETERS, PROP_METADATA} from '../util/decorators';
import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
import {GetterFn, MethodFn, SetterFn} from './types';
/**
* Attention: This regex has to hold even if the code is minified!
*/
@ -85,12 +88,11 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
}
// API for metadata created by invoking the decorators.
if (this._reflect != null && this._reflect.getOwnMetadata != null) {
const paramAnnotations = this._reflect.getOwnMetadata('parameters', type);
const paramTypes = this._reflect.getOwnMetadata('design:paramtypes', type);
if (paramTypes || paramAnnotations) {
return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
}
const paramAnnotations = type.hasOwnProperty(PARAMETERS) && (type as any)[PARAMETERS];
const paramTypes = this._reflect && this._reflect.getOwnMetadata &&
this._reflect.getOwnMetadata('design:paramtypes', type);
if (paramTypes || paramAnnotations) {
return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
}
// If a class has no decorators, at least create metadata
@ -130,8 +132,8 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
}
// API for metadata created by invoking the decorators.
if (this._reflect && this._reflect.getOwnMetadata) {
return this._reflect.getOwnMetadata('annotations', typeOrFunc);
if (typeOrFunc.hasOwnProperty(ANNOTATIONS)) {
return (typeOrFunc as any)[ANNOTATIONS];
}
return null;
}
@ -169,8 +171,8 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities {
}
// API for metadata created by invoking the decorators.
if (this._reflect && this._reflect.getOwnMetadata) {
return this._reflect.getOwnMetadata('propMetadata', typeOrFunc);
if (typeOrFunc.hasOwnProperty(PROP_METADATA)) {
return (typeOrFunc as any)[PROP_METADATA];
}
return null;
}

View File

@ -7,54 +7,12 @@
*/
import {Type} from '../type';
import {global, stringify} from '../util';
let _nextClassId = 0;
const Reflect = global['Reflect'];
/**
* Declares the interface to be used with {@link Class}.
*
* @stable
*/
export type ClassDefinition = {
/**
* Optional argument for specifying the superclass.
*/
extends?: Type<any>;
/**
* Required constructor function for a class.
*
* The function may be optionally wrapped in an `Array`, in which case additional parameter
* annotations may be specified.
* The number of arguments and the number of parameter annotations must match.
*
* See {@link Class} for example of usage.
*/
constructor: Function | any[];
} &
{
/**
* Other methods on the class. Note that values should have type 'Function' but TS requires
* all properties to have a narrower type than the index signature.
*/
[x: string]: Type<any>|Function|any[];
};
/**
* An interface implemented by all Angular type decorators, which allows them to be used as ES7
* decorators as well as
* Angular DSL syntax.
*
* DSL syntax:
*
* ```
* var MyClass = ng
* .Component({...})
* .Class({...});
* ```
*
* ES7 syntax:
*
* ```
@ -74,189 +32,11 @@ export interface TypeDecorator {
// so we cannot declare this interface as a subtype.
// see https://github.com/angular/angular/issues/3379#issuecomment-126169417
(target: Object, propertyKey?: string|symbol, parameterIndex?: number): void;
/**
* Storage for the accumulated annotations so far used by the DSL syntax.
*
* Used by {@link Class} to annotate the generated class.
*/
annotations: any[];
/**
* Generate a class from the definition and annotate it with {@link TypeDecorator#annotations}.
*/
Class(obj: ClassDefinition): Type<any>;
}
function extractAnnotation(annotation: any): any {
if (typeof annotation === 'function' && annotation.hasOwnProperty('annotation')) {
// it is a decorator, extract annotation
annotation = annotation.annotation;
}
return annotation;
}
function applyParams(fnOrArray: Function | any[] | undefined, key: string): Function {
if (fnOrArray === Object || fnOrArray === String || fnOrArray === Function ||
fnOrArray === Number || fnOrArray === Array) {
throw new Error(`Can not use native ${stringify(fnOrArray)} as constructor`);
}
if (typeof fnOrArray === 'function') {
return fnOrArray;
}
if (Array.isArray(fnOrArray)) {
const annotations: any[] = fnOrArray as any[];
const annoLength = annotations.length - 1;
const fn: Function = fnOrArray[annoLength];
if (typeof fn !== 'function') {
throw new Error(
`Last position of Class method array must be Function in key ${key} was '${stringify(fn)}'`);
}
if (annoLength != fn.length) {
throw new Error(
`Number of annotations (${annoLength}) does not match number of arguments (${fn.length}) in the function: ${stringify(fn)}`);
}
const paramsAnnotations: any[][] = [];
for (let i = 0, ii = annotations.length - 1; i < ii; i++) {
const paramAnnotations: any[] = [];
paramsAnnotations.push(paramAnnotations);
const annotation = annotations[i];
if (Array.isArray(annotation)) {
for (let j = 0; j < annotation.length; j++) {
paramAnnotations.push(extractAnnotation(annotation[j]));
}
} else if (typeof annotation === 'function') {
paramAnnotations.push(extractAnnotation(annotation));
} else {
paramAnnotations.push(annotation);
}
}
Reflect.defineMetadata('parameters', paramsAnnotations, fn);
return fn;
}
throw new Error(
`Only Function or Array is supported in Class definition for key '${key}' is '${stringify(fnOrArray)}'`);
}
/**
* Provides a way for expressing ES6 classes with parameter annotations in ES5.
*
* ## Basic Example
*
* ```
* var Greeter = ng.Class({
* constructor: function(name) {
* this.name = name;
* },
*
* greet: function() {
* alert('Hello ' + this.name + '!');
* }
* });
* ```
*
* is equivalent to ES6:
*
* ```
* class Greeter {
* constructor(name) {
* this.name = name;
* }
*
* greet() {
* alert('Hello ' + this.name + '!');
* }
* }
* ```
*
* or equivalent to ES5:
*
* ```
* var Greeter = function (name) {
* this.name = name;
* }
*
* Greeter.prototype.greet = function () {
* alert('Hello ' + this.name + '!');
* }
* ```
*
* ### Example with parameter annotations
*
* ```
* var MyService = ng.Class({
* constructor: [String, [new Optional(), Service], function(name, myService) {
* ...
* }]
* });
* ```
*
* is equivalent to ES6:
*
* ```
* class MyService {
* constructor(name: string, @Optional() myService: Service) {
* ...
* }
* }
* ```
*
* ### Example with inheritance
*
* ```
* var Shape = ng.Class({
* constructor: (color) {
* this.color = color;
* }
* });
*
* var Square = ng.Class({
* extends: Shape,
* constructor: function(color, size) {
* Shape.call(this, color);
* this.size = size;
* }
* });
* ```
* @suppress {globalThis}
* @stable
*/
export function Class(clsDef: ClassDefinition): Type<any> {
const constructor = applyParams(
clsDef.hasOwnProperty('constructor') ? clsDef.constructor : undefined, 'constructor');
let proto = constructor.prototype;
if (clsDef.hasOwnProperty('extends')) {
if (typeof clsDef.extends === 'function') {
(<Function>constructor).prototype = proto =
Object.create((<Function>clsDef.extends).prototype);
} else {
throw new Error(
`Class definition 'extends' property must be a constructor function was: ${stringify(clsDef.extends)}`);
}
}
for (const key in clsDef) {
if (key !== 'extends' && key !== 'prototype' && clsDef.hasOwnProperty(key)) {
proto[key] = applyParams(clsDef[key], key);
}
}
if (this && this.annotations instanceof Array) {
Reflect.defineMetadata('annotations', this.annotations, constructor);
}
const constructorName = constructor['name'];
if (!constructorName || constructorName === 'constructor') {
(constructor as any)['overriddenName'] = `class${_nextClassId++}`;
}
return <Type<any>>constructor;
}
export const ANNOTATIONS = '__annotations__';
export const PARAMETERS = '__paramaters__';
export const PROP_METADATA = '__prop__metadata__';
/**
* @suppress {globalThis}
@ -268,27 +48,21 @@ export function makeDecorator(
const metaCtor = makeMetadataCtor(props);
function DecoratorFactory(objOrType: any): (cls: any) => any {
if (!(Reflect && Reflect.getOwnMetadata)) {
throw 'reflect-metadata shim is required when using class decorators';
}
if (this instanceof DecoratorFactory) {
metaCtor.call(this, objOrType);
return this;
}
const annotationInstance = new (<any>DecoratorFactory)(objOrType);
const chainAnnotation =
typeof this === 'function' && Array.isArray(this.annotations) ? this.annotations : [];
chainAnnotation.push(annotationInstance);
const TypeDecorator: TypeDecorator = <TypeDecorator>function TypeDecorator(cls: Type<any>) {
const annotations = Reflect.getOwnMetadata('annotations', cls) || [];
// Use of Object.defineProperty is important since it creates non-enumerable property which
// prevents the property is copied during subclassing.
const annotations = cls.hasOwnProperty(ANNOTATIONS) ?
(cls as any)[ANNOTATIONS] :
Object.defineProperty(cls, ANNOTATIONS, {value: []})[ANNOTATIONS];
annotations.push(annotationInstance);
Reflect.defineMetadata('annotations', annotations, cls);
return cls;
};
TypeDecorator.annotations = chainAnnotation;
TypeDecorator.Class = Class;
if (chainFn) chainFn(TypeDecorator);
return TypeDecorator;
}
@ -327,7 +101,11 @@ export function makeParamDecorator(
return ParamDecorator;
function ParamDecorator(cls: any, unusedKey: any, index: number): any {
const parameters: (any[] | null)[] = Reflect.getOwnMetadata('parameters', cls) || [];
// Use of Object.defineProperty is important since it creates non-enumerable property which
// prevents the property is copied during subclassing.
const parameters = cls.hasOwnProperty(PARAMETERS) ?
(cls as any)[PARAMETERS] :
Object.defineProperty(cls, PARAMETERS, {value: []})[PARAMETERS];
// there might be gaps if some in between parameters do not have annotations.
// we pad with nulls.
@ -335,10 +113,7 @@ export function makeParamDecorator(
parameters.push(null);
}
parameters[index] = parameters[index] || [];
parameters[index] !.push(annotationInstance);
Reflect.defineMetadata('parameters', parameters, cls);
(parameters[index] = parameters[index] || []).push(annotationInstance);
return cls;
}
}
@ -363,10 +138,14 @@ export function makePropDecorator(
const decoratorInstance = new (<any>PropDecoratorFactory)(...args);
return function PropDecorator(target: any, name: string) {
const meta = Reflect.getOwnMetadata('propMetadata', target.constructor) || {};
const constructor = target.constructor;
// Use of Object.defineProperty is important since it creates non-enumerable property which
// prevents the property is copied during subclassing.
const meta = constructor.hasOwnProperty(PROP_METADATA) ?
(constructor as any)[PROP_METADATA] :
Object.defineProperty(constructor, PROP_METADATA, {value: {}})[PROP_METADATA];
meta[name] = meta.hasOwnProperty(name) && meta[name] || [];
meta[name].unshift(decoratorInstance);
Reflect.defineMetadata('propMetadata', meta, target.constructor);
};
}

View File

@ -1,32 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Component, Directive} from '@angular/core';
import {reflector} from '@angular/core/src/reflection/reflection';
import {describe, expect, it} from '@angular/core/testing/src/testing_internal';
export function main() {
describe('es5 decorators', () => {
it('should declare directive class', () => {
const MyDirective = Directive({}).Class({constructor: function() { this.works = true; }});
expect(new MyDirective().works).toEqual(true);
});
it('should declare Component class', () => {
const MyComponent = Component({}).Class({constructor: function() { this.works = true; }});
expect(new MyComponent().works).toEqual(true);
});
it('should create type in ES5', () => {
class MyComponent {};
let as: any /** TODO #9100 */;
(<any>MyComponent).annotations = as = Component({});
expect(reflector.annotations(MyComponent)).toEqual(as.annotations);
});
});
}

View File

@ -6,17 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Inject} from '@angular/core';
import {reflector} from '@angular/core/src/reflection/reflection';
import {global} from '@angular/core/src/util';
import {Class, makeDecorator, makePropDecorator} from '@angular/core/src/util/decorators';
import {reflector} from '../../src/reflection/reflection';
import {ANNOTATIONS, makeDecorator, makePropDecorator} from '../../src/util/decorators';
class DecoratedParent {}
class DecoratedChild extends DecoratedParent {}
export function main() {
const Reflect = global['Reflect'];
const TerminalDecorator =
makeDecorator('TerminalDecorator', (data: any) => ({terminal: true, ...data}));
const TestDecorator = makeDecorator(
@ -54,7 +50,7 @@ export function main() {
it('should invoke as decorator', () => {
function Type() {}
TestDecorator({marker: 'WORKS'})(Type);
const annotations = Reflect.getOwnMetadata('annotations', Type);
const annotations = (Type as any)[ANNOTATIONS];
expect(annotations[0].marker).toEqual('WORKS');
});
@ -64,102 +60,13 @@ export function main() {
expect(annotation.marker).toEqual('WORKS');
});
it('should invoke as chain', () => {
let chain: any = TestDecorator({marker: 'WORKS'});
expect(typeof chain.Terminal).toEqual('function');
chain = chain.Terminal();
expect(chain.annotations[0] instanceof TestDecorator).toEqual(true);
expect(chain.annotations[0].marker).toEqual('WORKS');
expect(chain.annotations[1] instanceof TerminalDecorator).toEqual(true);
});
it('should not apply decorators from the prototype chain', function() {
TestDecorator({marker: 'parent'})(DecoratedParent);
TestDecorator({marker: 'child'})(DecoratedChild);
const annotations = Reflect.getOwnMetadata('annotations', DecoratedChild);
const annotations = (DecoratedChild as any)[ANNOTATIONS];
expect(annotations.length).toBe(1);
expect(annotations[0].marker).toEqual('child');
});
describe('Class', () => {
it('should create a class', () => {
let i0: any;
let i1: any;
const MyClass = (<any>TestDecorator({marker: 'test-works'})).Class(<any>{
extends: Class(<any>{
constructor: function() {},
extendWorks: function() { return 'extend ' + this.arg; }
}),
constructor: [String, function(arg: any) { this.arg = arg; }],
methodA: [
i0 = new Inject(String),
[i1 = Inject(String), Number],
function(a: any, b: any) {},
],
works: function() { return this.arg; },
prototype: 'IGNORE'
});
const obj: any = new MyClass('WORKS');
expect(obj.arg).toEqual('WORKS');
expect(obj.works()).toEqual('WORKS');
expect(obj.extendWorks()).toEqual('extend WORKS');
expect(reflector.parameters(MyClass)).toEqual([[String]]);
expect(reflector.parameters(obj.methodA)).toEqual([[i0], [i1.annotation, Number]]);
const proto = (<Function>MyClass).prototype;
expect(proto.extends).toEqual(undefined);
expect(proto.prototype).toEqual(undefined);
expect(reflector.annotations(MyClass)[0].marker).toEqual('test-works');
});
describe('errors', () => {
it('should ensure that last constructor is required', () => {
expect(() => { (<Function>Class)({}); })
.toThrowError(
'Only Function or Array is supported in Class definition for key \'constructor\' is \'undefined\'');
});
it('should ensure that we dont accidentally patch native objects', () => {
expect(() => {
(<Function>Class)({constructor: Object});
}).toThrowError('Can not use native Object as constructor');
});
it('should ensure that last position is function', () => {
expect(() => { Class({constructor: []}); })
.toThrowError(
'Last position of Class method array must be Function in key constructor was \'undefined\'');
});
it('should ensure that annotation count matches parameters count', () => {
expect(() => {
Class({constructor: [String, function MyType() {}]});
})
.toThrowError(
'Number of annotations (1) does not match number of arguments (0) in the function: MyType');
});
it('should ensure that only Function|Arrays are supported', () => {
expect(() => { Class({constructor: function() {}, method: <any>'non_function'}); })
.toThrowError(
'Only Function or Array is supported in Class definition for key \'method\' is \'non_function\'');
});
it('should ensure that extends is a Function', () => {
expect(() => { Class({extends: <any>'non_type', constructor: function() {}}); })
.toThrowError(
'Class definition \'extends\' property must be a constructor function was: non_type');
});
it('should assign an overridden name for anonymous constructor functions', () => {
expect((Class({constructor: function() {}}) as any).overriddenName).not.toBeUndefined();
});
});
});
});
}

View File

@ -551,19 +551,19 @@ export class UpgradeAdapter {
.then(() => {
// At this point we have ng1 injector and we have prepared
// ng1 components to be upgraded, we now can bootstrap ng2.
const DynamicNgUpgradeModule =
NgModule({
providers: [
{provide: $INJECTOR, useFactory: () => ng1Injector},
{provide: $COMPILE, useFactory: () => ng1Injector.get($COMPILE)},
this.upgradedProviders
],
imports: [this.ng2AppModule],
entryComponents: this.downgradedComponents
}).Class({
constructor: function DynamicNgUpgradeModule() {},
ngDoBootstrap: function() {}
});
@NgModule({
providers: [
{provide: $INJECTOR, useFactory: () => ng1Injector},
{provide: $COMPILE, useFactory: () => ng1Injector.get($COMPILE)},
this.upgradedProviders
],
imports: [this.ng2AppModule],
entryComponents: this.downgradedComponents
})
class DynamicNgUpgradeModule {
constructor() {}
ngDoBootstrap() {}
}
(platformRef as any)
._bootstrapModuleWithZone(
DynamicNgUpgradeModule, this.compilerOptions, this.ngZone)

View File

@ -38,23 +38,26 @@ export class UpgradeNg1ComponentAdapterBuilder {
name.replace(CAMEL_CASE, (all: string, next: string) => '-' + next.toLowerCase());
const self = this;
this.type =
Directive({selector: selector, inputs: this.inputsRename, outputs: this.outputsRename})
.Class({
constructor: [
new Inject($SCOPE), Injector, ElementRef,
function(scope: angular.IScope, injector: Injector, elementRef: ElementRef) {
const helper = new UpgradeHelper(injector, name, elementRef, this.directive);
return new UpgradeNg1ComponentAdapter(
helper, scope, self.template, self.inputs, self.outputs, self.propertyOutputs,
self.checkProperties, self.propertyMap);
}
],
ngOnInit: function() { /* needs to be here for ng2 to properly detect it */ },
ngOnChanges: function() { /* needs to be here for ng2 to properly detect it */ },
ngDoCheck: function() { /* needs to be here for ng2 to properly detect it */ },
ngOnDestroy: function() { /* needs to be here for ng2 to properly detect it */ },
});
@Directive({selector: selector, inputs: this.inputsRename, outputs: this.outputsRename})
class MyClass {
directive: angular.IDirective;
constructor(
@Inject($SCOPE) scope: angular.IScope, injector: Injector, elementRef: ElementRef) {
const helper = new UpgradeHelper(injector, name, elementRef, this.directive);
return new UpgradeNg1ComponentAdapter(
helper, scope, self.template, self.inputs, self.outputs, self.propertyOutputs,
self.checkProperties, self.propertyMap) as any;
}
ngOnInit() { /* needs to be here for ng2 to properly detect it */
}
ngOnChanges() { /* needs to be here for ng2 to properly detect it */
}
ngDoCheck() { /* needs to be here for ng2 to properly detect it */
}
ngOnDestroy() { /* needs to be here for ng2 to properly detect it */
}
};
this.type = MyClass;
}
extractBindings() {

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectorRef, Class, Component, EventEmitter, Input, NO_ERRORS_SCHEMA, NgModule, NgZone, OnChanges, SimpleChange, SimpleChanges, Testability, destroyPlatform, forwardRef} from '@angular/core';
import {ChangeDetectorRef, Component, EventEmitter, Input, NO_ERRORS_SCHEMA, NgModule, NgZone, OnChanges, SimpleChange, SimpleChanges, Testability, destroyPlatform, forwardRef} from '@angular/core';
import {async, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
@ -25,14 +25,16 @@ export function main() {
it('should instantiate ng2 in ng1 template and project content', async(() => {
const ng1Module = angular.module('ng1', []);
const Ng2 = Component({
selector: 'ng2',
template: `{{ 'NG2' }}(<ng-content></ng-content>)`,
}).Class({constructor: function() {}});
@Component({
selector: 'ng2',
template: `{{ 'NG2' }}(<ng-content></ng-content>)`,
})
class Ng2 {
}
const Ng2Module = NgModule({declarations: [Ng2], imports: [BrowserModule]}).Class({
constructor: function() {}
});
@NgModule({declarations: [Ng2], imports: [BrowserModule]})
class Ng2Module {
}
const element =
html('<div>{{ \'ng1[\' }}<ng2>~{{ \'ng-content\' }}~</ng2>{{ \']\' }}</div>');
@ -49,15 +51,19 @@ export function main() {
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
const ng1Module = angular.module('ng1', []);
const Ng2 = Component({
selector: 'ng2',
template: `{{ 'ng2(' }}<ng1>{{'transclude'}}</ng1>{{ ')' }}`,
}).Class({constructor: function Ng2() {}});
@Component({
selector: 'ng2',
template: `{{ 'ng2(' }}<ng1>{{'transclude'}}</ng1>{{ ')' }}`,
})
class Ng2 {
};
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function Ng2Module() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng1', () => {
return {transclude: true, template: '{{ "ng1" }}(<ng-transclude></ng-transclude>)'};
@ -77,19 +83,20 @@ export function main() {
spyOn(platformRef, '_bootstrapModuleWithZone').and.callThrough();
const ng1Module = angular.module('ng1', []);
const Ng2 = Component({
selector: 'ng2',
template: `{{ 'NG2' }}(<ng-content></ng-content>)`
}).Class({constructor: function() {}});
@Component({selector: 'ng2', template: `{{ 'NG2' }}(<ng-content></ng-content>)`})
class Ng2 {
}
const element =
html('<div>{{ \'ng1[\' }}<ng2>~{{ \'ng-content\' }}~</ng2>{{ \']\' }}</div>');
const Ng2AppModule =
NgModule({
declarations: [Ng2],
imports: [BrowserModule],
}).Class({constructor: function Ng2AppModule() {}, ngDoBootstrap: function() {}});
@NgModule({
declarations: [Ng2],
imports: [BrowserModule],
})
class Ng2AppModule {
ngDoBootstrap() {}
};
const adapter: UpgradeAdapter = new UpgradeAdapter(Ng2AppModule, {providers: []});
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
@ -107,15 +114,19 @@ export function main() {
beforeEach(() => {
angular.module('ng1', []);
const ng2Component = Component({
selector: 'ng2',
template: `<BAD TEMPLATE div></div>`,
}).Class({constructor: function() {}});
@Component({
selector: 'ng2',
template: `<BAD TEMPLATE div></div>`,
})
class ng2Component {
}
const Ng2Module = NgModule({
declarations: [ng2Component],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [ng2Component],
imports: [BrowserModule],
})
class Ng2Module {
}
adapter = new UpgradeAdapter(Ng2Module);
});
@ -161,18 +172,22 @@ export function main() {
$rootScope.reset = () => log.length = 0;
});
const Ng2 = Component({
selector: 'ng2',
template: `{{l('2A')}}<ng1a></ng1a>{{l('2B')}}<ng1b></ng1b>{{l('2C')}}`
}).Class({constructor: function() { this.l = l; }});
@Component({
selector: 'ng2',
template: `{{l('2A')}}<ng1a></ng1a>{{l('2B')}}<ng1b></ng1b>{{l('2C')}}`
})
class Ng2 {
l: any;
constructor() { this.l = l; }
}
const Ng2Module =
NgModule({
declarations: [
adapter.upgradeNg1Component('ng1a'), adapter.upgradeNg1Component('ng1b'), Ng2
],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations:
[adapter.upgradeNg1Component('ng1a'), adapter.upgradeNg1Component('ng1b'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
@ -302,37 +317,35 @@ export function main() {
$rootScope.eventA = '?';
$rootScope.eventB = '?';
});
const Ng2 = Component({
selector: 'ng2',
inputs:
['literal', 'interpolate', 'oneWayA', 'oneWayB', 'twoWayA', 'twoWayB'],
outputs: [
'eventA', 'eventB', 'twoWayAEmitter: twoWayAChange',
'twoWayBEmitter: twoWayBChange'
],
template: 'ignore: {{ignore}}; ' +
'literal: {{literal}}; interpolate: {{interpolate}}; ' +
'oneWayA: {{oneWayA}}; oneWayB: {{oneWayB}}; ' +
'twoWayA: {{twoWayA}}; twoWayB: {{twoWayB}}; ({{ngOnChangesCount}})'
}).Class({
constructor: function() {
this.ngOnChangesCount = 0;
this.ignore = '-';
this.literal = '?';
this.interpolate = '?';
this.oneWayA = '?';
this.oneWayB = '?';
this.twoWayA = '?';
this.twoWayB = '?';
this.eventA = new EventEmitter();
this.eventB = new EventEmitter();
this.twoWayAEmitter = new EventEmitter();
this.twoWayBEmitter = new EventEmitter();
},
ngOnChanges: function(changes: SimpleChanges) {
@Component({
selector: 'ng2',
inputs: ['literal', 'interpolate', 'oneWayA', 'oneWayB', 'twoWayA', 'twoWayB'],
outputs: [
'eventA', 'eventB', 'twoWayAEmitter: twoWayAChange', 'twoWayBEmitter: twoWayBChange'
],
template: 'ignore: {{ignore}}; ' +
'literal: {{literal}}; interpolate: {{interpolate}}; ' +
'oneWayA: {{oneWayA}}; oneWayB: {{oneWayB}}; ' +
'twoWayA: {{twoWayA}}; twoWayB: {{twoWayB}}; ({{ngOnChangesCount}})'
})
class Ng2 {
ngOnChangesCount = 0;
ignore = '-';
literal = '?';
interpolate = '?';
oneWayA = '?';
oneWayB = '?';
twoWayA = '?';
twoWayB = '?';
eventA = new EventEmitter();
eventB = new EventEmitter();
twoWayAEmitter = new EventEmitter();
twoWayBEmitter = new EventEmitter();
ngOnChanges(changes: SimpleChanges) {
const assert = (prop: string, value: any) => {
if (this[prop] != value) {
throw new Error(`Expected: '${prop}' to be '${value}' but was '${this[prop]}'`);
if ((this as any)[prop] != value) {
throw new Error(
`Expected: '${prop}' to be '${value}' but was '${(this as any)[prop]}'`);
}
};
@ -374,13 +387,15 @@ export function main() {
throw new Error('Called too many times! ' + JSON.stringify(changes));
}
}
});
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const Ng2Module = NgModule({
declarations: [Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [Ng2],
imports: [BrowserModule],
})
class Ng2Module {
};
const element = html(`<div>
<ng2 literal="Text" interpolate="Hello {{name}}"
@ -493,11 +508,13 @@ export function main() {
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html(`<div><ng2 ng-model="modelA"></ng2> | {{modelA}}</div>`);
const Ng2Module = NgModule({
declarations: [Ng2],
imports: [BrowserModule],
schemas: [NO_ERRORS_SCHEMA],
}).Class({constructor: function() {}});
@NgModule({
declarations: [Ng2],
imports: [BrowserModule],
schemas: [NO_ERRORS_SCHEMA],
})
class Ng2Module {
}
adapter.bootstrap(element, ['ng1']).ready((ref) => {
let $rootScope: any = ref.ng1RootScope;
@ -537,15 +554,17 @@ export function main() {
};
});
const Ng2 = Component({selector: 'ng2', template: 'test'}).Class({
constructor: function() {},
ngOnDestroy: function() { onDestroyed.emit('destroyed'); }
});
@Component({selector: 'ng2', template: 'test'})
class Ng2 {
ngOnDestroy() { onDestroyed.emit('destroyed'); }
}
const Ng2Module = NgModule({
declarations: [Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html('<ng1></ng1>');
@ -571,13 +590,16 @@ export function main() {
}
]);
const Ng2 =
Component({selector: 'ng2', template: 'test'}).Class({constructor: function() {}});
@Component({selector: 'ng2', template: 'test'})
class Ng2 {
};
const Ng2Module = NgModule({
declarations: [Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html('<ng1></ng1>');
@ -590,15 +612,17 @@ export function main() {
it('should support multi-slot projection', async(() => {
const ng1Module = angular.module('ng1', []);
const Ng2 = Component({
selector: 'ng2',
template: '2a(<ng-content select=".ng1a"></ng-content>)' +
'2b(<ng-content select=".ng1b"></ng-content>)'
}).Class({constructor: function() {}});
@Component({
selector: 'ng2',
template: '2a(<ng-content select=".ng1a"></ng-content>)' +
'2b(<ng-content select=".ng1b"></ng-content>)'
})
class Ng2 {
}
const Ng2Module = NgModule({declarations: [Ng2], imports: [BrowserModule]}).Class({
constructor: function() {}
});
@NgModule({declarations: [Ng2], imports: [BrowserModule]})
class Ng2Module {
}
// The ng-if on one of the projected children is here to make sure
// the correct slot is targeted even with structural directives in play.
@ -942,27 +966,27 @@ export function main() {
};
};
ng1Module.directive('ng1', ng1);
const Ng2 =
Component({
selector: 'ng2',
template:
'<ng1 fullName="{{last}}, {{first}}, {{city}}" [dataA]="first" [(dataB)]="last" [modelC]="city" ' +
'(event)="event=$event"></ng1>' +
'<ng1 fullName="{{\'TEST\'}}" dataA="First" dataB="Last" modelC="City"></ng1>' +
'{{event}}-{{last}}, {{first}}, {{city}}'
}).Class({
constructor: function() {
this.first = 'Victor';
this.last = 'Savkin';
this.city = 'SF';
this.event = '?';
}
});
@Component({
selector: 'ng2',
template:
'<ng1 fullName="{{last}}, {{first}}, {{city}}" [dataA]="first" [(dataB)]="last" [modelC]="city" ' +
'(event)="event=$event"></ng1>' +
'<ng1 fullName="{{\'TEST\'}}" dataA="First" dataB="Last" modelC="City"></ng1>' +
'{{event}}-{{last}}, {{first}}, {{city}}'
})
class Ng2 {
first = 'Victor';
last = 'Savkin';
city = 'SF';
event = '?';
}
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html(`<div><ng2></ng2></div>`);
@ -989,23 +1013,24 @@ export function main() {
};
};
ng1Module.directive('ng1', ng1);
const Ng2 = Component({
selector: 'ng2',
template: '<ng1 [dataA]="first" [modelB]="last"></ng1>' +
'<ng1 dataA="First" modelB="Last"></ng1>' +
'<ng1></ng1>' +
'<ng1></ng1>'
}).Class({
constructor: function() {
this.first = 'Victor';
this.last = 'Savkin';
}
});
@Component({
selector: 'ng2',
template: '<ng1 [dataA]="first" [modelB]="last"></ng1>' +
'<ng1 dataA="First" modelB="Last"></ng1>' +
'<ng1></ng1>' +
'<ng1></ng1>'
})
class Ng2 {
first = 'Victor';
last = 'Savkin';
}
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html(`<div><ng2></ng2></div>`);
@ -1036,23 +1061,22 @@ export function main() {
};
ng1Module.directive('ng1', ng1);
const Ng2 =
Component({
selector: 'ng2',
template:
'{{someText}} - Length: {{dataList.length}} | <ng1 [(data)]="dataList"></ng1>'
}).Class({
@Component({
selector: 'ng2',
template:
'{{someText}} - Length: {{dataList.length}} | <ng1 [(data)]="dataList"></ng1>'
})
class Ng2 {
dataList = [1, 2, 3];
someText = 'ng2';
}
constructor: function() {
this.dataList = [1, 2, 3];
this.someText = 'ng2';
}
});
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html(`<div><ng2></ng2></div>`);
@ -1081,23 +1105,22 @@ export function main() {
};
ng1Module.directive('ng1', ng1);
const Ng2 =
Component({
selector: 'ng2',
template:
'{{someText}} - Length: {{dataList.length}} | <ng1 [(data)]="dataList"></ng1>'
}).Class({
@Component({
selector: 'ng2',
template:
'{{someText}} - Length: {{dataList.length}} | <ng1 [(data)]="dataList"></ng1>'
})
class Ng2 {
dataList = [1, 2, 3];
someText = 'ng2';
}
constructor: function() {
this.dataList = [1, 2, 3];
this.someText = 'ng2';
}
});
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html(`<div><ng2></ng2></div>`);
@ -1122,14 +1145,16 @@ export function main() {
const ng1 = () => { return {templateUrl: 'url.html'}; };
ng1Module.directive('ng1', ng1);
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
constructor: function() {}
});
@Component({selector: 'ng2', template: '<ng1></ng1>'})
class Ng2 {
}
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html(`<div><ng2></ng2></div>`);
@ -1149,14 +1174,16 @@ export function main() {
const ng1 = () => { return {templateUrl() { return 'url.html'; }}; };
ng1Module.directive('ng1', ng1);
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
constructor: function() {}
});
@Component({selector: 'ng2', template: '<ng1></ng1>'})
class Ng2 {
}
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html(`<div><ng2></ng2></div>`);
@ -1173,14 +1200,16 @@ export function main() {
const ng1 = () => { return {template: ''}; };
ng1Module.directive('ng1', ng1);
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
constructor: function() {}
});
@Component({selector: 'ng2', template: '<ng1></ng1>'})
class Ng2 {
}
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html(`<div><ng2></ng2></div>`);
@ -1197,14 +1226,16 @@ export function main() {
const ng1 = () => { return {template() { return ''; }}; };
ng1Module.directive('ng1', ng1);
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
constructor: function() {}
});
@Component({selector: 'ng2', template: '<ng1></ng1>'})
class Ng2 {
}
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html(`<div><ng2></ng2></div>`);
@ -1222,14 +1253,16 @@ export function main() {
const ng1 = () => { return {templateUrl: 'url.html'}; };
ng1Module.directive('ng1', ng1);
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
constructor: function() {}
});
@Component({selector: 'ng2', template: '<ng1></ng1>'})
class Ng2 {
}
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html(`<div><ng2></ng2></div>`);
@ -1249,30 +1282,31 @@ export function main() {
template:
'{{ctl.scope}}; {{ctl.isClass}}; {{ctl.hasElement}}; {{ctl.isPublished()}}',
controllerAs: 'ctl',
controller: Class({
constructor: function($scope: any, $element: any) {
(<any>this).verifyIAmAClass();
controller: class {
scope: any; hasElement: string; $element: any; isClass: any;
constructor($scope: any, $element: any) {
this.verifyIAmAClass();
this.scope = $scope.$parent.$parent == $scope.$root ? 'scope' : 'wrong-scope';
this.hasElement = $element[0].nodeName;
this.$element = $element;
},
verifyIAmAClass: function() { this.isClass = 'isClass'; },
isPublished: function() {
} verifyIAmAClass() { this.isClass = 'isClass'; } isPublished() {
return this.$element.controller('ng1') == this ? 'published' : 'not-published';
}
})
}
};
};
ng1Module.directive('ng1', ng1);
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
constructor: function() {}
});
@Component({selector: 'ng2', template: '<ng1></ng1>'})
class Ng2 {
}
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html(`<div><ng2></ng2></div>`);
@ -1292,19 +1326,21 @@ export function main() {
bindToController: true,
template: '{{ctl.title}}',
controllerAs: 'ctl',
controller: Class({constructor: function() {}})
controller: class {}
};
};
ng1Module.directive('ng1', ng1);
const Ng2 = Component({selector: 'ng2', template: '<ng1 title="WORKS"></ng1>'}).Class({
constructor: function() {}
});
@Component({selector: 'ng2', template: '<ng1 title="WORKS"></ng1>'})
class Ng2 {
}
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html(`<div><ng2></ng2></div>`);
@ -1324,19 +1360,21 @@ export function main() {
bindToController: {title: '@'},
template: '{{ctl.title}}',
controllerAs: 'ctl',
controller: Class({constructor: function() {}})
controller: class {}
};
};
ng1Module.directive('ng1', ng1);
const Ng2 = Component({selector: 'ng2', template: '<ng1 title="WORKS"></ng1>'}).Class({
constructor: function() {}
});
@Component({selector: 'ng2', template: '<ng1 title="WORKS"></ng1>'})
class Ng2 {
}
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html(`<div><ng2></ng2></div>`);
@ -1357,7 +1395,7 @@ export function main() {
template: '{{ctl.status}}',
require: 'ng1',
controllerAs: 'ctrl',
controller: Class({constructor: function() { this.status = 'WORKS'; }}),
controller: class {status = 'WORKS';},
link: function(scope: any, element: any, attrs: any, linkController: any) {
expect(scope.$root).toEqual($rootScope);
expect(element[0].nodeName).toEqual('NG1');
@ -1368,14 +1406,16 @@ export function main() {
};
ng1Module.directive('ng1', ng1);
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
constructor: function() {}
});
@Component({selector: 'ng2', template: '<ng1></ng1>'})
class Ng2 {
}
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html(`<div><ng2></ng2></div>`);
@ -1389,9 +1429,7 @@ export function main() {
const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module));
const ng1Module = angular.module('ng1', []);
const parent = () => {
return {controller: Class({constructor: function() { this.parent = 'PARENT'; }})};
};
const parent = () => { return {controller: class {parent = 'PARENT';}}; };
const ng1 = () => {
return {
scope: {title: '@'},
@ -1399,7 +1437,7 @@ export function main() {
template: '{{parent.parent}}:{{ng1.status}}',
require: ['ng1', '^parent', '?^^notFound'],
controllerAs: 'ctrl',
controller: Class({constructor: function() { this.status = 'WORKS'; }}),
controller: class {status = 'WORKS';},
link: function(scope: any, element: any, attrs: any, linkControllers: any) {
expect(linkControllers[0].status).toEqual('WORKS');
expect(linkControllers[1].parent).toEqual('PARENT');
@ -1412,14 +1450,16 @@ export function main() {
ng1Module.directive('parent', parent);
ng1Module.directive('ng1', ng1);
const Ng2 = Component({selector: 'ng2', template: '<ng1></ng1>'}).Class({
constructor: function() {}
});
@Component({selector: 'ng2', template: '<ng1></ng1>'})
class Ng2 {
}
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
const element = html(`<div><parent><ng2></ng2></parent></div>`);
@ -1835,7 +1875,8 @@ export function main() {
}
// On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3),
// `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be on
// `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be
// on
// the queue at the end of the test, causing it to fail.
// Mocking animations (via `ngAnimateMock`) avoids the issue.
angular.module('ng1', ['ngAnimateMock'])
@ -1923,7 +1964,8 @@ export function main() {
}
// On browsers that don't support `requestAnimationFrame` (IE 9, Android <= 4.3),
// `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be on
// `$animate` will use `setTimeout(..., 16.6)` instead. This timeout will still be
// on
// the queue at the end of the test, causing it to fail.
// Mocking animations (via `ngAnimateMock`) avoids the issue.
angular.module('ng1', ['ngAnimateMock'])
@ -2618,19 +2660,21 @@ export function main() {
const ng1 = {
bindings: {personProfile: '<'},
template: 'Hello {{$ctrl.personProfile.firstName}} {{$ctrl.personProfile.lastName}}',
controller: Class({constructor: function() {}})
controller: class {}
};
ng1Module.component('ng1', ng1);
const Ng2 =
Component({selector: 'ng2', template: '<ng1 [personProfile]="goku"></ng1>'}).Class({
constructor: function() { this.goku = {firstName: 'GOKU', lastName: 'SAN'}; }
});
@Component({selector: 'ng2', template: '<ng1 [personProfile]="goku"></ng1>'})
class Ng2 {
goku = {firstName: 'GOKU', lastName: 'SAN'};
}
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
ng1Module.directive('ng2', adapter.downgradeNg2Component(Ng2));
@ -2650,19 +2694,22 @@ export function main() {
};
ng1Module.component('ng1', ng1);
const Ng2a = Component({selector: 'ng2a', template: 'ng2a(<ng1></ng1>)'}).Class({
constructor: function() {}
});
@Component({selector: 'ng2a', template: 'ng2a(<ng1></ng1>)'})
class Ng2a {
}
ng1Module.directive('ng2a', adapter.downgradeNg2Component(Ng2a));
const Ng2b =
Component({selector: 'ng2b', template: 'ng2b'}).Class({constructor: function() {}});
@Component({selector: 'ng2b', template: 'ng2b'})
class Ng2b {
}
ng1Module.directive('ng2b', adapter.downgradeNg2Component(Ng2b));
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2a, Ng2b],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2a, Ng2b],
imports: [BrowserModule],
})
class Ng2Module {
}
const element = html(`<div><ng2a></ng2a></div>`);
adapter.bootstrap(element, ['ng1']).ready((ref) => {
@ -2675,10 +2722,12 @@ export function main() {
function SomeToken() {}
it('should export ng2 instance to ng1', async(() => {
const MyNg2Module = NgModule({
providers: [{provide: SomeToken, useValue: 'correct_value'}],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
providers: [{provide: SomeToken, useValue: 'correct_value'}],
imports: [BrowserModule],
})
class MyNg2Module {
}
const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module);
const module = angular.module('myExample', []);
@ -2690,8 +2739,9 @@ export function main() {
}));
it('should export ng1 instance to ng2', async(() => {
const MyNg2Module =
NgModule({imports: [BrowserModule]}).Class({constructor: function() {}});
@NgModule({imports: [BrowserModule]})
class MyNg2Module {
};
const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module);
const module = angular.module('myExample', []);
@ -2710,18 +2760,17 @@ export function main() {
it('should respect hierarchical dependency injection for ng2', async(() => {
const ng1Module = angular.module('ng1', []);
const Ng2Parent = Component({
selector: 'ng2-parent',
template: `ng2-parent(<ng-content></ng-content>)`
}).Class({constructor: function() {}});
const Ng2Child = Component({selector: 'ng2-child', template: `ng2-child`}).Class({
constructor: [Ng2Parent, function(parent: any) {}]
});
@Component({selector: 'ng2-parent', template: `ng2-parent(<ng-content></ng-content>)`})
class Ng2Parent {
}
@Component({selector: 'ng2-child', template: `ng2-child`})
class Ng2Child {
constructor(parent: Ng2Parent) {}
}
const Ng2Module =
NgModule({declarations: [Ng2Parent, Ng2Child], imports: [BrowserModule]}).Class({
constructor: function() {}
});
@NgModule({declarations: [Ng2Parent, Ng2Child], imports: [BrowserModule]})
class Ng2Module {
}
const element = html('<ng2-parent><ng2-child></ng2-child></ng2-parent>');
@ -2737,8 +2786,9 @@ export function main() {
describe('testability', () => {
it('should handle deferred bootstrap', async(() => {
const MyNg2Module =
NgModule({imports: [BrowserModule]}).Class({constructor: function() {}});
@NgModule({imports: [BrowserModule]})
class MyNg2Module {
}
const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module);
angular.module('ng1', []);
@ -2759,8 +2809,9 @@ export function main() {
}));
it('should wait for ng2 testability', async(() => {
const MyNg2Module =
NgModule({imports: [BrowserModule]}).Class({constructor: function() {}});
@NgModule({imports: [BrowserModule]})
class MyNg2Module {
}
const adapter: UpgradeAdapter = new UpgradeAdapter(MyNg2Module);
angular.module('ng1', []);
@ -2797,17 +2848,20 @@ export function main() {
};
module.directive('ng1', ng1);
const Ng2 =
Component({
selector: 'ng2',
inputs: ['name'],
template: 'ng2[<ng1 [title]="name">transclude</ng1>](<ng-content></ng-content>)'
}).Class({constructor: function() {}});
@Component({
selector: 'ng2',
inputs: ['name'],
template: 'ng2[<ng1 [title]="name">transclude</ng1>](<ng-content></ng-content>)'
})
class Ng2 {
}
const Ng2Module = NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
}).Class({constructor: function() {}});
@NgModule({
declarations: [adapter.upgradeNg1Component('ng1'), Ng2],
imports: [BrowserModule],
})
class Ng2Module {
}
module.directive('ng2', adapter.downgradeNg2Component(Ng2));
@ -2829,14 +2883,16 @@ export function main() {
beforeEach(() => {
const ng1Module = angular.module('ng1', []);
const Ng2 = Component({
selector: 'ng2',
template: 'Hello World',
}).Class({constructor: function() {}});
@Component({
selector: 'ng2',
template: 'Hello World',
})
class Ng2 {
}
const Ng2Module = NgModule({declarations: [Ng2], imports: [BrowserModule]}).Class({
constructor: function() {}
});
@NgModule({declarations: [Ng2], imports: [BrowserModule]})
class Ng2Module {
}
const upgradeAdapter = new UpgradeAdapter(Ng2Module);
ng1Module.directive('ng2', upgradeAdapter.downgradeNg2Component(Ng2));

View File

@ -163,17 +163,6 @@ export declare abstract class ChangeDetectorRef {
abstract reattach(): void;
}
/** @stable */
export declare function Class(clsDef: ClassDefinition): Type<any>;
/** @stable */
export declare type ClassDefinition = {
extends?: Type<any>;
constructor: Function | any[];
} & {
[x: string]: Type<any> | Function | any[];
};
/** @stable */
export interface ClassProvider {
multi?: boolean;
@ -1029,10 +1018,8 @@ export declare const Type: FunctionConstructor;
/** @stable */
export interface TypeDecorator {
annotations: any[];
(target: Object, propertyKey?: string | symbol, parameterIndex?: number): void;
<T extends Type<any>>(type: T): T;
Class(obj: ClassDefinition): Type<any>;
}
/** @stable */