diff --git a/modules/@angular/compiler/src/directive_lifecycle_reflector.dart b/modules/@angular/compiler/src/directive_lifecycle_reflector.dart deleted file mode 100644 index 15fc459565..0000000000 --- a/modules/@angular/compiler/src/directive_lifecycle_reflector.dart +++ /dev/null @@ -1,21 +0,0 @@ -library angular2.src.core.compiler.directive_lifecycle_reflector; - -import 'package:angular2/src/core/reflection/reflection.dart'; -import 'package:angular2/src/core/metadata/lifecycle_hooks.dart'; - -const INTERFACES = const { - LifecycleHooks.OnInit: OnInit, - LifecycleHooks.OnDestroy: OnDestroy, - LifecycleHooks.DoCheck: DoCheck, - LifecycleHooks.OnChanges: OnChanges, - LifecycleHooks.AfterContentInit: AfterContentInit, - LifecycleHooks.AfterContentChecked: AfterContentChecked, - LifecycleHooks.AfterViewInit: AfterViewInit, - LifecycleHooks.AfterViewChecked: AfterViewChecked, -}; - -bool hasLifecycleHook(LifecycleHooks interface, token) { - if (token is! Type) return false; - Type interfaceType = INTERFACES[interface]; - return reflector.interfaces(token).contains(interfaceType); -} diff --git a/modules/@angular/compiler/src/directive_lifecycle_reflector.ts b/modules/@angular/compiler/src/directive_lifecycle_reflector.ts index f117c8754e..88d97b4066 100644 --- a/modules/@angular/compiler/src/directive_lifecycle_reflector.ts +++ b/modules/@angular/compiler/src/directive_lifecycle_reflector.ts @@ -1,31 +1,43 @@ +import { + OnInit, + OnDestroy, + DoCheck, + OnChanges, + AfterContentInit, + AfterContentChecked, + AfterViewInit, + AfterViewChecked, + reflector +} from '@angular/core'; import {LifecycleHooks} from '../core_private'; import {Type} from '../src/facade/lang'; +import {MapWrapper} from '../src/facade/collection'; +const LIFECYCLE_INTERFACES: Map = MapWrapper.createFromPairs([ + [LifecycleHooks.OnInit, OnInit], + [LifecycleHooks.OnDestroy, OnDestroy], + [LifecycleHooks.DoCheck, DoCheck], + [LifecycleHooks.OnChanges, OnChanges], + [LifecycleHooks.AfterContentInit, AfterContentInit], + [LifecycleHooks.AfterContentChecked, AfterContentChecked], + [LifecycleHooks.AfterViewInit, AfterViewInit], + [LifecycleHooks.AfterViewChecked, AfterViewChecked], +]); -export function hasLifecycleHook(lcInterface: LifecycleHooks, token): boolean { - if (!(token instanceof Type)) return false; +const LIFECYCLE_PROPS: Map = MapWrapper.createFromPairs([ + [LifecycleHooks.OnInit, 'ngOnInit'], + [LifecycleHooks.OnDestroy, 'ngOnDestroy'], + [LifecycleHooks.DoCheck, 'ngDoCheck'], + [LifecycleHooks.OnChanges, 'ngOnChanges'], + [LifecycleHooks.AfterContentInit, 'ngAfterContentInit'], + [LifecycleHooks.AfterContentChecked, 'ngAfterContentChecked'], + [LifecycleHooks.AfterViewInit, 'ngAfterViewInit'], + [LifecycleHooks.AfterViewChecked, 'ngAfterViewChecked'], +]); - var proto = (token).prototype; - - switch (lcInterface) { - case LifecycleHooks.AfterContentInit: - return !!proto.ngAfterContentInit; - case LifecycleHooks.AfterContentChecked: - return !!proto.ngAfterContentChecked; - case LifecycleHooks.AfterViewInit: - return !!proto.ngAfterViewInit; - case LifecycleHooks.AfterViewChecked: - return !!proto.ngAfterViewChecked; - case LifecycleHooks.OnChanges: - return !!proto.ngOnChanges; - case LifecycleHooks.DoCheck: - return !!proto.ngDoCheck; - case LifecycleHooks.OnDestroy: - return !!proto.ngOnDestroy; - case LifecycleHooks.OnInit: - return !!proto.ngOnInit; - default: - return false; - } +export function hasLifecycleHook(hook: LifecycleHooks, token): boolean { + var lcInterface = LIFECYCLE_INTERFACES.get(hook); + var lcProp = LIFECYCLE_PROPS.get(hook); + return reflector.hasLifecycleHook(token, lcInterface, lcProp); } diff --git a/modules/@angular/compiler/src/view_compiler/compile_view.ts b/modules/@angular/compiler/src/view_compiler/compile_view.ts index c5d96a1533..1125859b46 100644 --- a/modules/@angular/compiler/src/view_compiler/compile_view.ts +++ b/modules/@angular/compiler/src/view_compiler/compile_view.ts @@ -159,7 +159,7 @@ export class CompileView implements NameResolver { proxyParams.push(new o.FnParam(paramName)); proxyReturnEntries.push(o.variable(paramName)); } - createPureProxy(o.fn(proxyParams, [new o.ReturnStatement(o.literalArr(proxyReturnEntries))]), + createPureProxy(o.fn(proxyParams, [new o.ReturnStatement(o.literalArr(proxyReturnEntries))], new o.ArrayType(o.DYNAMIC_TYPE)), values.length, proxyExpr, this); return proxyExpr.callFn(values); } @@ -178,7 +178,7 @@ export class CompileView implements NameResolver { proxyReturnEntries.push([entries[i][0], o.variable(paramName)]); values.push(entries[i][1]); } - createPureProxy(o.fn(proxyParams, [new o.ReturnStatement(o.literalMap(proxyReturnEntries))]), + createPureProxy(o.fn(proxyParams, [new o.ReturnStatement(o.literalMap(proxyReturnEntries))], new o.MapType(o.DYNAMIC_TYPE)), entries.length, proxyExpr, this); return proxyExpr.callFn(values); } diff --git a/modules/@angular/compiler_cli/README.md b/modules/@angular/compiler_cli/README.md index 26f8aa6ed1..b657986cc5 100644 --- a/modules/@angular/compiler_cli/README.md +++ b/modules/@angular/compiler_cli/README.md @@ -83,12 +83,12 @@ Run the compiler from source: # Build angular2 and the compiler ./node_modules/.bin/tsc -p modules # Run it on the test project -$ ./integrationtest.sh +$ ./modules/@angular/compiler_cli./integrationtest.sh ``` Release: ``` -$ ./integrationtest.sh +$ ./modules/@angular/compiler_cli./integrationtest.sh $ cp modules/@angular/compiler_cli/README.md modules/@angular/compiler_cli/package.json dist/all/@angular/compiler_cli # npm login as angular $ npm publish dist/all/@angular/compiler_cli --access=public diff --git a/modules/@angular/compiler_cli/integrationtest/src/basic.html b/modules/@angular/compiler_cli/integrationtest/src/basic.html index 8a535b9117..97f4d2eaf5 100644 --- a/modules/@angular/compiler_cli/integrationtest/src/basic.html +++ b/modules/@angular/compiler_cli/integrationtest/src/basic.html @@ -1,3 +1,4 @@
{{ctxProp}}
- \ No newline at end of file + +
\ No newline at end of file diff --git a/modules/@angular/compiler_cli/integrationtest/src/basic.ts b/modules/@angular/compiler_cli/integrationtest/src/basic.ts index 56e6430eaf..5b819773f8 100644 --- a/modules/@angular/compiler_cli/integrationtest/src/basic.ts +++ b/modules/@angular/compiler_cli/integrationtest/src/basic.ts @@ -1,5 +1,5 @@ import {Component, Inject} from '@angular/core'; -import {FORM_DIRECTIVES, NgIf} from '@angular/common'; +import {FORM_DIRECTIVES, NgIf, NgFor} from '@angular/common'; import {MyComp} from './a/multiple_components'; @Component({ @@ -7,10 +7,11 @@ import {MyComp} from './a/multiple_components'; templateUrl: './basic.html', styles: ['.red { color: red }'], styleUrls: ['./basic.css'], - directives: [MyComp, FORM_DIRECTIVES, NgIf] + directives: [MyComp, FORM_DIRECTIVES, NgIf, NgFor] }) export class Basic { ctxProp: string; ctxBool: boolean; + ctxArr: any[] = []; constructor() { this.ctxProp = 'initialValue'; } } diff --git a/modules/@angular/compiler_cli/integrationtest/test/basic_spec.ts b/modules/@angular/compiler_cli/integrationtest/test/basic_spec.ts index 750e064db6..748b28c839 100644 --- a/modules/@angular/compiler_cli/integrationtest/test/basic_spec.ts +++ b/modules/@angular/compiler_cli/integrationtest/test/basic_spec.ts @@ -28,7 +28,14 @@ describe("template codegen output", () => { expect(fs.readFileSync(dtsOutput, {encoding: 'utf-8'})).toContain('Basic'); }); - it("should be able to create the basic component and trigger an ngIf", () => { + it("should be able to create the basic component", () => { + const appInjector = ReflectiveInjector.resolveAndCreate(BROWSER_APP_STATIC_PROVIDERS, + browserPlatform().injector); + var comp = BasicNgFactory.create(appInjector); + expect(comp.instance).toBeTruthy(); + }); + + it("should support ngIf", () => { const appInjector = ReflectiveInjector.resolveAndCreate(BROWSER_APP_STATIC_PROVIDERS, browserPlatform().injector); var comp = BasicNgFactory.create(appInjector); @@ -40,4 +47,19 @@ describe("template codegen output", () => { expect(debugElement.children.length).toBe(3); expect(debugElement.children[2].injector.get(MyComp)).toBeTruthy(); }); + + it("should support ngFor", () => { + const appInjector = ReflectiveInjector.resolveAndCreate(BROWSER_APP_STATIC_PROVIDERS, + browserPlatform().injector); + var comp = BasicNgFactory.create(appInjector); + var debugElement = getDebugNode(comp.location.nativeElement); + expect(debugElement.children.length).toBe(2); + + // test NgFor + comp.instance.ctxArr = [1, 2]; + comp.changeDetectorRef.detectChanges(); + expect(debugElement.children.length).toBe(4); + expect(debugElement.children[2].attributes['value']).toBe('1'); + expect(debugElement.children[3].attributes['value']).toBe('2'); + }); }); \ No newline at end of file diff --git a/modules/@angular/compiler_cli/src/static_reflection_capabilities.ts b/modules/@angular/compiler_cli/src/static_reflection_capabilities.ts index 91ca210a37..ab40bbed91 100644 --- a/modules/@angular/compiler_cli/src/static_reflection_capabilities.ts +++ b/modules/@angular/compiler_cli/src/static_reflection_capabilities.ts @@ -2,7 +2,7 @@ import {reflector} from '@angular/core'; import {ReflectionCapabilities} from './core_private'; import {StaticReflector} from './static_reflector'; -export class StaticAndDynamicReflectionCapabilities { +export class StaticAndDynamicReflectionCapabilities { static install(staticDelegate: StaticReflector) { reflector.updateCapabilities(new StaticAndDynamicReflectionCapabilities(staticDelegate)); } @@ -11,36 +11,30 @@ export class StaticAndDynamicReflectionCapabilities { constructor(private staticDelegate: StaticReflector) {} - isReflectionEnabled(): boolean { - return true; - } - factory(type: any): Function { - return this.dynamicDelegate.factory(type); - } - interfaces(type: any): any[] { - return this.dynamicDelegate.interfaces(type); + isReflectionEnabled(): boolean { return true; } + factory(type: any): Function { return this.dynamicDelegate.factory(type); } + interfaces(type: any): any[] { return this.dynamicDelegate.interfaces(type); } + hasLifecycleHook(type: any, lcInterface: /*Type*/ any, lcProperty: string): boolean { + return isStaticType(type) ? + this.staticDelegate.hasLifecycleHook(type, lcInterface, lcProperty) : + this.dynamicDelegate.hasLifecycleHook(type, lcInterface, lcProperty); } parameters(type: any): any[][] { - return isStaticType(type) ? this.staticDelegate.parameters(type) : this.dynamicDelegate.parameters(type); + return isStaticType(type) ? this.staticDelegate.parameters(type) : + this.dynamicDelegate.parameters(type); } annotations(type: any): any[] { - return isStaticType(type) ? this.staticDelegate.annotations(type) : this.dynamicDelegate.annotations(type); + return isStaticType(type) ? this.staticDelegate.annotations(type) : + this.dynamicDelegate.annotations(type); } propMetadata(typeOrFunc: any): {[key: string]: any[]} { - return isStaticType(typeOrFunc) ? this.staticDelegate.propMetadata(typeOrFunc) : this.dynamicDelegate.propMetadata(typeOrFunc); - } - getter(name: string) { - return this.dynamicDelegate.getter(name); - } - setter(name: string) { - return this.dynamicDelegate.setter(name); - } - method(name: string) { - return this.dynamicDelegate.method(name); - } - importUri(type: any): string { - return this.staticDelegate.importUri(type); + return isStaticType(typeOrFunc) ? this.staticDelegate.propMetadata(typeOrFunc) : + this.dynamicDelegate.propMetadata(typeOrFunc); } + getter(name: string) { return this.dynamicDelegate.getter(name); } + setter(name: string) { return this.dynamicDelegate.setter(name); } + method(name: string) { return this.dynamicDelegate.method(name); } + importUri(type: any): string { return this.staticDelegate.importUri(type); } } function isStaticType(type: any): boolean { diff --git a/modules/@angular/compiler_cli/src/static_reflector.ts b/modules/@angular/compiler_cli/src/static_reflector.ts index 2b3c4a5f2d..c2feed5ef1 100644 --- a/modules/@angular/compiler_cli/src/static_reflector.ts +++ b/modules/@angular/compiler_cli/src/static_reflector.ts @@ -114,7 +114,7 @@ export class StaticReflector implements ReflectorReader { public parameters(type: StaticSymbol): any[] { if (!(type instanceof StaticSymbol)) { - throw new Error(`parameters recieved ${JSON.stringify(type)} which is not a StaticSymbol`); + throw new Error(`parameters received ${JSON.stringify(type)} which is not a StaticSymbol`); } try { let parameters = this.parameterCache.get(type); @@ -124,8 +124,8 @@ export class StaticReflector implements ReflectorReader { let ctorData = members ? members['__ctor__'] : null; if (ctorData) { let ctor = (ctorData).find(a => a['__symbolic'] == 'constructor'); - let parameterTypes = this.simplify(type, ctor['parameters']); - let parameterDecorators = this.simplify(type, ctor['parameterDecorators']); + let parameterTypes = this.simplify(type, ctor['parameters'] || []); + let parameterDecorators = this.simplify(type, ctor['parameterDecorators'] || []); parameters = []; parameterTypes.forEach((paramType, index) => { @@ -152,6 +152,17 @@ export class StaticReflector implements ReflectorReader { } } + hasLifecycleHook(type: any, lcInterface: /*Type*/ any, lcProperty: string): boolean { + if (!(type instanceof StaticSymbol)) { + throw new Error( + `hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`); + } + let classMetadata = this.getTypeMetadata(type); + let members = classMetadata ? classMetadata['members'] : null; + let member:any[] = members ? members[lcProperty] : null; + return member ? member.some(a => a['__symbolic'] == 'method') : false; + } + private registerDecoratorOrConstructor(type: StaticSymbol, ctor: any): void { this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => { let argValues: any[] = []; diff --git a/modules/@angular/core/src/metadata/lifecycle_hooks.ts b/modules/@angular/core/src/metadata/lifecycle_hooks.ts index f6d8425a70..efe8b1c899 100644 --- a/modules/@angular/core/src/metadata/lifecycle_hooks.ts +++ b/modules/@angular/core/src/metadata/lifecycle_hooks.ts @@ -75,7 +75,7 @@ export var LIFECYCLE_HOOKS_VALUES = [ * bootstrap(App).catch(err => console.error(err)); * ``` */ -export interface OnChanges { ngOnChanges(changes: {[key: string]: SimpleChange}); } +export abstract class OnChanges { abstract ngOnChanges(changes: {[key: string]: SimpleChange}); } /** * Implement this interface to execute custom initialization logic after your directive's @@ -118,7 +118,7 @@ export interface OnChanges { ngOnChanges(changes: {[key: string]: SimpleChange}) * bootstrap(App).catch(err => console.error(err)); * ``` */ -export interface OnInit { ngOnInit(); } +export abstract class OnInit { abstract ngOnInit(); } /** * Implement this interface to override the default change detection algorithm for your directive. @@ -185,7 +185,7 @@ export interface OnInit { ngOnInit(); } * } * ``` */ -export interface DoCheck { ngDoCheck(); } +export abstract class DoCheck { abstract ngDoCheck(); } /** * Implement this interface to get notified when your directive is destroyed. @@ -276,7 +276,7 @@ export interface DoCheck { ngDoCheck(); } * every 50ms, until it reaches 0. * */ -export interface OnDestroy { ngOnDestroy(); } +export abstract class OnDestroy { abstract ngOnDestroy(); } /** * Implement this interface to get notified when your directive's content has been fully @@ -329,7 +329,7 @@ export interface OnDestroy { ngOnDestroy(); } * bootstrap(App).catch(err => console.error(err)); * ``` */ -export interface AfterContentInit { ngAfterContentInit(); } +export abstract class AfterContentInit { abstract ngAfterContentInit(); } /** * Implement this interface to get notified after every check of your directive's content. @@ -377,7 +377,7 @@ export interface AfterContentInit { ngAfterContentInit(); } * bootstrap(App).catch(err => console.error(err)); * ``` */ -export interface AfterContentChecked { ngAfterContentChecked(); } +export abstract class AfterContentChecked { abstract ngAfterContentChecked(); } /** * Implement this interface to get notified when your component's view has been fully initialized. @@ -424,7 +424,7 @@ export interface AfterContentChecked { ngAfterContentChecked(); } * bootstrap(App).catch(err => console.error(err)); * ``` */ -export interface AfterViewInit { ngAfterViewInit(); } +export abstract class AfterViewInit { abstract ngAfterViewInit(); } /** * Implement this interface to get notified after every check of your component's view. @@ -474,4 +474,4 @@ export interface AfterViewInit { ngAfterViewInit(); } * bootstrap(App).catch(err => console.error(err)); * ``` */ -export interface AfterViewChecked { ngAfterViewChecked(); } +export abstract class AfterViewChecked { abstract ngAfterViewChecked(); } diff --git a/modules/@angular/core/src/reflection/platform_reflection_capabilities.ts b/modules/@angular/core/src/reflection/platform_reflection_capabilities.ts index c28ec060cc..3ecb6a6573 100644 --- a/modules/@angular/core/src/reflection/platform_reflection_capabilities.ts +++ b/modules/@angular/core/src/reflection/platform_reflection_capabilities.ts @@ -5,6 +5,7 @@ export interface PlatformReflectionCapabilities { isReflectionEnabled(): boolean; factory(type: Type): Function; interfaces(type: Type): any[]; + hasLifecycleHook(type: any, lcInterface: /*Type*/ any, lcProperty: string): boolean; parameters(type: any): any[][]; annotations(type: any): any[]; propMetadata(typeOrFunc: any): {[key: string]: any[]}; diff --git a/modules/@angular/core/src/reflection/reflection_capabilities.dart b/modules/@angular/core/src/reflection/reflection_capabilities.dart index 7082065057..49fbeeee69 100644 --- a/modules/@angular/core/src/reflection/reflection_capabilities.dart +++ b/modules/@angular/core/src/reflection/reflection_capabilities.dart @@ -291,6 +291,11 @@ class ReflectionCapabilities implements PlatformReflectionCapabilities { return name.endsWith("=") ? name.substring(0, name.length - 1) : name; } + bool hasLifecycleHook(dynamic type, Type lcInterface, String lcProperty) { + if (type is! Type) return false; + return this.interfaces(type).contains(lcInterface); + } + List interfaces(type) { final clazz = reflectType(type); _assertDeclaresLifecycleHooks(clazz); diff --git a/modules/@angular/core/src/reflection/reflection_capabilities.ts b/modules/@angular/core/src/reflection/reflection_capabilities.ts index 525c7ec332..e009b8a7e7 100644 --- a/modules/@angular/core/src/reflection/reflection_capabilities.ts +++ b/modules/@angular/core/src/reflection/reflection_capabilities.ts @@ -197,8 +197,16 @@ export class ReflectionCapabilities implements PlatformReflectionCapabilities { return {}; } - interfaces(type: Type): any[] { - throw new BaseException("JavaScript does not support interfaces"); + // Note: JavaScript does not support to query for interfaces during runtime. + // However, we can't throw here as the reflector will always call this method + // when asked for a lifecycle interface as this is what we check in Dart. + interfaces(type: Type): any[] { return []; } + + hasLifecycleHook(type: any, lcInterface: Type, lcProperty: string): boolean { + if (!(type instanceof Type)) return false; + + var proto = (type).prototype; + return !!proto[lcProperty]; } getter(name: string): GetterFn { return new Function('o', 'return o.' + name + ';'); } diff --git a/modules/@angular/core/src/reflection/reflector.ts b/modules/@angular/core/src/reflection/reflector.ts index 56d1984be0..ad5d945d5c 100644 --- a/modules/@angular/core/src/reflection/reflector.ts +++ b/modules/@angular/core/src/reflection/reflector.ts @@ -45,9 +45,7 @@ export class Reflector extends ReflectorReader { this.reflectionCapabilities = reflectionCapabilities; } - updateCapabilities(caps: PlatformReflectionCapabilities) { - this.reflectionCapabilities = caps; - } + updateCapabilities(caps: PlatformReflectionCapabilities) { this.reflectionCapabilities = caps; } isReflectionEnabled(): boolean { return this.reflectionCapabilities.isReflectionEnabled(); } @@ -120,7 +118,7 @@ export class Reflector extends ReflectorReader { } } - interfaces(type: Type): any[] { + interfaces(type: /*Type*/ any): any[] { if (this._injectableInfo.has(type)) { var res = this._getReflectionInfo(type).interfaces; return isPresent(res) ? res : []; @@ -129,6 +127,15 @@ export class Reflector extends ReflectorReader { } } + hasLifecycleHook(type: any, lcInterface: /*Type*/ any, lcProperty: string): boolean { + var interfaces = this.interfaces(type); + if (interfaces.indexOf(lcInterface) !== -1) { + return true; + } else { + return this.reflectionCapabilities.hasLifecycleHook(type, lcInterface, lcProperty); + } + } + getter(name: string): GetterFn { if (this._getters.has(name)) { return this._getters.get(name); diff --git a/modules/@angular/integration_test/public_api_spec.ts b/modules/@angular/integration_test/public_api_spec.ts index 5a7093cd70..2804b5d10a 100644 --- a/modules/@angular/integration_test/public_api_spec.ts +++ b/modules/@angular/integration_test/public_api_spec.ts @@ -297,18 +297,18 @@ var CORE: string[] = [ 'resolveForwardRef:js', 'PLATFORM_COMMON_PROVIDERS', 'PLATFORM_INITIALIZER', - 'AfterContentChecked:dart', - 'AfterContentInit:dart', - 'AfterViewChecked:dart', - 'AfterViewInit:dart', - 'DoCheck:dart', + 'AfterContentChecked', + 'AfterContentInit', + 'AfterViewChecked', + 'AfterViewInit', + 'DoCheck', 'IterableDifferFactory:dart', 'IterableDiffer:dart', 'KeyValueDifferFactory:dart', 'KeyValueDiffer:dart', - 'OnChanges:dart', - 'OnDestroy:dart', - 'OnInit:dart', + 'OnChanges', + 'OnDestroy', + 'OnInit', 'PipeTransform:dart', 'reflector', 'Stream:dart',