refactor(compiler): various cleanups

- use `$implicit` variable value correctly
- handle `ng-non-bindable` correctly
- add some more assertions to `TemplateCompiler`
- make `CompiledTemplate` const
- fix default value for `@Directive.moduleId`
- add new compiler to application bindings

BREAKING CHANGE:
- `Compiler.compileInHost` and all methods of `DynamicComponentLoader` don’t take `Binding` any more, only `Type`s. This is in preparation for the new compiler which does not support this.

Part of #3605

Closes #4346
This commit is contained in:
Tobias Bosch
2015-09-18 10:33:23 -07:00
parent bffa2cb59b
commit 7470ad1bd1
29 changed files with 493 additions and 285 deletions

View File

@ -129,9 +129,7 @@ export class Compiler {
// Create a hostView as if the compiler encountered <hostcmp></hostcmp>.
// Used for bootstrapping.
compileInHost(componentTypeOrBinding: Type | Binding): Promise<ProtoViewRef> {
var componentType = isType(componentTypeOrBinding) ? componentTypeOrBinding :
(<Binding>componentTypeOrBinding).token;
compileInHost(componentType: Type): Promise<ProtoViewRef> {
var r = wtfStartTimeRange('Compiler#compile()', stringify(componentType));
var hostAppProtoView = this._compilerCache.getHost(componentType);
@ -139,7 +137,7 @@ export class Compiler {
if (isPresent(hostAppProtoView)) {
hostPvPromise = PromiseWrapper.resolve(hostAppProtoView);
} else {
var componentBinding: DirectiveBinding = this._bindDirective(componentTypeOrBinding);
var componentBinding: DirectiveBinding = this._bindDirective(componentType);
Compiler._assertTypeIsComponent(componentBinding);
var directiveMetadata = componentBinding.metadata;

View File

@ -6,10 +6,6 @@ import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
import {ElementRef} from './element_ref';
import {ViewRef, HostViewRef} from './view_ref';
function _asType(typeOrBinding: Type | Binding): Type {
return isType(typeOrBinding) ? typeOrBinding : (<Binding>typeOrBinding).token;
}
/**
* Angular's reference to a component instance.
*
@ -69,7 +65,7 @@ export class DynamicComponentLoader {
* Loads a root component that is placed at the first element that matches the component's
* selector.
*
* - `typeOrBinding` `Type` \ {@link Binding} - representing the component to load.
* - `typeOrBinding` `Type` - representing the component to load.
* - `overrideSelector` (optional) selector to load the component at (or use
* `@Component.selector`) The selector can be anywhere (i.e. outside the current component.)
* - `injector` {@link Injector} - optional injector to use for the component.
@ -120,24 +116,22 @@ export class DynamicComponentLoader {
* </my-app>
* ```
*/
loadAsRoot(typeOrBinding: Type | Binding, overrideSelector: string, injector: Injector,
loadAsRoot(type: Type, overrideSelector: string, injector: Injector,
onDispose?: () => void): Promise<ComponentRef> {
return this._compiler.compileInHost(typeOrBinding)
.then(hostProtoViewRef => {
var hostViewRef =
this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
var newLocation = this._viewManager.getHostElement(hostViewRef);
var component = this._viewManager.getComponent(newLocation);
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
var hostViewRef =
this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
var newLocation = this._viewManager.getHostElement(hostViewRef);
var component = this._viewManager.getComponent(newLocation);
var dispose = () => {
this._viewManager.destroyRootHostView(hostViewRef);
if (isPresent(onDispose)) {
onDispose();
}
};
return new ComponentRef(newLocation, component, _asType(typeOrBinding), injector,
dispose);
});
var dispose = () => {
this._viewManager.destroyRootHostView(hostViewRef);
if (isPresent(onDispose)) {
onDispose();
}
};
return new ComponentRef(newLocation, component, type, injector, dispose);
});
}
/**
@ -187,11 +181,10 @@ export class DynamicComponentLoader {
* </my-app>
* ```
*/
loadIntoLocation(typeOrBinding: Type | Binding, hostLocation: ElementRef, anchorName: string,
loadIntoLocation(type: Type, hostLocation: ElementRef, anchorName: string,
bindings: ResolvedBinding[] = null): Promise<ComponentRef> {
return this.loadNextToLocation(
typeOrBinding, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName),
bindings);
type, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName), bindings);
}
/**
@ -235,23 +228,22 @@ export class DynamicComponentLoader {
* <child-component>Child</child-component>
* ```
*/
loadNextToLocation(typeOrBinding: Type | Binding, location: ElementRef,
loadNextToLocation(type: Type, location: ElementRef,
bindings: ResolvedBinding[] = null): Promise<ComponentRef> {
return this._compiler.compileInHost(typeOrBinding)
.then(hostProtoViewRef => {
var viewContainer = this._viewManager.getViewContainer(location);
var hostViewRef =
viewContainer.createHostView(hostProtoViewRef, viewContainer.length, bindings);
var newLocation = this._viewManager.getHostElement(hostViewRef);
var component = this._viewManager.getComponent(newLocation);
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
var viewContainer = this._viewManager.getViewContainer(location);
var hostViewRef =
viewContainer.createHostView(hostProtoViewRef, viewContainer.length, bindings);
var newLocation = this._viewManager.getHostElement(hostViewRef);
var component = this._viewManager.getComponent(newLocation);
var dispose = () => {
var index = viewContainer.indexOf(<ViewRef>hostViewRef);
if (index !== -1) {
viewContainer.remove(index);
}
};
return new ComponentRef(newLocation, component, _asType(typeOrBinding), null, dispose);
});
var dispose = () => {
var index = viewContainer.indexOf(<ViewRef>hostViewRef);
if (index !== -1) {
viewContainer.remove(index);
}
};
return new ComponentRef(newLocation, component, type, null, dispose);
});
}
}

View File

@ -1,4 +1,4 @@
import {Type, CONST_EXPR, isPresent, isBlank} from 'angular2/src/core/facade/lang';
import {Type, CONST_EXPR, CONST, isPresent, isBlank} from 'angular2/src/core/facade/lang';
import {
RenderTemplateCmd,
RenderCommandVisitor,
@ -9,47 +9,32 @@ import {
RenderEmbeddedTemplateCmd
} from 'angular2/src/core/render/render';
/**
* A compiled template. This is const as we are storing it as annotation
* for the compiled component type.
*/
@CONST()
export class CompiledTemplate {
private _changeDetectorFactory: Function = null;
private _styles: string[] = null;
private _commands: TemplateCmd[] = null;
// Note: paramGetter is a function so that we can have cycles between templates!
constructor(public id: number, private _paramGetter: Function) {}
private _init() {
if (isBlank(this._commands)) {
var params = this._paramGetter();
this._changeDetectorFactory = params[0];
this._commands = params[1];
this._styles = params[2];
}
}
get changeDetectorFactory(): Function {
this._init();
return this._changeDetectorFactory;
}
get styles(): string[] {
this._init();
return this._styles;
}
get commands(): TemplateCmd[] {
this._init();
return this._commands;
}
// paramGetter returns a tuple with:
// - ChangeDetector factory function
// - TemplateCmd[]
// - styles
constructor(public id: number,
public dataGetter: /*()=>[Function, TemplateCmd[], string[]]*/ Function) {}
}
const EMPTY_ARR = CONST_EXPR([]);
export interface TemplateCmd extends RenderTemplateCmd {
visit(visitor: CommandVisitor, context: any): any;
visit(visitor: RenderCommandVisitor, context: any): any;
}
export class TextCmd implements TemplateCmd, RenderTextCmd {
constructor(public value: string, public isBound: boolean, public ngContentIndex: number) {}
visit(visitor: CommandVisitor, context: any): any { return visitor.visitText(this, context); }
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitText(this, context);
}
}
export function text(value: string, isBound: boolean, ngContentIndex: number): TextCmd {
@ -59,7 +44,7 @@ export function text(value: string, isBound: boolean, ngContentIndex: number): T
export class NgContentCmd implements TemplateCmd, RenderNgContentCmd {
isBound: boolean = false;
constructor(public ngContentIndex: number) {}
visit(visitor: CommandVisitor, context: any): any {
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitNgContent(this, context);
}
}
@ -72,7 +57,7 @@ export interface IBeginElementCmd extends TemplateCmd, RenderBeginElementCmd {
variableNameAndValues: Array<string | number>;
eventTargetAndNames: string[];
directives: Type[];
visit(visitor: CommandVisitor, context: any): any;
visit(visitor: RenderCommandVisitor, context: any): any;
}
export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeginElementCmd {
@ -80,7 +65,7 @@ export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeg
public eventTargetAndNames: string[],
public variableNameAndValues: Array<string | number>, public directives: Type[],
public isBound: boolean, public ngContentIndex: number) {}
visit(visitor: CommandVisitor, context: any): any {
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitBeginElement(this, context);
}
}
@ -94,7 +79,9 @@ export function beginElement(name: string, attrNameAndValues: string[],
}
export class EndElementCmd implements TemplateCmd {
visit(visitor: CommandVisitor, context: any): any { return visitor.visitEndElement(context); }
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitEndElement(context);
}
}
export function endElement(): TemplateCmd {
@ -113,7 +100,7 @@ export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderB
this.component = directives[0];
this.templateId = template.id;
}
visit(visitor: CommandVisitor, context: any): any {
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitBeginComponent(this, context);
}
}
@ -127,7 +114,9 @@ export function beginComponent(
}
export class EndComponentCmd implements TemplateCmd {
visit(visitor: CommandVisitor, context: any): any { return visitor.visitEndComponent(context); }
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitEndComponent(context);
}
}
export function endComponent(): TemplateCmd {
@ -142,7 +131,7 @@ export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd,
constructor(public attrNameAndValues: string[], public variableNameAndValues: string[],
public directives: Type[], public isMerged: boolean, public ngContentIndex: number,
public changeDetectorFactory: Function, public children: TemplateCmd[]) {}
visit(visitor: CommandVisitor, context: any): any {
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitEmbeddedTemplate(this, context);
}
}
@ -155,7 +144,15 @@ export function embeddedTemplate(attrNameAndValues: string[], variableNameAndVal
ngContentIndex, changeDetectorFactory, children);
}
export interface CommandVisitor extends RenderCommandVisitor {}
export interface CommandVisitor extends RenderCommandVisitor {
visitText(cmd: TextCmd, context: any): any;
visitNgContent(cmd: NgContentCmd, context: any): any;
visitBeginElement(cmd: BeginElementCmd, context: any): any;
visitEndElement(context: any): any;
visitBeginComponent(cmd: BeginComponentCmd, context: any): any;
visitEndComponent(context: any): any;
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any;
}
export function visitAllCommands(visitor: CommandVisitor, cmds: TemplateCmd[],
context: any = null) {