diff --git a/modules/@angular/upgrade/src/static/component_info.ts b/modules/@angular/upgrade/src/common/component_info.ts similarity index 99% rename from modules/@angular/upgrade/src/static/component_info.ts rename to modules/@angular/upgrade/src/common/component_info.ts index a9dc0c3a74..130f8f2589 100644 --- a/modules/@angular/upgrade/src/static/component_info.ts +++ b/modules/@angular/upgrade/src/common/component_info.ts @@ -44,4 +44,4 @@ export class PropertyBinding { this.bindAttr = `bind${capitalAttr}`; this.bindonAttr = `bindon${capitalAttr}`; } -} \ No newline at end of file +} diff --git a/modules/@angular/upgrade/src/static/downgrade_component.ts b/modules/@angular/upgrade/src/common/downgrade_component.ts similarity index 98% rename from modules/@angular/upgrade/src/static/downgrade_component.ts rename to modules/@angular/upgrade/src/common/downgrade_component.ts index c333688d15..7db8c71343 100644 --- a/modules/@angular/upgrade/src/static/downgrade_component.ts +++ b/modules/@angular/upgrade/src/common/downgrade_component.ts @@ -8,9 +8,8 @@ import {ComponentFactory, ComponentFactoryResolver, Injector, Type} from '@angular/core'; -import * as angular from '../common/angular1'; -import {$INJECTOR, $PARSE, INJECTOR_KEY, REQUIRE_NG_MODEL} from '../common/constants'; - +import * as angular from './angular1'; +import {$INJECTOR, $PARSE, INJECTOR_KEY, REQUIRE_NG_MODEL} from './constants'; import {DowngradeComponentAdapter} from './downgrade_component_adapter'; let downgradeCount = 0; diff --git a/modules/@angular/upgrade/src/static/downgrade_component_adapter.ts b/modules/@angular/upgrade/src/common/downgrade_component_adapter.ts similarity index 98% rename from modules/@angular/upgrade/src/static/downgrade_component_adapter.ts rename to modules/@angular/upgrade/src/common/downgrade_component_adapter.ts index e2c97df327..59987739fa 100644 --- a/modules/@angular/upgrade/src/static/downgrade_component_adapter.ts +++ b/modules/@angular/upgrade/src/common/downgrade_component_adapter.ts @@ -8,11 +8,10 @@ import {ChangeDetectorRef, ComponentFactory, ComponentRef, EventEmitter, Injector, OnChanges, ReflectiveInjector, SimpleChange, SimpleChanges, Type} from '@angular/core'; -import * as angular from '../common/angular1'; -import {$SCOPE} from '../common/constants'; +import * as angular from './angular1'; import {hookupNgModel} from '../common/util'; - import {ComponentInfo, PropertyBinding} from './component_info'; +import {$SCOPE} from './constants'; const INITIAL_VALUE = { __UNINITIALIZED__: true diff --git a/modules/@angular/upgrade/src/dynamic/metadata.ts b/modules/@angular/upgrade/src/dynamic/metadata.ts index 8de96b4b8a..e842051f55 100644 --- a/modules/@angular/upgrade/src/dynamic/metadata.ts +++ b/modules/@angular/upgrade/src/dynamic/metadata.ts @@ -9,63 +9,32 @@ import {DirectiveResolver} from '@angular/compiler'; import {Directive, Type} from '@angular/core'; +import {PropertyBinding} from '../common/component_info'; + + const COMPONENT_SELECTOR = /^[\w|-]*$/; const SKEWER_CASE = /-(\w)/g; const directiveResolver = new DirectiveResolver(); -export interface AttrProp { - prop: string; - attr: string; - bracketAttr: string; - bracketParenAttr: string; - parenAttr: string; - onAttr: string; - bindAttr: string; - bindonAttr: string; -} - export interface ComponentInfo { type: Type; selector: string; - inputs?: AttrProp[]; - outputs?: AttrProp[]; + inputs?: PropertyBinding[]; + outputs?: PropertyBinding[]; } export function getComponentInfo(type: Type): ComponentInfo { const resolvedMetadata: Directive = directiveResolver.resolve(type); - let selector = resolvedMetadata.selector; - if (!selector.match(COMPONENT_SELECTOR)) { - throw new Error('Only selectors matching element names are supported, got: ' + selector); - } - selector = selector.replace( - SKEWER_CASE, (all: any /** TODO #9100 */, letter: string) => letter.toUpperCase()); + const selector = resolvedMetadata.selector; + return { - type: type, - selector: selector, + type, + selector, inputs: parseFields(resolvedMetadata.inputs), outputs: parseFields(resolvedMetadata.outputs) }; } -export function parseFields(names: string[]): AttrProp[] { - const attrProps: AttrProp[] = []; - if (names) { - for (let i = 0; i < names.length; i++) { - const parts = names[i].split(':'); - const prop = parts[0].trim(); - const attr = (parts[1] || parts[0]).trim(); - const capitalAttr = attr.charAt(0).toUpperCase() + attr.substr(1); - attrProps.push({ - prop: prop, - attr: attr, - bracketAttr: `[${attr}]`, - parenAttr: `(${attr})`, - bracketParenAttr: `[(${attr})]`, - onAttr: `on${capitalAttr}`, - bindAttr: `bind${capitalAttr}`, - bindonAttr: `bindon${capitalAttr}` - }); - } - } - return attrProps; +export function parseFields(bindings: string[]): PropertyBinding[] { + return (bindings || []).map(binding => new PropertyBinding(binding)); } diff --git a/modules/@angular/upgrade/src/dynamic/upgrade_adapter.ts b/modules/@angular/upgrade/src/dynamic/upgrade_adapter.ts index fcee9d6998..5353641327 100644 --- a/modules/@angular/upgrade/src/dynamic/upgrade_adapter.ts +++ b/modules/@angular/upgrade/src/dynamic/upgrade_adapter.ts @@ -112,7 +112,7 @@ export class UpgradeAdapter { * @internal */ private ng1ComponentsToBeUpgraded: {[name: string]: UpgradeNg1ComponentAdapterBuilder} = {}; - private providers: Provider[] = []; + private upgradedProviders: Provider[] = []; private ngZone: NgZone; private ng1Module: angular.IModule; private moduleRef: NgModuleRef = null; @@ -437,11 +437,11 @@ export class UpgradeAdapter { * * ``` */ - public upgradeNg1Provider(name: string, options?: {asToken: any}) { + upgradeNg1Provider(name: string, options?: {asToken: any}) { const token = options && options.asToken || name; - this.providers.push({ + this.upgradedProviders.push({ provide: token, - useFactory: (ng1Injector: angular.IInjectorService) => ng1Injector.get(name), + useFactory: ($injector: angular.IInjectorService) => $injector.get(name), deps: [$INJECTOR] }); } @@ -557,7 +557,7 @@ export class UpgradeAdapter { providers: [ {provide: $INJECTOR, useFactory: () => ng1Injector}, {provide: $COMPILE, useFactory: () => ng1Injector.get($COMPILE)}, - this.providers + this.upgradedProviders ], imports: [this.ng2AppModule] }).Class({ diff --git a/modules/@angular/upgrade/static.ts b/modules/@angular/upgrade/static.ts index 1d2db00603..6a3760fbb0 100644 --- a/modules/@angular/upgrade/static.ts +++ b/modules/@angular/upgrade/static.ts @@ -12,8 +12,8 @@ * Entry point for all public APIs of the upgrade/static package, allowing * Angular 1 and Angular 2+ to run side by side in the same application. */ +export {downgradeComponent} from './src/common/downgrade_component'; export {downgradeInjectable} from './src/common/downgrade_injectable'; -export {downgradeComponent} from './src/static/downgrade_component'; export {UpgradeComponent} from './src/static/upgrade_component'; export {UpgradeModule} from './src/static/upgrade_module'; diff --git a/modules/@angular/upgrade/test/static/component_info_spec.ts b/modules/@angular/upgrade/test/common/component_info_spec.ts similarity index 96% rename from modules/@angular/upgrade/test/static/component_info_spec.ts rename to modules/@angular/upgrade/test/common/component_info_spec.ts index fc71925525..b4b13f2fe4 100644 --- a/modules/@angular/upgrade/test/static/component_info_spec.ts +++ b/modules/@angular/upgrade/test/common/component_info_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {PropertyBinding} from '@angular/upgrade/src/aot/component_info'; +import {PropertyBinding} from '@angular/upgrade/src/common/component_info'; export function main() { describe('PropertyBinding', () => { diff --git a/modules/@angular/upgrade/test/dynamic/metadata_spec.ts b/modules/@angular/upgrade/test/dynamic/metadata_spec.ts index 95c4b2c94d..5975d8b559 100644 --- a/modules/@angular/upgrade/test/dynamic/metadata_spec.ts +++ b/modules/@angular/upgrade/test/dynamic/metadata_spec.ts @@ -7,23 +7,17 @@ */ import {Component} from '@angular/core'; -import {describe, expect, it} from '@angular/core/testing/testing_internal'; import {getComponentInfo, parseFields} from '@angular/upgrade/src/dynamic/metadata'; export function main() { describe('upgrade metadata', () => { it('should extract component selector', () => { - expect(getComponentInfo(ElementNameComponent).selector).toEqual('elementNameDashed'); + expect(getComponentInfo(ElementNameComponent).selector).toBe('element-name-dashed'); }); describe('errors', () => { it('should throw on missing selector', () => { - expect(() => getComponentInfo(AttributeNameComponent)) - .toThrowError('Only selectors matching element names are supported, got: [attr-name]'); - }); - - it('should throw on non element names', () => { expect(() => getComponentInfo(NoAnnotationComponent)) .toThrowError('No Directive annotation found on NoAnnotationComponent'); }); @@ -34,7 +28,7 @@ export function main() { it('should process values', () => { expect(parseFields([' name ', ' prop : attr '])).toEqual([ - { + jasmine.objectContaining({ prop: 'name', attr: 'name', bracketAttr: '[name]', @@ -43,8 +37,8 @@ export function main() { onAttr: 'onName', bindAttr: 'bindName', bindonAttr: 'bindonName' - }, - { + }), + jasmine.objectContaining({ prop: 'prop', attr: 'attr', bracketAttr: '[attr]', @@ -53,7 +47,7 @@ export function main() { onAttr: 'onAttr', bindAttr: 'bindAttr', bindonAttr: 'bindonAttr' - } + }) ]); }); }); diff --git a/modules/@angular/upgrade/test/dynamic/upgrade_spec.ts b/modules/@angular/upgrade/test/dynamic/upgrade_spec.ts index 157be74c7a..a5e66adaf4 100644 --- a/modules/@angular/upgrade/test/dynamic/upgrade_spec.ts +++ b/modules/@angular/upgrade/test/dynamic/upgrade_spec.ts @@ -271,6 +271,25 @@ export function main() { }); describe('downgrade ng2 component', () => { + it('should allow non-element selectors for downgraded components', async(() => { + @Component({selector: '[itWorks]', template: 'It works'}) + class WorksComponent { + } + + @NgModule({declarations: [WorksComponent], imports: [BrowserModule]}) + class Ng2Module { + } + + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); + const ng1Module = angular.module('ng1', []); + ng1Module.directive('ng2', adapter.downgradeNg2Component(WorksComponent)); + + const element = html(''); + adapter.bootstrap(element, ['ng1']).ready((ref) => { + expect(multiTrim(document.body.textContent)).toBe('It works'); + }); + })); + it('should bind properties, events', async(() => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); const ng1Module = angular.module('ng1', []); @@ -458,7 +477,6 @@ export function main() { }); })); - it('should fallback to the root ng2.injector when compiled outside the dom', async(() => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); const ng1Module = angular.module('ng1', []); diff --git a/modules/@angular/upgrade/test/static/integration/downgrade_component_spec.ts b/modules/@angular/upgrade/test/static/integration/downgrade_component_spec.ts index 3670228b63..3bc42020e3 100644 --- a/modules/@angular/upgrade/test/static/integration/downgrade_component_spec.ts +++ b/modules/@angular/upgrade/test/static/integration/downgrade_component_spec.ts @@ -273,6 +273,30 @@ export function main() { }); })); + it('should allow attribute selectors for downgraded components', async(() => { + @Component({selector: '[itWorks]', template: 'It works'}) + class WorksComponent { + } + + @NgModule({ + declarations: [WorksComponent], + entryComponents: [WorksComponent], + imports: [BrowserModule, UpgradeModule] + }) + class Ng2Module { + ngDoBootstrap() {} + } + + const ng1Module = angular.module('ng1', []).directive( + 'worksComponent', downgradeComponent({component: WorksComponent})); + + const element = html(''); + + bootstrap(platformBrowserDynamic(), Ng2Module, element, ng1Module).then((upgrade) => { + expect(multiTrim(document.body.textContent)).toBe('It works'); + }); + })); + it('should allow attribute selectors for components in ng2', async(() => { @Component({selector: '[itWorks]', template: 'It works'}) class WorksComponent {