feat(browser): use AppModules for bootstrap in the browser

This introduces the `BrowserModule` to be used for long form
bootstrap and offline compile bootstrap:

```
@AppModule({
  modules: [BrowserModule],
  precompile: [MainComponent],
  providers: […], // additional providers
  directives: […], // additional platform directives
  pipes: […] // additional platform pipes
})
class MyModule {
  constructor(appRef: ApplicationRef) {
    appRef.bootstrap(MainComponent);
  }
}

// offline compile
import {bootstrapModuleFactory} from ‘@angular/platform-browser’;
bootstrapModuleFactory(MyModuleNgFactory);

// runtime compile long form
import {bootstrapModule} from ‘@angular/platform-browser-dynamic’;
bootstrapModule(MyModule);
```

The short form, `bootstrap(...)`, can now creates a module on the fly,
given `directives`, `pipes, `providers`, `precompile` and `modules`
properties.

Related changes:
- make `SanitizationService`, `SecurityContext` public in `@angular/core` so that the offline compiler can resolve the token
- move `AnimationDriver` to `platform-browser` and make it
  public so that the offline compiler can resolve the token

BREAKING CHANGES:
- short form bootstrap does no longer allow
  to inject compiler internals (i.e. everything 
  from `@angular/compiler). Inject `Compiler` instead.
  To provide custom providers for the compiler,
  create a custom compiler via `browserCompiler({providers: [...]})`
  and pass that into the `bootstrap` method.
This commit is contained in:
Tobias Bosch
2016-06-30 13:07:17 -07:00
parent 74b45dfbf8
commit 3f55aa609f
71 changed files with 793 additions and 406 deletions

View File

@ -1,8 +1,8 @@
import {LowerCasePipe, NgIf} from '@angular/common';
import {CompilerConfig} from '@angular/compiler';
import {AppModule, AppModuleMetadata, Compiler, Component, ComponentFactoryResolver, ComponentRef, DebugElement, Host, Inject, Injectable, Injector, OpaqueToken, Optional, Provider, SelfMetadata, SkipSelf, SkipSelfMetadata, forwardRef, getDebugNode, provide} from '@angular/core';
import {AppModule, AppModuleMetadata, Compiler, Component, ComponentFactoryResolver, ComponentRef, ComponentResolver, DebugElement, Host, Inject, Injectable, Injector, OpaqueToken, Optional, Provider, SelfMetadata, SkipSelf, SkipSelfMetadata, forwardRef, getDebugNode, provide} from '@angular/core';
import {ComponentFixture} from '@angular/core/testing';
import {beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
import {BaseException} from '../../src/facade/exceptions';
import {ConcreteType, IS_DART, Type, stringify} from '../../src/facade/lang';
@ -195,6 +195,26 @@ function declareTests({useJit}: {useJit: boolean}) {
}));
checkNgIfAndLowerCasePipe(compFixture, compFixture.debugElement.children[0]);
});
it('should provide a Compiler instance that uses the directives/pipes of the module', () => {
let appModule = compiler.compileAppModuleSync(ModuleWithDirectivesAndPipes).create();
let boundCompiler: Compiler = appModule.injector.get(Compiler);
var cf = boundCompiler.compileComponentSync(CompUsingModuleDirectiveAndPipe);
let compFixture = new ComponentFixture(cf.create(injector), null, false);
checkNgIfAndLowerCasePipe(compFixture, compFixture.debugElement);
});
it('should provide a ComponentResolver instance that uses the directives/pipes of the module',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
let appModule = compiler.compileAppModuleSync(ModuleWithDirectivesAndPipes).create();
let boundCompiler: ComponentResolver = appModule.injector.get(ComponentResolver);
boundCompiler.resolveComponent(CompUsingModuleDirectiveAndPipe).then((cf) => {
let compFixture = new ComponentFixture(cf.create(injector), null, false);
checkNgIfAndLowerCasePipe(compFixture, compFixture.debugElement);
async.done();
});
}));
});
describe('providers', function() {
@ -471,6 +491,19 @@ function declareTests({useJit}: {useJit: boolean}) {
expect(injector.get('a')).toBe('aValue');
expect(injector.get('someToken')).toBe('someValue');
});
it('should override the providers of nested modules', () => {
var injector = compiler
.compileAppModuleSync(
SomeModule, new AppModuleMetadata({
providers: [{provide: 'someToken', useValue: 'someNewValue'}],
modules: [ModuleWithProvider]
}))
.create()
.injector;
expect(injector.get('someToken')).toBe('someNewValue');
});
});
});

View File

@ -40,6 +40,10 @@ export function main() {
var renderLog: RenderLog;
var directiveLog: DirectiveLog;
function createCompFixture<T>(template: string): ComponentFixture<TestComponent>;
function createCompFixture<T>(template: string, compType: ConcreteType<T>): ComponentFixture<T>;
function createCompFixture<T>(
template: string, compType: ConcreteType<T>, _tcb: TestComponentBuilder): ComponentFixture<T>;
function createCompFixture<T>(
template: string, compType: ConcreteType<T> = <any>TestComponent,
_tcb: TestComponentBuilder = null): ComponentFixture<T> {
@ -58,19 +62,23 @@ export function main() {
return nodes.map(node => node.injector.get(dirType));
}
function _bindSimpleProp<T>(bindAttr: string): ComponentFixture<TestComponent>;
function _bindSimpleProp<T>(bindAttr: string, compType: ConcreteType<T>): ComponentFixture<T>;
function _bindSimpleProp<T>(
bindAttr: string, compType: ConcreteType<T> = <any>TestComponent): ComponentFixture<T> {
var template = `<div ${bindAttr}></div>`;
return createCompFixture(template, compType);
}
function _bindSimpleValue(expression: any): ComponentFixture<TestComponent>;
function _bindSimpleValue<T>(expression: any, compType: ConcreteType<T>): ComponentFixture<T>;
function _bindSimpleValue<T>(
expression: any, compType: ConcreteType<T> = <any>TestComponent): ComponentFixture<T> {
return _bindSimpleProp(`[someProp]='${expression}'`, compType);
}
function _bindAndCheckSimpleValue<T>(
expression: any, compType: ConcreteType<T> = <any>TestComponent): string[] {
function _bindAndCheckSimpleValue(
expression: any, compType: ConcreteType<any> = TestComponent): string[] {
var ctx = _bindSimpleValue(expression, compType);
ctx.detectChanges(false);
return renderLog.log;