refactor(core): change module semantics

This contains major changes to the compiler, bootstrap of the platforms
and test environment initialization.

Main part of #10043
Closes #10164

BREAKING CHANGE:
- Semantics and name of `@AppModule` (now `@NgModule`) changed quite a bit.
  This is actually not breaking as `@AppModules` were not part of rc.4.
  We will have detailed docs on `@NgModule` separately.
- `coreLoadAndBootstrap` and `coreBootstrap` can't be used any more (without migration support).
  Use `bootstrapModule` / `bootstrapModuleFactory` instead.
- All Components listed in routes have to be part of the `declarations` of an NgModule.
  Either directly on the bootstrap module / lazy loaded module, or in an NgModule imported by them.
This commit is contained in:
Tobias Bosch
2016-07-18 03:50:31 -07:00
parent ca16fc29a6
commit 46b212706b
129 changed files with 3580 additions and 3366 deletions

View File

@ -8,10 +8,7 @@
import {Component} from '@angular/core';
@Component({
selector: 'my-comp',
template: '<div></div>',
})
@Component({selector: 'my-comp', template: '<div></div>', directives: [NextComp]})
export class MultipleComponentsMyComp {
}

View File

@ -1,4 +1,4 @@
<div [attr.array]="[0]" [attr.map]="{a:1}" title="translate me" i18n-title="meaning|desc">{{ctxProp}}</div>
<form><input type="button" [(ngModel)]="ctxProp"/></form>
<form><input type="button" [(ngModel)]="ctxProp" name="first"/></form>
<my-comp *ngIf="ctxBool"></my-comp>
<div *ngFor="let x of ctxArr" [attr.value]="x"></div>

View File

@ -6,8 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {FORM_DIRECTIVES, NgFor, NgIf} from '@angular/common';
import {NgFor, NgIf} from '@angular/common';
import {Component, Inject} from '@angular/core';
import {FORM_DIRECTIVES} from '@angular/forms';
import {MultipleComponentsMyComp} from './a/multiple_components';

View File

@ -10,4 +10,4 @@ import {browserPlatform} from '@angular/platform-browser';
import {BasicComp} from './basic';
import {MainModuleNgFactory} from './module.ngfactory';
MainModuleNgFactory.create().instance.appRef.bootstrap(BasicComp);
MainModuleNgFactory.create(null).instance.appRef.bootstrap(BasicComp);

View File

@ -6,20 +6,29 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AppModule, ApplicationRef} from '@angular/core';
import {ApplicationRef, NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {AnimateCmp} from './animate';
import {BasicComp} from './basic';
import {CompWithProviders, CompWithReferences} from './features';
import {CompUsingRootModuleDirectiveAndPipe, SomeDirectiveInRootModule, SomeLibModule, SomePipeInRootModule, SomeService} from './module_fixtures';
import {CompWithAnalyzePrecompileProvider, CompWithPrecompile} from './precompile';
import {ProjectingComp} from './projection';
import {CompWithChildQuery} from './queries';
import {CompWithChildQuery, CompWithDirectiveChild} from './queries';
@AppModule({
modules: [BrowserModule],
@NgModule({
declarations: [
SomeDirectiveInRootModule, SomePipeInRootModule, AnimateCmp, BasicComp, CompWithPrecompile,
CompWithAnalyzePrecompileProvider, ProjectingComp, CompWithChildQuery, CompWithDirectiveChild,
CompUsingRootModuleDirectiveAndPipe, CompWithProviders, CompWithReferences
],
imports: [BrowserModule, FormsModule, SomeLibModule],
providers: [SomeService],
precompile: [
AnimateCmp, BasicComp, CompWithPrecompile, CompWithAnalyzePrecompileProvider, ProjectingComp,
CompWithChildQuery
CompWithChildQuery, CompUsingRootModuleDirectiveAndPipe
]
})
export class MainModule {

View File

@ -7,7 +7,7 @@
*/
import {LowerCasePipe, NgIf} from '@angular/common';
import {ANALYZE_FOR_PRECOMPILE, AppModule, Component, ComponentFactoryResolver, Directive, Inject, Injectable, Input, OpaqueToken, Pipe} from '@angular/core';
import {ANALYZE_FOR_PRECOMPILE, Component, ComponentFactoryResolver, Directive, Inject, Injectable, Input, NgModule, OpaqueToken, Pipe} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
@Injectable()
@ -16,50 +16,37 @@ export class SomeService {
}
@Injectable()
export class NestedService {
export class ServiceUsingLibModule {
}
@Directive({selector: '[someDir]', host: {'[title]': 'someDir'}})
export class SomeDirective {
export class SomeDirectiveInRootModule {
@Input()
someDir: string;
}
@Directive({selector: '[someDir]', host: {'[title]': 'someDir'}})
export class SomeDirectiveInLibModule {
@Input()
someDir: string;
}
@Pipe({name: 'somePipe'})
export class SomePipe {
export class SomePipeInRootModule {
transform(value: string): any { return `transformed ${value}`; }
}
@Component({selector: 'cmp', template: `<div [someDir]="'someValue' | somePipe"></div>`})
export class SomeComp {
constructor() {}
@Pipe({name: 'somePipe'})
export class SomePipeInLibModule {
transform(value: string): any { return `transformed ${value}`; }
}
@Component({selector: 'parent', template: `<cmp></cmp>`, directives: [SomeComp]})
export class ParentComp {
@Component({selector: 'comp', template: `<div [someDir]="'someValue' | somePipe"></div>`})
export class CompUsingRootModuleDirectiveAndPipe {
}
@AppModule({providers: [NestedService]})
export class NestedModule {
}
@AppModule({
directives: [SomeDirective],
pipes: [SomePipe],
providers: [SomeService],
precompile: [SomeComp],
modules: [NestedModule, BrowserModule]
})
export class SomeModule {
}
@AppModule({
directives: [SomeDirective],
pipes: [SomePipe],
precompile: [ParentComp],
modules: [BrowserModule]
})
export class SomeModuleUsingParentComp {
@Component({selector: 'comp', template: `<div [someDir]="'someValue' | somePipe"></div>`})
export class CompUsingLibModuleDirectiveAndPipe {
}
export const SOME_TOKEN = new OpaqueToken('someToken');
@ -71,7 +58,13 @@ export function provideValueWithPrecompile(value: any) {
];
}
@AppModule({providers: [provideValueWithPrecompile([{a: 'b', component: SomeComp}])]})
export class SomeModuleWithAnalyzePrecompileProvider {
constructor(@Inject(SOME_TOKEN) public providedValue: any) {}
@NgModule({
declarations: [SomeDirectiveInLibModule, SomePipeInLibModule, CompUsingLibModuleDirectiveAndPipe],
precompile: [CompUsingLibModuleDirectiveAndPipe],
providers: [
ServiceUsingLibModule,
provideValueWithPrecompile([{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}])
],
})
export class SomeLibModule {
}

View File

@ -1,62 +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 './init';
import {NestedModule, NestedService, ParentComp, SomeComp, SomeModule, SomeService} from '../src/module_fixtures';
import {SomeModuleNgFactory, SomeModuleUsingParentCompNgFactory, SomeModuleWithAnalyzePrecompileProviderNgFactory} from '../src/module_fixtures.ngfactory';
import {createComponent, createModule} from './util';
describe('AppModule', () => {
it('should support providers', () => {
var moduleRef = createModule(SomeModuleNgFactory);
expect(moduleRef.instance instanceof SomeModule).toBe(true);
expect(moduleRef.injector.get(SomeModule) instanceof SomeModule).toBe(true);
expect(moduleRef.injector.get(SomeService) instanceof SomeService).toBe(true);
});
it('should support precompile components', () => {
var moduleRef = createModule(SomeModuleNgFactory);
var cf = moduleRef.componentFactoryResolver.resolveComponentFactory(SomeComp);
expect(cf.componentType).toBe(SomeComp);
var compRef = cf.create(moduleRef.injector);
expect(compRef.instance instanceof SomeComp).toBe(true);
});
it('should support precompile via the ANALYZE_FOR_PRECOMPILE provider and function providers in components',
() => {
const moduleRef = createModule(SomeModuleWithAnalyzePrecompileProviderNgFactory);
const cf = moduleRef.componentFactoryResolver.resolveComponentFactory(SomeComp);
expect(cf.componentType).toBe(SomeComp);
// check that the function call that created the provider for ANALYZE_FOR_PRECOMPILE worked.
expect(moduleRef.instance.providedValue).toEqual([{a: 'b', component: SomeComp}]);
});
it('should support module directives and pipes', () => {
var compFixture = createComponent(SomeComp, SomeModuleNgFactory);
compFixture.detectChanges();
var debugElement = compFixture.debugElement;
expect(debugElement.children[0].properties['title']).toBe('transformed someValue');
});
it('should support module directives and pipes on nested components', () => {
var compFixture = createComponent(ParentComp, SomeModuleUsingParentCompNgFactory);
compFixture.detectChanges();
var debugElement = compFixture.debugElement;
debugElement = debugElement.children[0];
expect(debugElement.children[0].properties['title']).toBe('transformed someValue');
});
it('should support child moduless', () => {
var moduleRef = createModule(SomeModuleNgFactory);
expect(moduleRef.instance instanceof SomeModule).toBe(true);
expect(moduleRef.injector.get(NestedModule) instanceof NestedModule).toBe(true);
expect(moduleRef.injector.get(NestedService) instanceof NestedService).toBe(true);
});
});

View File

@ -0,0 +1,63 @@
/**
* @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 './init';
import {MainModule} from '../src/module';
import {CompUsingLibModuleDirectiveAndPipe, CompUsingRootModuleDirectiveAndPipe, SOME_TOKEN, ServiceUsingLibModule, SomeLibModule, SomeService} from '../src/module_fixtures';
import {createComponent, createModule} from './util';
describe('NgModule', () => {
it('should support providers', () => {
const moduleRef = createModule();
expect(moduleRef.instance instanceof MainModule).toBe(true);
expect(moduleRef.injector.get(MainModule) instanceof MainModule).toBe(true);
expect(moduleRef.injector.get(SomeService) instanceof SomeService).toBe(true);
});
it('should support precompile components', () => {
const moduleRef = createModule();
const cf = moduleRef.componentFactoryResolver.resolveComponentFactory(
CompUsingRootModuleDirectiveAndPipe);
expect(cf.componentType).toBe(CompUsingRootModuleDirectiveAndPipe);
const compRef = cf.create(moduleRef.injector);
expect(compRef.instance instanceof CompUsingRootModuleDirectiveAndPipe).toBe(true);
});
it('should support precompile via the ANALYZE_FOR_PRECOMPILE provider and function providers in components',
() => {
const moduleRef = createModule();
const cf = moduleRef.componentFactoryResolver.resolveComponentFactory(
CompUsingRootModuleDirectiveAndPipe);
expect(cf.componentType).toBe(CompUsingRootModuleDirectiveAndPipe);
// check that the function call that created the provider for ANALYZE_FOR_PRECOMPILE worked.
expect(moduleRef.injector.get(SOME_TOKEN)).toEqual([
{a: 'b', component: CompUsingLibModuleDirectiveAndPipe}
]);
});
it('should support module directives and pipes', () => {
const compFixture = createComponent(CompUsingRootModuleDirectiveAndPipe);
compFixture.detectChanges();
const debugElement = compFixture.debugElement;
expect(debugElement.children[0].properties['title']).toBe('transformed someValue');
});
it('should support module directives and pipes on lib modules', () => {
const compFixture = createComponent(CompUsingLibModuleDirectiveAndPipe);
compFixture.detectChanges();
const debugElement = compFixture.debugElement;
expect(debugElement.children[0].properties['title']).toBe('transformed someValue');
expect(debugElement.injector.get(SomeLibModule) instanceof SomeLibModule).toBe(true);
expect(debugElement.injector.get(ServiceUsingLibModule) instanceof ServiceUsingLibModule)
.toBe(true);
});
});

View File

@ -6,23 +6,19 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AppModuleFactory, AppModuleRef, bootstrapModuleFactory} from '@angular/core';
import {NgModuleFactory, NgModuleRef, bootstrapModuleFactory} from '@angular/core';
import {ComponentFixture} from '@angular/core/testing';
import {serverPlatform} from '@angular/platform-server';
import {MainModule} from '../src/module';
import {MainModuleNgFactory} from '../src/module.ngfactory';
export function createModule<M>(factory: AppModuleFactory<M>): AppModuleRef<M> {
return bootstrapModuleFactory(factory, serverPlatform());
export function createModule(): NgModuleRef<MainModule> {
return bootstrapModuleFactory(MainModuleNgFactory, serverPlatform());
}
export function createComponent<C>(
comp: {new (...args: any[]): C},
moduleFactory: AppModuleFactory<any> = null): ComponentFixture<C> {
if (!moduleFactory) {
moduleFactory = MainModuleNgFactory;
}
const moduleRef = createModule(moduleFactory);
export function createComponent<C>(comp: {new (...args: any[]): C}): ComponentFixture<C> {
const moduleRef = createModule();
const compRef =
moduleRef.componentFactoryResolver.resolveComponentFactory(comp).create(moduleRef.injector);
return new ComponentFixture(compRef, null, null);