refactor(compiler): cleanup and preparation for integration
- Rename `DirectiveMetadata` into `CompileDirectiveMetadata`, merge with `NormalizedDirectiveMetadata` and remove `ChangeDetectionMetadata` - Store change detector factories not as array but directly at the `CompiledTemplate` or the embedded template to make instantiation easier later on - Already analyze variable values and map them to `Directive.exportAs` - Keep the directive sort order as specified in the `@View()` annotation - Allow to clear the runtime cache in `StyleCompiler` and `TemplateCompiler` - Ignore `script` elements to match the semantics of the current compiler - Make all components dynamically loadable and remove the previously introduced property `@Component#dynamicLoadable` for now until we find a better option to configure this - Don’t allow to specify bindings in `@View#directives` and `@View#pipes` as this was never supported by the transformer (see below for the breaking change) BREAKING CHANGE: - don't support DI bindings in `@View#directives` and `@View@pipes` any more in preparation of integrating the new compiler. Use `@Directive#bindings` to reexport directives under a different token instead. Part of #3605 Closes #4314
This commit is contained in:
@@ -14,7 +14,7 @@ import {
|
||||
ASTWithSource
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
|
||||
import {NormalizedDirectiveMetadata, TypeMetadata} from './directive_metadata';
|
||||
import {CompileDirectiveMetadata, CompileTypeMetadata} from './directive_metadata';
|
||||
import {
|
||||
TemplateAst,
|
||||
ElementAst,
|
||||
@@ -32,9 +32,10 @@ import {
|
||||
AttrAst,
|
||||
TextAst
|
||||
} from './template_ast';
|
||||
import {LifecycleHooks} from 'angular2/src/core/compiler/interfaces';
|
||||
|
||||
export function createChangeDetectorDefinitions(
|
||||
componentType: TypeMetadata, componentStrategy: ChangeDetectionStrategy,
|
||||
componentType: CompileTypeMetadata, componentStrategy: ChangeDetectionStrategy,
|
||||
genConfig: ChangeDetectorGenConfig, parsedTemplate: TemplateAst[]): ChangeDetectorDefinition[] {
|
||||
var pvVisitors = [];
|
||||
var visitor = new ProtoViewVisitor(null, pvVisitors, componentStrategy);
|
||||
@@ -59,7 +60,9 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
||||
this.boundElementCount++;
|
||||
templateVisitAll(this, ast.directives);
|
||||
for (var i = 0; i < ast.directives.length; i++) {
|
||||
ast.directives[i].visit(this, i);
|
||||
}
|
||||
|
||||
var childVisitor =
|
||||
new ProtoViewVisitor(this, this.allVisitors, ChangeDetectionStrategy.Default);
|
||||
@@ -76,7 +79,7 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
}
|
||||
templateVisitAll(this, ast.properties, null);
|
||||
templateVisitAll(this, ast.events);
|
||||
templateVisitAll(this, ast.vars);
|
||||
templateVisitAll(this, ast.exportAsVars);
|
||||
for (var i = 0; i < ast.directives.length; i++) {
|
||||
ast.directives[i].visit(this, i);
|
||||
}
|
||||
@@ -94,8 +97,8 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
visitEvent(ast: BoundEventAst, directiveRecord: DirectiveRecord): any {
|
||||
var bindingRecord =
|
||||
isPresent(directiveRecord) ?
|
||||
BindingRecord.createForHostEvent(ast.handler, ast.name, directiveRecord) :
|
||||
BindingRecord.createForEvent(ast.handler, ast.name, this.boundElementCount - 1);
|
||||
BindingRecord.createForHostEvent(ast.handler, ast.fullName, directiveRecord) :
|
||||
BindingRecord.createForEvent(ast.handler, ast.fullName, this.boundElementCount - 1);
|
||||
this.eventRecords.push(bindingRecord);
|
||||
return null;
|
||||
}
|
||||
@@ -138,17 +141,20 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
visitDirective(ast: DirectiveAst, directiveIndexAsNumber: number): any {
|
||||
var directiveIndex = new DirectiveIndex(this.boundElementCount - 1, directiveIndexAsNumber);
|
||||
var directiveMetadata = ast.directive;
|
||||
var changeDetectionMeta = directiveMetadata.changeDetection;
|
||||
var directiveRecord = new DirectiveRecord({
|
||||
directiveIndex: directiveIndex,
|
||||
callAfterContentInit: changeDetectionMeta.callAfterContentInit,
|
||||
callAfterContentChecked: changeDetectionMeta.callAfterContentChecked,
|
||||
callAfterViewInit: changeDetectionMeta.callAfterViewInit,
|
||||
callAfterViewChecked: changeDetectionMeta.callAfterViewChecked,
|
||||
callOnChanges: changeDetectionMeta.callOnChanges,
|
||||
callDoCheck: changeDetectionMeta.callDoCheck,
|
||||
callOnInit: changeDetectionMeta.callOnInit,
|
||||
changeDetection: changeDetectionMeta.changeDetection
|
||||
callAfterContentInit:
|
||||
directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterContentInit) !== -1,
|
||||
callAfterContentChecked:
|
||||
directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterContentChecked) !== -1,
|
||||
callAfterViewInit:
|
||||
directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterViewInit) !== -1,
|
||||
callAfterViewChecked:
|
||||
directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterViewChecked) !== -1,
|
||||
callOnChanges: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1,
|
||||
callDoCheck: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1,
|
||||
callOnInit: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1,
|
||||
changeDetection: directiveMetadata.changeDetection
|
||||
});
|
||||
this.directiveRecords.push(directiveRecord);
|
||||
|
||||
@@ -165,6 +171,7 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
}
|
||||
templateVisitAll(this, ast.hostProperties, directiveRecord);
|
||||
templateVisitAll(this, ast.hostEvents, directiveRecord);
|
||||
templateVisitAll(this, ast.exportAsVars);
|
||||
return null;
|
||||
}
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, directiveRecord: DirectiveRecord): any {
|
||||
@@ -178,7 +185,7 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
}
|
||||
|
||||
|
||||
function createChangeDefinitions(pvVisitors: ProtoViewVisitor[], componentType: TypeMetadata,
|
||||
function createChangeDefinitions(pvVisitors: ProtoViewVisitor[], componentType: CompileTypeMetadata,
|
||||
genConfig: ChangeDetectorGenConfig): ChangeDetectorDefinition[] {
|
||||
var pvVariableNames = _collectNestedProtoViewsVariableNames(pvVisitors);
|
||||
return pvVisitors.map(pvVisitor => {
|
||||
@@ -202,6 +209,7 @@ function _collectNestedProtoViewsVariableNames(pvVisitors: ProtoViewVisitor[]):
|
||||
}
|
||||
|
||||
|
||||
function _protoViewId(hostComponentType: TypeMetadata, pvIndex: number, viewType: string): string {
|
||||
function _protoViewId(hostComponentType: CompileTypeMetadata, pvIndex: number, viewType: string):
|
||||
string {
|
||||
return `${hostComponentType.name}_${viewType}_${pvIndex}`;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {TypeMetadata} from './directive_metadata';
|
||||
import {SourceExpression, moduleRef} from './source_module';
|
||||
import {CompileTypeMetadata} from './directive_metadata';
|
||||
import {SourceExpressions, moduleRef} from './source_module';
|
||||
import {
|
||||
ChangeDetectorJITGenerator
|
||||
} from 'angular2/src/core/change_detection/change_detection_jit_generator';
|
||||
@@ -32,7 +32,7 @@ var PREGEN_PROTO_CHANGE_DETECTOR_MODULE =
|
||||
export class ChangeDetectionCompiler {
|
||||
constructor(private _genConfig: ChangeDetectorGenConfig) {}
|
||||
|
||||
compileComponentRuntime(componentType: TypeMetadata, strategy: ChangeDetectionStrategy,
|
||||
compileComponentRuntime(componentType: CompileTypeMetadata, strategy: ChangeDetectionStrategy,
|
||||
parsedTemplate: TemplateAst[]): Function[] {
|
||||
var changeDetectorDefinitions =
|
||||
createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate);
|
||||
@@ -51,8 +51,8 @@ export class ChangeDetectionCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
compileComponentCodeGen(componentType: TypeMetadata, strategy: ChangeDetectionStrategy,
|
||||
parsedTemplate: TemplateAst[]): SourceExpression {
|
||||
compileComponentCodeGen(componentType: CompileTypeMetadata, strategy: ChangeDetectionStrategy,
|
||||
parsedTemplate: TemplateAst[]): SourceExpressions {
|
||||
var changeDetectorDefinitions =
|
||||
createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate);
|
||||
var factories = [];
|
||||
@@ -75,7 +75,6 @@ export class ChangeDetectionCompiler {
|
||||
return codegen.generateSource();
|
||||
}
|
||||
});
|
||||
var expression = `[ ${factories.join(',')} ]`;
|
||||
return new SourceExpression(sourceParts, expression);
|
||||
return new SourceExpressions(sourceParts, factories);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,7 @@
|
||||
export {TemplateCompiler} from './template_compiler';
|
||||
export {
|
||||
DirectiveMetadata,
|
||||
TypeMetadata,
|
||||
TemplateMetadata,
|
||||
ChangeDetectionMetadata,
|
||||
INormalizedDirectiveMetadata
|
||||
CompileDirectiveMetadata,
|
||||
CompileTypeMetadata,
|
||||
CompileTemplateMetadata
|
||||
} from './directive_metadata';
|
||||
export {SourceModule, SourceWithImports} from './source_module';
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,14 +8,14 @@ import {
|
||||
RegExpWrapper
|
||||
} from 'angular2/src/core/facade/lang';
|
||||
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
||||
import {MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
||||
import {MapWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
|
||||
import * as cpl from './directive_metadata';
|
||||
import * as dirAnn from 'angular2/src/core/metadata/directives';
|
||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
||||
import {ViewMetadata} from 'angular2/src/core/metadata/view';
|
||||
import {hasLifecycleHook} from 'angular2/src/core/compiler/directive_lifecycle_reflector';
|
||||
import {LifecycleHooks} from 'angular2/src/core/compiler/interfaces';
|
||||
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/compiler/interfaces';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
|
||||
@@ -26,81 +26,55 @@ var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
|
||||
@Injectable()
|
||||
export class RuntimeMetadataResolver {
|
||||
private _directiveCounter = 0;
|
||||
private _cache: Map<Type, cpl.DirectiveMetadata> = new Map();
|
||||
private _cache: Map<Type, cpl.CompileDirectiveMetadata> = new Map();
|
||||
|
||||
constructor(private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver) {}
|
||||
|
||||
getMetadata(directiveType: Type): cpl.DirectiveMetadata {
|
||||
getMetadata(directiveType: Type): cpl.CompileDirectiveMetadata {
|
||||
var meta = this._cache.get(directiveType);
|
||||
if (isBlank(meta)) {
|
||||
var directiveAnnotation = this._directiveResolver.resolve(directiveType);
|
||||
var moduleId = calcModuleId(directiveType, directiveAnnotation);
|
||||
var templateMeta = null;
|
||||
var hostListeners = {};
|
||||
var hostProperties = {};
|
||||
var hostAttributes = {};
|
||||
var changeDetectionStrategy = null;
|
||||
var dynamicLoadable: boolean = false;
|
||||
|
||||
if (isPresent(directiveAnnotation.host)) {
|
||||
StringMapWrapper.forEach(directiveAnnotation.host, (value: string, key: string) => {
|
||||
var matches = RegExpWrapper.firstMatch(HOST_REG_EXP, key);
|
||||
if (isBlank(matches)) {
|
||||
hostAttributes[key] = value;
|
||||
} else if (isPresent(matches[1])) {
|
||||
hostProperties[matches[1]] = value;
|
||||
} else if (isPresent(matches[2])) {
|
||||
hostListeners[matches[2]] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (directiveAnnotation instanceof dirAnn.ComponentMetadata) {
|
||||
var compAnnotation = <dirAnn.ComponentMetadata>directiveAnnotation;
|
||||
var viewAnnotation = this._viewResolver.resolve(directiveType);
|
||||
templateMeta = new cpl.TemplateMetadata({
|
||||
templateMeta = new cpl.CompileTemplateMetadata({
|
||||
encapsulation: viewAnnotation.encapsulation,
|
||||
template: viewAnnotation.template,
|
||||
templateUrl: viewAnnotation.templateUrl,
|
||||
styles: viewAnnotation.styles,
|
||||
styleUrls: viewAnnotation.styleUrls,
|
||||
hostAttributes: hostAttributes
|
||||
styleUrls: viewAnnotation.styleUrls
|
||||
});
|
||||
changeDetectionStrategy = compAnnotation.changeDetection;
|
||||
dynamicLoadable = compAnnotation.dynamicLoadable;
|
||||
}
|
||||
meta = new cpl.DirectiveMetadata({
|
||||
meta = cpl.CompileDirectiveMetadata.create({
|
||||
selector: directiveAnnotation.selector,
|
||||
exportAs: directiveAnnotation.exportAs,
|
||||
isComponent: isPresent(templateMeta),
|
||||
dynamicLoadable: dynamicLoadable,
|
||||
type: new cpl.TypeMetadata({
|
||||
dynamicLoadable: true,
|
||||
type: new cpl.CompileTypeMetadata({
|
||||
id: this._directiveCounter++,
|
||||
name: stringify(directiveType),
|
||||
moduleId: moduleId,
|
||||
runtime: directiveType
|
||||
}),
|
||||
template: templateMeta,
|
||||
changeDetection: new cpl.ChangeDetectionMetadata({
|
||||
changeDetection: changeDetectionStrategy,
|
||||
properties: directiveAnnotation.properties,
|
||||
events: directiveAnnotation.events,
|
||||
hostListeners: hostListeners,
|
||||
hostProperties: hostProperties,
|
||||
callAfterContentInit: hasLifecycleHook(LifecycleHooks.AfterContentInit, directiveType),
|
||||
callAfterContentChecked:
|
||||
hasLifecycleHook(LifecycleHooks.AfterContentChecked, directiveType),
|
||||
callAfterViewInit: hasLifecycleHook(LifecycleHooks.AfterViewInit, directiveType),
|
||||
callAfterViewChecked: hasLifecycleHook(LifecycleHooks.AfterViewChecked, directiveType),
|
||||
callOnChanges: hasLifecycleHook(LifecycleHooks.OnChanges, directiveType),
|
||||
callDoCheck: hasLifecycleHook(LifecycleHooks.DoCheck, directiveType),
|
||||
callOnInit: hasLifecycleHook(LifecycleHooks.OnInit, directiveType),
|
||||
})
|
||||
changeDetection: changeDetectionStrategy,
|
||||
properties: directiveAnnotation.properties,
|
||||
events: directiveAnnotation.events,
|
||||
host: directiveAnnotation.host,
|
||||
lifecycleHooks: ListWrapper.filter(LIFECYCLE_HOOKS_VALUES,
|
||||
hook => hasLifecycleHook(hook, directiveType))
|
||||
});
|
||||
this._cache.set(directiveType, meta);
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
|
||||
getViewDirectivesMetadata(component: Type): cpl.DirectiveMetadata[] {
|
||||
getViewDirectivesMetadata(component: Type): cpl.CompileDirectiveMetadata[] {
|
||||
var view = this._viewResolver.resolve(component);
|
||||
var directives = flattenDirectives(view);
|
||||
for (var i = 0; i < directives.length; i++) {
|
||||
@@ -113,8 +87,9 @@ export class RuntimeMetadataResolver {
|
||||
}
|
||||
}
|
||||
|
||||
function removeDuplicatedDirectives(directives: cpl.DirectiveMetadata[]): cpl.DirectiveMetadata[] {
|
||||
var directivesMap: Map<number, cpl.DirectiveMetadata> = new Map();
|
||||
function removeDuplicatedDirectives(directives: cpl.CompileDirectiveMetadata[]):
|
||||
cpl.CompileDirectiveMetadata[] {
|
||||
var directivesMap: Map<number, cpl.CompileDirectiveMetadata> = new Map();
|
||||
directives.forEach((dirMeta) => { directivesMap.set(dirMeta.type.id, dirMeta); });
|
||||
return MapWrapper.values(directivesMap);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,10 @@ export class SourceExpression {
|
||||
constructor(public declarations: string[], public expression: string) {}
|
||||
}
|
||||
|
||||
export class SourceExpressions {
|
||||
constructor(public declarations: string[], public expressions: string[]) {}
|
||||
}
|
||||
|
||||
export class SourceWithImports {
|
||||
constructor(public source: string, public imports: string[][]) {}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {TypeMetadata, NormalizedDirectiveMetadata} from './directive_metadata';
|
||||
import {CompileTypeMetadata, CompileDirectiveMetadata} from './directive_metadata';
|
||||
import {SourceModule, SourceExpression, moduleRef} from './source_module';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||
import {XHR} from 'angular2/src/core/render/xhr';
|
||||
@@ -29,16 +29,16 @@ export class StyleCompiler {
|
||||
|
||||
constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {}
|
||||
|
||||
compileComponentRuntime(component: NormalizedDirectiveMetadata): Promise<string[]> {
|
||||
compileComponentRuntime(component: CompileDirectiveMetadata): Promise<string[]> {
|
||||
var styles = component.template.styles;
|
||||
var styleAbsUrls = component.template.styleAbsUrls;
|
||||
var styleAbsUrls = component.template.styleUrls;
|
||||
return this._loadStyles(styles, styleAbsUrls,
|
||||
component.template.encapsulation === ViewEncapsulation.Emulated)
|
||||
.then(styles => styles.map(style => StringWrapper.replaceAll(style, COMPONENT_REGEX,
|
||||
`${component.type.id}`)));
|
||||
}
|
||||
|
||||
compileComponentCodeGen(component: NormalizedDirectiveMetadata): SourceExpression {
|
||||
compileComponentCodeGen(component: CompileDirectiveMetadata): SourceExpression {
|
||||
var shim = component.template.encapsulation === ViewEncapsulation.Emulated;
|
||||
var suffix;
|
||||
if (shim) {
|
||||
@@ -48,7 +48,7 @@ export class StyleCompiler {
|
||||
} else {
|
||||
suffix = '';
|
||||
}
|
||||
return this._styleCodeGen(component.template.styles, component.template.styleAbsUrls, shim,
|
||||
return this._styleCodeGen(component.template.styles, component.template.styleUrls, shim,
|
||||
suffix);
|
||||
}
|
||||
|
||||
@@ -62,6 +62,8 @@ export class StyleCompiler {
|
||||
];
|
||||
}
|
||||
|
||||
clearCache() { this._styleCache.clear(); }
|
||||
|
||||
private _loadStyles(plainStyles: string[], absUrls: string[],
|
||||
encapsulate: boolean): Promise<string[]> {
|
||||
var promises = absUrls.map((absUrl) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {AST} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
||||
import {NormalizedDirectiveMetadata} from './directive_metadata';
|
||||
import {CompileDirectiveMetadata} from './directive_metadata';
|
||||
|
||||
export interface TemplateAst {
|
||||
sourceInfo: string;
|
||||
@@ -38,7 +38,7 @@ export class BoundEventAst implements TemplateAst {
|
||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||
return visitor.visitEvent(this, context);
|
||||
}
|
||||
getFullName(): string {
|
||||
get fullName() {
|
||||
if (isPresent(this.target)) {
|
||||
return `${this.target}:${this.name}`;
|
||||
} else {
|
||||
@@ -57,7 +57,7 @@ export class VariableAst implements TemplateAst {
|
||||
export class ElementAst implements TemplateAst {
|
||||
constructor(public name: string, public attrs: AttrAst[],
|
||||
public properties: BoundElementPropertyAst[], public events: BoundEventAst[],
|
||||
public vars: VariableAst[], public directives: DirectiveAst[],
|
||||
public exportAsVars: VariableAst[], public directives: DirectiveAst[],
|
||||
public children: TemplateAst[], public ngContentIndex: number,
|
||||
public sourceInfo: string) {}
|
||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||
@@ -65,11 +65,11 @@ export class ElementAst implements TemplateAst {
|
||||
}
|
||||
|
||||
isBound(): boolean {
|
||||
return (this.properties.length > 0 || this.events.length > 0 || this.vars.length > 0 ||
|
||||
return (this.properties.length > 0 || this.events.length > 0 || this.exportAsVars.length > 0 ||
|
||||
this.directives.length > 0);
|
||||
}
|
||||
|
||||
getComponent(): NormalizedDirectiveMetadata {
|
||||
getComponent(): CompileDirectiveMetadata {
|
||||
return this.directives.length > 0 && this.directives[0].directive.isComponent ?
|
||||
this.directives[0].directive :
|
||||
null;
|
||||
@@ -94,10 +94,10 @@ export class BoundDirectivePropertyAst implements TemplateAst {
|
||||
}
|
||||
|
||||
export class DirectiveAst implements TemplateAst {
|
||||
constructor(public directive: NormalizedDirectiveMetadata,
|
||||
constructor(public directive: CompileDirectiveMetadata,
|
||||
public properties: BoundDirectivePropertyAst[],
|
||||
public hostProperties: BoundElementPropertyAst[], public hostEvents: BoundEventAst[],
|
||||
public sourceInfo: string) {}
|
||||
public exportAsVars: VariableAst[], public sourceInfo: string) {}
|
||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||
return visitor.visitDirective(this, context);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import {Type, Json, isBlank, stringify} from 'angular2/src/core/facade/lang';
|
||||
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
||||
import {ListWrapper, SetWrapper} from 'angular2/src/core/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
||||
import {CompiledTemplate, TemplateCmd} from 'angular2/src/core/compiler/template_commands';
|
||||
import {
|
||||
createHostComponentMeta,
|
||||
DirectiveMetadata,
|
||||
INormalizedDirectiveMetadata,
|
||||
NormalizedDirectiveMetadata,
|
||||
TypeMetadata,
|
||||
ChangeDetectionMetadata,
|
||||
NormalizedTemplateMetadata
|
||||
CompileDirectiveMetadata,
|
||||
CompileTypeMetadata,
|
||||
CompileTemplateMetadata
|
||||
} from './directive_metadata';
|
||||
import {TemplateAst} from './template_ast';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
@@ -36,7 +32,8 @@ export class TemplateCompiler {
|
||||
private _commandCompiler: CommandCompiler,
|
||||
private _cdCompiler: ChangeDetectionCompiler) {}
|
||||
|
||||
normalizeDirectiveMetadata(directive: DirectiveMetadata): Promise<INormalizedDirectiveMetadata> {
|
||||
normalizeDirectiveMetadata(directive:
|
||||
CompileDirectiveMetadata): Promise<CompileDirectiveMetadata> {
|
||||
var normalizedTemplatePromise;
|
||||
if (directive.isComponent) {
|
||||
normalizedTemplatePromise =
|
||||
@@ -45,49 +42,58 @@ export class TemplateCompiler {
|
||||
normalizedTemplatePromise = PromiseWrapper.resolve(null);
|
||||
}
|
||||
return normalizedTemplatePromise.then(
|
||||
(normalizedTemplate) => new NormalizedDirectiveMetadata({
|
||||
selector: directive.selector,
|
||||
dynamicLoadable: directive.dynamicLoadable,
|
||||
isComponent: directive.isComponent,
|
||||
(normalizedTemplate) => new CompileDirectiveMetadata({
|
||||
type: directive.type,
|
||||
changeDetection: directive.changeDetection, template: normalizedTemplate
|
||||
isComponent: directive.isComponent,
|
||||
dynamicLoadable: directive.dynamicLoadable,
|
||||
selector: directive.selector,
|
||||
exportAs: directive.exportAs,
|
||||
changeDetection: directive.changeDetection,
|
||||
properties: directive.properties,
|
||||
events: directive.events,
|
||||
hostListeners: directive.hostListeners,
|
||||
hostProperties: directive.hostProperties,
|
||||
hostAttributes: directive.hostAttributes,
|
||||
lifecycleHooks: directive.lifecycleHooks, template: normalizedTemplate
|
||||
}));
|
||||
}
|
||||
|
||||
serializeDirectiveMetadata(metadata: INormalizedDirectiveMetadata): string {
|
||||
return Json.stringify((<NormalizedDirectiveMetadata>metadata).toJson());
|
||||
serializeDirectiveMetadata(metadata: CompileDirectiveMetadata): string {
|
||||
return Json.stringify(metadata.toJson());
|
||||
}
|
||||
|
||||
deserializeDirectiveMetadata(data: string): INormalizedDirectiveMetadata {
|
||||
return NormalizedDirectiveMetadata.fromJson(Json.parse(data));
|
||||
deserializeDirectiveMetadata(data: string): CompileDirectiveMetadata {
|
||||
return CompileDirectiveMetadata.fromJson(Json.parse(data));
|
||||
}
|
||||
|
||||
compileHostComponentRuntime(type: Type): Promise<CompiledTemplate> {
|
||||
var compMeta: DirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type);
|
||||
if (isBlank(compMeta) || !compMeta.isComponent || !compMeta.dynamicLoadable) {
|
||||
throw new BaseException(
|
||||
`Could not compile '${stringify(type)}' because it is not dynamically loadable.`);
|
||||
}
|
||||
var hostMeta: NormalizedDirectiveMetadata =
|
||||
var compMeta: CompileDirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type);
|
||||
var hostMeta: CompileDirectiveMetadata =
|
||||
createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||
this._compileComponentRuntime(hostMeta, [compMeta], new Set());
|
||||
return this._compiledTemplateDone.get(hostMeta.type.id);
|
||||
}
|
||||
|
||||
private _compileComponentRuntime(compMeta: NormalizedDirectiveMetadata,
|
||||
viewDirectives: DirectiveMetadata[],
|
||||
clearCache() {
|
||||
this._styleCompiler.clearCache();
|
||||
this._compiledTemplateCache.clear();
|
||||
this._compiledTemplateDone.clear();
|
||||
}
|
||||
|
||||
private _compileComponentRuntime(compMeta: CompileDirectiveMetadata,
|
||||
viewDirectives: CompileDirectiveMetadata[],
|
||||
compilingComponentIds: Set<number>): CompiledTemplate {
|
||||
var compiledTemplate = this._compiledTemplateCache.get(compMeta.type.id);
|
||||
var done = this._compiledTemplateDone.get(compMeta.type.id);
|
||||
if (isBlank(compiledTemplate)) {
|
||||
var styles;
|
||||
var changeDetectorFactories;
|
||||
var changeDetectorFactory;
|
||||
var commands;
|
||||
compiledTemplate =
|
||||
new CompiledTemplate(compMeta.type.id, () => [changeDetectorFactories, commands, styles]);
|
||||
new CompiledTemplate(compMeta.type.id, () => [changeDetectorFactory, commands, styles]);
|
||||
this._compiledTemplateCache.set(compMeta.type.id, compiledTemplate);
|
||||
compilingComponentIds.add(compMeta.type.id);
|
||||
done = PromiseWrapper.all([this._styleCompiler.compileComponentRuntime(compMeta)].concat(
|
||||
done = PromiseWrapper.all([<any>this._styleCompiler.compileComponentRuntime(compMeta)].concat(
|
||||
viewDirectives.map(
|
||||
dirMeta => this.normalizeDirectiveMetadata(dirMeta))))
|
||||
.then((stylesAndNormalizedViewDirMetas: any[]) => {
|
||||
@@ -96,10 +102,12 @@ export class TemplateCompiler {
|
||||
var parsedTemplate = this._templateParser.parse(
|
||||
compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
|
||||
|
||||
changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
|
||||
compMeta.type, compMeta.changeDetection.changeDetection, parsedTemplate);
|
||||
var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
|
||||
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
||||
changeDetectorFactory = changeDetectorFactories[0];
|
||||
styles = stylesAndNormalizedViewDirMetas[0];
|
||||
commands = this._compileCommandsRuntime(compMeta, parsedTemplate,
|
||||
changeDetectorFactories,
|
||||
compilingComponentIds, childPromises);
|
||||
return PromiseWrapper.all(childPromises);
|
||||
})
|
||||
@@ -112,12 +120,14 @@ export class TemplateCompiler {
|
||||
return compiledTemplate;
|
||||
}
|
||||
|
||||
private _compileCommandsRuntime(compMeta: NormalizedDirectiveMetadata,
|
||||
parsedTemplate: TemplateAst[], compilingComponentIds: Set<number>,
|
||||
private _compileCommandsRuntime(compMeta: CompileDirectiveMetadata, parsedTemplate: TemplateAst[],
|
||||
changeDetectorFactories: Function[],
|
||||
compilingComponentIds: Set<number>,
|
||||
childPromises: Promise<any>[]): TemplateCmd[] {
|
||||
return this._commandCompiler.compileComponentRuntime(
|
||||
compMeta, parsedTemplate, (childComponentDir: NormalizedDirectiveMetadata) => {
|
||||
var childViewDirectives: DirectiveMetadata[] =
|
||||
compMeta, parsedTemplate, changeDetectorFactories,
|
||||
(childComponentDir: CompileDirectiveMetadata) => {
|
||||
var childViewDirectives: CompileDirectiveMetadata[] =
|
||||
this._runtimeMetadataResolver.getViewDirectivesMetadata(
|
||||
childComponentDir.type.runtime);
|
||||
var childIsRecursive = SetWrapper.has(compilingComponentIds, childComponentDir.type.id);
|
||||
@@ -135,12 +145,12 @@ export class TemplateCompiler {
|
||||
components: NormalizedComponentWithViewDirectives[]): SourceModule {
|
||||
var declarations = [];
|
||||
var templateArguments = [];
|
||||
var componentMetas: NormalizedDirectiveMetadata[] = [];
|
||||
var componentMetas: CompileDirectiveMetadata[] = [];
|
||||
components.forEach(componentWithDirs => {
|
||||
var compMeta = <NormalizedDirectiveMetadata>componentWithDirs.component;
|
||||
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
|
||||
componentMetas.push(compMeta);
|
||||
this._processTemplateCodeGen(compMeta,
|
||||
<NormalizedDirectiveMetadata[]>componentWithDirs.directives,
|
||||
<CompileDirectiveMetadata[]>componentWithDirs.directives,
|
||||
declarations, templateArguments);
|
||||
if (compMeta.dynamicLoadable) {
|
||||
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||
@@ -148,7 +158,7 @@ export class TemplateCompiler {
|
||||
this._processTemplateCodeGen(hostMeta, [compMeta], declarations, templateArguments);
|
||||
}
|
||||
});
|
||||
ListWrapper.forEachWithIndex(componentMetas, (compMeta: NormalizedDirectiveMetadata,
|
||||
ListWrapper.forEachWithIndex(componentMetas, (compMeta: CompileDirectiveMetadata,
|
||||
index: number) => {
|
||||
var templateDataFn = codeGenValueFn([], `[${templateArguments[index].join(',')}]`);
|
||||
declarations.push(
|
||||
@@ -161,32 +171,33 @@ export class TemplateCompiler {
|
||||
return this._styleCompiler.compileStylesheetCodeGen(moduleId, cssText);
|
||||
}
|
||||
|
||||
private _processTemplateCodeGen(compMeta: NormalizedDirectiveMetadata,
|
||||
directives: NormalizedDirectiveMetadata[],
|
||||
private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata,
|
||||
directives: CompileDirectiveMetadata[],
|
||||
targetDeclarations: string[], targetTemplateArguments: any[][]) {
|
||||
var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta);
|
||||
var parsedTemplate =
|
||||
this._templateParser.parse(compMeta.template.template, directives, compMeta.type.name);
|
||||
var changeDetectorsExpr = this._cdCompiler.compileComponentCodeGen(
|
||||
compMeta.type, compMeta.changeDetection.changeDetection, parsedTemplate);
|
||||
var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen(
|
||||
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
||||
var commandsExpr = this._commandCompiler.compileComponentCodeGen(
|
||||
compMeta, parsedTemplate, codeGenComponentTemplateFactory);
|
||||
compMeta, parsedTemplate, changeDetectorsExprs.expressions,
|
||||
codeGenComponentTemplateFactory);
|
||||
|
||||
addAll(styleExpr.declarations, targetDeclarations);
|
||||
addAll(changeDetectorsExpr.declarations, targetDeclarations);
|
||||
addAll(changeDetectorsExprs.declarations, targetDeclarations);
|
||||
addAll(commandsExpr.declarations, targetDeclarations);
|
||||
|
||||
targetTemplateArguments.push(
|
||||
[changeDetectorsExpr.expression, commandsExpr.expression, styleExpr.expression]);
|
||||
[changeDetectorsExprs.expressions[0], commandsExpr.expression, styleExpr.expression]);
|
||||
}
|
||||
}
|
||||
|
||||
export class NormalizedComponentWithViewDirectives {
|
||||
constructor(public component: INormalizedDirectiveMetadata,
|
||||
public directives: INormalizedDirectiveMetadata[]) {}
|
||||
constructor(public component: CompileDirectiveMetadata,
|
||||
public directives: CompileDirectiveMetadata[]) {}
|
||||
}
|
||||
|
||||
function templateVariableName(type: TypeMetadata): string {
|
||||
function templateVariableName(type: CompileTypeMetadata): string {
|
||||
return `${type.name}Template`;
|
||||
}
|
||||
|
||||
@@ -200,6 +211,6 @@ function addAll(source: any[], target: any[]) {
|
||||
}
|
||||
}
|
||||
|
||||
function codeGenComponentTemplateFactory(nestedCompType: NormalizedDirectiveMetadata): string {
|
||||
function codeGenComponentTemplateFactory(nestedCompType: CompileDirectiveMetadata): string {
|
||||
return `${moduleRef(templateModuleName(nestedCompType.type.moduleId))}${templateVariableName(nestedCompType.type)}`;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import {
|
||||
TypeMetadata,
|
||||
TemplateMetadata,
|
||||
NormalizedDirectiveMetadata,
|
||||
NormalizedTemplateMetadata
|
||||
CompileTypeMetadata,
|
||||
CompileDirectiveMetadata,
|
||||
CompileTemplateMetadata
|
||||
} from './directive_metadata';
|
||||
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
|
||||
@@ -11,6 +10,7 @@ import {XHR} from 'angular2/src/core/render/xhr';
|
||||
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
|
||||
import {resolveStyleUrls} from './style_url_resolver';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||
|
||||
import {
|
||||
HtmlAstVisitor,
|
||||
@@ -22,21 +22,15 @@ import {
|
||||
} from './html_ast';
|
||||
import {HtmlParser} from './html_parser';
|
||||
|
||||
const NG_CONTENT_SELECT_ATTR = 'select';
|
||||
const NG_CONTENT_ELEMENT = 'ng-content';
|
||||
const LINK_ELEMENT = 'link';
|
||||
const LINK_STYLE_REL_ATTR = 'rel';
|
||||
const LINK_STYLE_HREF_ATTR = 'href';
|
||||
const LINK_STYLE_REL_VALUE = 'stylesheet';
|
||||
const STYLE_ELEMENT = 'style';
|
||||
import {preparseElement, PreparsedElement, PreparsedElementType} from './template_preparser';
|
||||
|
||||
@Injectable()
|
||||
export class TemplateNormalizer {
|
||||
constructor(private _xhr: XHR, private _urlResolver: UrlResolver,
|
||||
private _domParser: HtmlParser) {}
|
||||
|
||||
normalizeTemplate(directiveType: TypeMetadata,
|
||||
template: TemplateMetadata): Promise<NormalizedTemplateMetadata> {
|
||||
normalizeTemplate(directiveType: CompileTypeMetadata,
|
||||
template: CompileTemplateMetadata): Promise<CompileTemplateMetadata> {
|
||||
if (isPresent(template.template)) {
|
||||
return PromiseWrapper.resolve(this.normalizeLoadedTemplate(
|
||||
directiveType, template, template.template, directiveType.moduleId));
|
||||
@@ -48,11 +42,11 @@ export class TemplateNormalizer {
|
||||
}
|
||||
}
|
||||
|
||||
normalizeLoadedTemplate(directiveType: TypeMetadata, templateMeta: TemplateMetadata,
|
||||
template: string, templateAbsUrl: string): NormalizedTemplateMetadata {
|
||||
normalizeLoadedTemplate(directiveType: CompileTypeMetadata, templateMeta: CompileTemplateMetadata,
|
||||
template: string, templateAbsUrl: string): CompileTemplateMetadata {
|
||||
var domNodes = this._domParser.parse(template, directiveType.name);
|
||||
var visitor = new TemplatePreparseVisitor();
|
||||
var remainingNodes = htmlVisitAll(visitor, domNodes);
|
||||
htmlVisitAll(visitor, domNodes);
|
||||
var allStyles = templateMeta.styles.concat(visitor.styles);
|
||||
|
||||
var allStyleAbsUrls =
|
||||
@@ -65,11 +59,17 @@ export class TemplateNormalizer {
|
||||
styleWithImports.styleUrls.forEach(styleUrl => allStyleAbsUrls.push(styleUrl));
|
||||
return styleWithImports.style;
|
||||
});
|
||||
return new NormalizedTemplateMetadata({
|
||||
encapsulation: templateMeta.encapsulation,
|
||||
template: this._domParser.unparse(remainingNodes),
|
||||
var encapsulation = templateMeta.encapsulation;
|
||||
if (encapsulation === ViewEncapsulation.Emulated && allResolvedStyles.length === 0 &&
|
||||
allStyleAbsUrls.length === 0) {
|
||||
encapsulation = ViewEncapsulation.None;
|
||||
}
|
||||
return new CompileTemplateMetadata({
|
||||
encapsulation: encapsulation,
|
||||
template: template,
|
||||
templateUrl: templateAbsUrl,
|
||||
styles: allResolvedStyles,
|
||||
styleAbsUrls: allStyleAbsUrls,
|
||||
styleUrls: allStyleAbsUrls,
|
||||
ngContentSelectors: visitor.ngContentSelectors
|
||||
});
|
||||
}
|
||||
@@ -80,50 +80,30 @@ class TemplatePreparseVisitor implements HtmlAstVisitor {
|
||||
styles: string[] = [];
|
||||
styleUrls: string[] = [];
|
||||
|
||||
visitElement(ast: HtmlElementAst, context: any): HtmlElementAst {
|
||||
var selectAttr = null;
|
||||
var hrefAttr = null;
|
||||
var relAttr = null;
|
||||
ast.attrs.forEach(attr => {
|
||||
if (attr.name == NG_CONTENT_SELECT_ATTR) {
|
||||
selectAttr = attr.value;
|
||||
} else if (attr.name == LINK_STYLE_HREF_ATTR) {
|
||||
hrefAttr = attr.value;
|
||||
} else if (attr.name == LINK_STYLE_REL_ATTR) {
|
||||
relAttr = attr.value;
|
||||
}
|
||||
});
|
||||
var nodeName = ast.name;
|
||||
var keepElement = true;
|
||||
if (nodeName == NG_CONTENT_ELEMENT) {
|
||||
this.ngContentSelectors.push(normalizeNgContentSelect(selectAttr));
|
||||
} else if (nodeName == STYLE_ELEMENT) {
|
||||
keepElement = false;
|
||||
var textContent = '';
|
||||
ast.children.forEach(child => {
|
||||
if (child instanceof HtmlTextAst) {
|
||||
textContent += (<HtmlTextAst>child).value;
|
||||
}
|
||||
});
|
||||
this.styles.push(textContent);
|
||||
} else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) {
|
||||
keepElement = false;
|
||||
this.styleUrls.push(hrefAttr);
|
||||
visitElement(ast: HtmlElementAst, context: any): any {
|
||||
var preparsedElement = preparseElement(ast);
|
||||
switch (preparsedElement.type) {
|
||||
case PreparsedElementType.NG_CONTENT:
|
||||
this.ngContentSelectors.push(preparsedElement.selectAttr);
|
||||
break;
|
||||
case PreparsedElementType.STYLE:
|
||||
var textContent = '';
|
||||
ast.children.forEach(child => {
|
||||
if (child instanceof HtmlTextAst) {
|
||||
textContent += (<HtmlTextAst>child).value;
|
||||
}
|
||||
});
|
||||
this.styles.push(textContent);
|
||||
break;
|
||||
case PreparsedElementType.STYLESHEET:
|
||||
this.styleUrls.push(preparsedElement.hrefAttr);
|
||||
break;
|
||||
}
|
||||
if (keepElement) {
|
||||
return new HtmlElementAst(ast.name, ast.attrs, htmlVisitAll(this, ast.children),
|
||||
ast.sourceInfo);
|
||||
} else {
|
||||
return null;
|
||||
if (preparsedElement.type !== PreparsedElementType.NON_BINDABLE) {
|
||||
htmlVisitAll(this, ast.children);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
visitAttr(ast: HtmlAttrAst, context: any): HtmlAttrAst { return ast; }
|
||||
visitText(ast: HtmlTextAst, context: any): HtmlTextAst { return ast; }
|
||||
visitAttr(ast: HtmlAttrAst, context: any): any { return null; }
|
||||
visitText(ast: HtmlTextAst, context: any): any { return null; }
|
||||
}
|
||||
|
||||
function normalizeNgContentSelect(selectAttr: string): string {
|
||||
if (isBlank(selectAttr) || selectAttr.length === 0) {
|
||||
return '*';
|
||||
}
|
||||
return selectAttr;
|
||||
}
|
||||
@@ -1,4 +1,9 @@
|
||||
import {MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
||||
import {
|
||||
MapWrapper,
|
||||
ListWrapper,
|
||||
StringMapWrapper,
|
||||
SetWrapper
|
||||
} from 'angular2/src/core/facade/collection';
|
||||
import {
|
||||
RegExpWrapper,
|
||||
isPresent,
|
||||
@@ -12,7 +17,7 @@ import {Injectable} from 'angular2/src/core/di';
|
||||
import {BaseException} from 'angular2/src/core/facade/exceptions';
|
||||
import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast';
|
||||
import {NormalizedDirectiveMetadata} from './directive_metadata';
|
||||
import {CompileDirectiveMetadata} from './directive_metadata';
|
||||
import {HtmlParser} from './html_parser';
|
||||
|
||||
import {
|
||||
@@ -33,6 +38,7 @@ import {
|
||||
import {CssSelector, SelectorMatcher} from 'angular2/src/core/render/dom/compiler/selector';
|
||||
|
||||
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
|
||||
import {preparseElement, PreparsedElement, PreparsedElementType} from './template_preparser';
|
||||
|
||||
import {
|
||||
HtmlAstVisitor,
|
||||
@@ -43,7 +49,7 @@ import {
|
||||
htmlVisitAll
|
||||
} from './html_ast';
|
||||
|
||||
import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
|
||||
import {dashCaseToCamelCase, camelCaseToDashCase, splitAtColon} from './util';
|
||||
|
||||
// Group 1 = "bind-"
|
||||
// Group 2 = "var-" or "#"
|
||||
@@ -56,12 +62,10 @@ import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
|
||||
var BIND_NAME_REGEXP =
|
||||
/^(?:(?:(?:(bind-)|(var-|#)|(on-)|(bindon-))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g;
|
||||
|
||||
const NG_CONTENT_ELEMENT = 'ng-content';
|
||||
const TEMPLATE_ELEMENT = 'template';
|
||||
const TEMPLATE_ATTR = 'template';
|
||||
const TEMPLATE_ATTR_PREFIX = '*';
|
||||
const CLASS_ATTR = 'class';
|
||||
const IMPLICIT_VAR_NAME = '$implicit';
|
||||
|
||||
var PROPERTY_PARTS_SEPARATOR = new RegExp('\\.');
|
||||
const ATTRIBUTE_PREFIX = 'attr';
|
||||
@@ -75,7 +79,7 @@ export class TemplateParser {
|
||||
constructor(private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry,
|
||||
private _htmlParser: HtmlParser) {}
|
||||
|
||||
parse(template: string, directives: NormalizedDirectiveMetadata[],
|
||||
parse(template: string, directives: CompileDirectiveMetadata[],
|
||||
sourceInfo: string): TemplateAst[] {
|
||||
var parseVisitor = new TemplateParseVisitor(directives, this._exprParser, this._schemaRegistry);
|
||||
var result =
|
||||
@@ -91,13 +95,16 @@ export class TemplateParser {
|
||||
class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
selectorMatcher: SelectorMatcher;
|
||||
errors: string[] = [];
|
||||
constructor(directives: NormalizedDirectiveMetadata[], private _exprParser: Parser,
|
||||
directivesIndexByTypeId: Map<number, number> = new Map();
|
||||
constructor(directives: CompileDirectiveMetadata[], private _exprParser: Parser,
|
||||
private _schemaRegistry: ElementSchemaRegistry) {
|
||||
this.selectorMatcher = new SelectorMatcher();
|
||||
directives.forEach(directive => {
|
||||
var selector = CssSelector.parse(directive.selector);
|
||||
this.selectorMatcher.addSelectables(selector, directive);
|
||||
});
|
||||
ListWrapper.forEachWithIndex(directives,
|
||||
(directive: CompileDirectiveMetadata, index: number) => {
|
||||
var selector = CssSelector.parse(directive.selector);
|
||||
this.selectorMatcher.addSelectables(selector, directive);
|
||||
this.directivesIndexByTypeId.set(directive.type.id, index);
|
||||
});
|
||||
}
|
||||
|
||||
private _reportError(message: string) { this.errors.push(message); }
|
||||
@@ -154,6 +161,17 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
|
||||
visitElement(element: HtmlElementAst, component: Component): any {
|
||||
var nodeName = element.name;
|
||||
var preparsedElement = preparseElement(element);
|
||||
if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
||||
preparsedElement.type === PreparsedElementType.STYLE ||
|
||||
preparsedElement.type === PreparsedElementType.STYLESHEET ||
|
||||
preparsedElement.type === PreparsedElementType.NON_BINDABLE) {
|
||||
// Skipping <script> for security reasons
|
||||
// Skipping <style> and stylesheets as we already processed them
|
||||
// in the StyleCompiler
|
||||
return null;
|
||||
}
|
||||
|
||||
var matchableAttrs: string[][] = [];
|
||||
var elementOrDirectiveProps: BoundElementOrDirectiveProperty[] = [];
|
||||
var vars: VariableAst[] = [];
|
||||
@@ -177,34 +195,37 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
hasInlineTemplates = true;
|
||||
}
|
||||
});
|
||||
var isTemplateElement = nodeName == TEMPLATE_ELEMENT;
|
||||
var elementCssSelector = this._createElementCssSelector(nodeName, matchableAttrs);
|
||||
var directives = this._createDirectiveAsts(
|
||||
element.name, this._parseDirectives(this.selectorMatcher, elementCssSelector),
|
||||
elementOrDirectiveProps, element.sourceInfo);
|
||||
elementOrDirectiveProps, isTemplateElement ? [] : vars, element.sourceInfo);
|
||||
var elementProps: BoundElementPropertyAst[] =
|
||||
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directives);
|
||||
var children = htmlVisitAll(this, element.children, Component.create(directives));
|
||||
var elementNgContentIndex =
|
||||
hasInlineTemplates ? null : component.findNgContentIndex(elementCssSelector);
|
||||
var parsedElement;
|
||||
if (nodeName == NG_CONTENT_ELEMENT) {
|
||||
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
|
||||
parsedElement = new NgContentAst(elementNgContentIndex, element.sourceInfo);
|
||||
} else if (nodeName == TEMPLATE_ELEMENT) {
|
||||
} else if (isTemplateElement) {
|
||||
this._assertNoComponentsNorElementBindingsOnTemplate(directives, elementProps, events,
|
||||
element.sourceInfo);
|
||||
parsedElement = new EmbeddedTemplateAst(attrs, vars, directives, children,
|
||||
elementNgContentIndex, element.sourceInfo);
|
||||
} else {
|
||||
this._assertOnlyOneComponent(directives, element.sourceInfo);
|
||||
parsedElement = new ElementAst(nodeName, attrs, elementProps, events, vars, directives,
|
||||
children, elementNgContentIndex, element.sourceInfo);
|
||||
var elementExportAsVars = ListWrapper.filter(vars, varAst => varAst.value.length === 0);
|
||||
parsedElement =
|
||||
new ElementAst(nodeName, attrs, elementProps, events, elementExportAsVars, directives,
|
||||
children, elementNgContentIndex, element.sourceInfo);
|
||||
}
|
||||
if (hasInlineTemplates) {
|
||||
var templateCssSelector =
|
||||
this._createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
|
||||
var templateDirectives = this._createDirectiveAsts(
|
||||
element.name, this._parseDirectives(this.selectorMatcher, templateCssSelector),
|
||||
templateElementOrDirectiveProps, element.sourceInfo);
|
||||
templateElementOrDirectiveProps, [], element.sourceInfo);
|
||||
var templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
|
||||
element.name, templateElementOrDirectiveProps, templateDirectives);
|
||||
this._assertNoComponentsNorElementBindingsOnTemplate(templateDirectives, templateElementProps,
|
||||
@@ -263,8 +284,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
} else if (isPresent(
|
||||
bindParts[2])) { // match: var-name / var-name="iden" / #name / #name="iden"
|
||||
var identifier = bindParts[5];
|
||||
var value = attrValue.length === 0 ? IMPLICIT_VAR_NAME : attrValue;
|
||||
this._parseVariable(identifier, value, attr.sourceInfo, targetMatchableAttrs, targetVars);
|
||||
this._parseVariable(identifier, attrValue, attr.sourceInfo, targetMatchableAttrs,
|
||||
targetVars);
|
||||
|
||||
} else if (isPresent(bindParts[3])) { // match: on-event
|
||||
this._parseEvent(bindParts[5], attrValue, attr.sourceInfo, targetMatchableAttrs,
|
||||
@@ -377,7 +398,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
}
|
||||
|
||||
private _parseDirectives(selectorMatcher: SelectorMatcher,
|
||||
elementCssSelector: CssSelector): NormalizedDirectiveMetadata[] {
|
||||
elementCssSelector: CssSelector): CompileDirectiveMetadata[] {
|
||||
var directives = [];
|
||||
selectorMatcher.match(elementCssSelector,
|
||||
(selector, directive) => { directives.push(directive); });
|
||||
@@ -385,7 +406,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
// as selectorMatcher uses Maps inside.
|
||||
// Also need to make components the first directive in the array
|
||||
ListWrapper.sort(directives,
|
||||
(dir1: NormalizedDirectiveMetadata, dir2: NormalizedDirectiveMetadata) => {
|
||||
(dir1: CompileDirectiveMetadata, dir2: CompileDirectiveMetadata) => {
|
||||
var dir1Comp = dir1.isComponent;
|
||||
var dir2Comp = dir2.isComponent;
|
||||
if (dir1Comp && !dir2Comp) {
|
||||
@@ -393,29 +414,44 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
} else if (!dir1Comp && dir2Comp) {
|
||||
return 1;
|
||||
} else {
|
||||
return StringWrapper.compare(dir1.type.name, dir2.type.name);
|
||||
return this.directivesIndexByTypeId.get(dir1.type.id) -
|
||||
this.directivesIndexByTypeId.get(dir2.type.id);
|
||||
}
|
||||
});
|
||||
return directives;
|
||||
}
|
||||
|
||||
private _createDirectiveAsts(elementName: string, directives: NormalizedDirectiveMetadata[],
|
||||
private _createDirectiveAsts(elementName: string, directives: CompileDirectiveMetadata[],
|
||||
props: BoundElementOrDirectiveProperty[],
|
||||
possibleExportAsVars: VariableAst[],
|
||||
sourceInfo: string): DirectiveAst[] {
|
||||
return directives.map((directive: NormalizedDirectiveMetadata) => {
|
||||
var matchedVariables: Set<string> = new Set();
|
||||
var directiveAsts = directives.map((directive: CompileDirectiveMetadata) => {
|
||||
var hostProperties: BoundElementPropertyAst[] = [];
|
||||
var hostEvents: BoundEventAst[] = [];
|
||||
var directiveProperties: BoundDirectivePropertyAst[] = [];
|
||||
var changeDetection = directive.changeDetection;
|
||||
if (isPresent(changeDetection)) {
|
||||
this._createDirectiveHostPropertyAsts(elementName, changeDetection.hostProperties,
|
||||
sourceInfo, hostProperties);
|
||||
this._createDirectiveHostEventAsts(changeDetection.hostListeners, sourceInfo, hostEvents);
|
||||
this._createDirectivePropertyAsts(changeDetection.properties, props, directiveProperties);
|
||||
}
|
||||
this._createDirectiveHostPropertyAsts(elementName, directive.hostProperties, sourceInfo,
|
||||
hostProperties);
|
||||
this._createDirectiveHostEventAsts(directive.hostListeners, sourceInfo, hostEvents);
|
||||
this._createDirectivePropertyAsts(directive.properties, props, directiveProperties);
|
||||
var exportAsVars = [];
|
||||
possibleExportAsVars.forEach((varAst) => {
|
||||
if ((varAst.value.length === 0 && directive.isComponent) ||
|
||||
(directive.exportAs == varAst.value)) {
|
||||
exportAsVars.push(varAst);
|
||||
matchedVariables.add(varAst.name);
|
||||
}
|
||||
});
|
||||
return new DirectiveAst(directive, directiveProperties, hostProperties, hostEvents,
|
||||
sourceInfo);
|
||||
exportAsVars, sourceInfo);
|
||||
});
|
||||
possibleExportAsVars.forEach((varAst) => {
|
||||
if (varAst.value.length > 0 && !SetWrapper.has(matchedVariables, varAst.name)) {
|
||||
this._reportError(
|
||||
`There is no directive with "exportAs" set to "${varAst.value}" at ${varAst.sourceInfo}`);
|
||||
}
|
||||
});
|
||||
return directiveAsts;
|
||||
}
|
||||
|
||||
private _createDirectiveHostPropertyAsts(elementName: string,
|
||||
@@ -439,7 +475,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private _createDirectivePropertyAsts(directiveProperties: string[],
|
||||
private _createDirectivePropertyAsts(directiveProperties: StringMap<string, string>,
|
||||
boundProps: BoundElementOrDirectiveProperty[],
|
||||
targetBoundDirectiveProps: BoundDirectivePropertyAst[]) {
|
||||
if (isPresent(directiveProperties)) {
|
||||
@@ -447,12 +483,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
boundProps.forEach(boundProp =>
|
||||
boundPropsByName.set(dashCaseToCamelCase(boundProp.name), boundProp));
|
||||
|
||||
directiveProperties.forEach((bindConfig: string) => {
|
||||
// canonical syntax: `dirProp: elProp`
|
||||
// if there is no `:`, use dirProp = elProp
|
||||
var parts = splitAtColon(bindConfig, [bindConfig, bindConfig]);
|
||||
var dirProp = parts[0];
|
||||
var elProp = dashCaseToCamelCase(parts[1]);
|
||||
StringMapWrapper.forEach(directiveProperties, (elProp: string, dirProp: string) => {
|
||||
elProp = dashCaseToCamelCase(elProp);
|
||||
var boundProp = boundPropsByName.get(elProp);
|
||||
|
||||
// Bindings are optional, so this binding only needs to be set up if an expression is given.
|
||||
@@ -566,15 +598,6 @@ export function splitClasses(classAttrValue: string): string[] {
|
||||
return StringWrapper.split(classAttrValue.trim(), /\s+/g);
|
||||
}
|
||||
|
||||
export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
||||
var parts = StringWrapper.split(input.trim(), /\s*:\s*/g);
|
||||
if (parts.length > 1) {
|
||||
return parts;
|
||||
} else {
|
||||
return defaultValues;
|
||||
}
|
||||
}
|
||||
|
||||
class Component {
|
||||
static create(directives: DirectiveAst[]): Component {
|
||||
if (directives.length === 0 || !directives[0].directive.isComponent) {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import {HtmlElementAst} from './html_ast';
|
||||
import {isBlank, isPresent} from 'angular2/src/core/facade/lang';
|
||||
|
||||
const NG_CONTENT_SELECT_ATTR = 'select';
|
||||
const NG_CONTENT_ELEMENT = 'ng-content';
|
||||
const LINK_ELEMENT = 'link';
|
||||
const LINK_STYLE_REL_ATTR = 'rel';
|
||||
const LINK_STYLE_HREF_ATTR = 'href';
|
||||
const LINK_STYLE_REL_VALUE = 'stylesheet';
|
||||
const STYLE_ELEMENT = 'style';
|
||||
const SCRIPT_ELEMENT = 'script';
|
||||
const NG_NON_BINDABLE_ATTR = 'ng-non-bindable';
|
||||
|
||||
export function preparseElement(ast: HtmlElementAst): PreparsedElement {
|
||||
var selectAttr = null;
|
||||
var hrefAttr = null;
|
||||
var relAttr = null;
|
||||
var nonBindable = false;
|
||||
ast.attrs.forEach(attr => {
|
||||
if (attr.name == NG_CONTENT_SELECT_ATTR) {
|
||||
selectAttr = attr.value;
|
||||
} else if (attr.name == LINK_STYLE_HREF_ATTR) {
|
||||
hrefAttr = attr.value;
|
||||
} else if (attr.name == LINK_STYLE_REL_ATTR) {
|
||||
relAttr = attr.value;
|
||||
} else if (attr.name == NG_NON_BINDABLE_ATTR) {
|
||||
nonBindable = true;
|
||||
}
|
||||
});
|
||||
selectAttr = normalizeNgContentSelect(selectAttr);
|
||||
var nodeName = ast.name;
|
||||
var type = PreparsedElementType.OTHER;
|
||||
if (nonBindable) {
|
||||
type = PreparsedElementType.NON_BINDABLE;
|
||||
} else if (nodeName == NG_CONTENT_ELEMENT) {
|
||||
type = PreparsedElementType.NG_CONTENT;
|
||||
} else if (nodeName == STYLE_ELEMENT) {
|
||||
type = PreparsedElementType.STYLE;
|
||||
} else if (nodeName == SCRIPT_ELEMENT) {
|
||||
type = PreparsedElementType.SCRIPT;
|
||||
} else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) {
|
||||
type = PreparsedElementType.STYLESHEET;
|
||||
}
|
||||
return new PreparsedElement(type, selectAttr, hrefAttr);
|
||||
}
|
||||
|
||||
export enum PreparsedElementType {
|
||||
NG_CONTENT,
|
||||
STYLE,
|
||||
STYLESHEET,
|
||||
SCRIPT,
|
||||
NON_BINDABLE,
|
||||
OTHER
|
||||
}
|
||||
|
||||
export class PreparsedElement {
|
||||
constructor(public type: PreparsedElementType, public selectAttr: string,
|
||||
public hrefAttr: string) {}
|
||||
}
|
||||
|
||||
|
||||
function normalizeNgContentSelect(selectAttr: string): string {
|
||||
if (isBlank(selectAttr) || selectAttr.length === 0) {
|
||||
return '*';
|
||||
}
|
||||
return selectAttr;
|
||||
}
|
||||
@@ -72,3 +72,13 @@ export function codeGenValueFn(params: string[], value: string): string {
|
||||
return `function(${params.join(',')}) { return ${value}; }`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
||||
var parts = StringWrapper.split(input.trim(), /\s*:\s*/g);
|
||||
if (parts.length > 1) {
|
||||
return parts;
|
||||
} else {
|
||||
return defaultValues;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ export {
|
||||
DebugContext,
|
||||
ChangeDetectorGenConfig
|
||||
} from './interfaces';
|
||||
export {ChangeDetectionStrategy, changeDetectionStrategyFromJson} from './constants';
|
||||
export {ChangeDetectionStrategy, CHANGE_DECTION_STRATEGY_VALUES} from './constants';
|
||||
export {DynamicProtoChangeDetector} from './proto_change_detector';
|
||||
export {BindingRecord, BindingTarget} from './binding_record';
|
||||
export {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
StringWrapper,
|
||||
normalizeBool,
|
||||
isBlank,
|
||||
serializeEnum,
|
||||
deserializeEnum
|
||||
} from 'angular2/src/core/facade/lang';
|
||||
import {MapWrapper} from 'angular2/src/core/facade/collection';
|
||||
import {StringWrapper, normalizeBool, isBlank} from 'angular2/src/core/facade/lang';
|
||||
|
||||
export enum ChangeDetectionStrategy {
|
||||
/**
|
||||
@@ -48,19 +41,15 @@ export enum ChangeDetectionStrategy {
|
||||
OnPushObserve
|
||||
}
|
||||
|
||||
var strategyMap: Map<number, ChangeDetectionStrategy> = MapWrapper.createFromPairs([
|
||||
[0, ChangeDetectionStrategy.CheckOnce],
|
||||
[1, ChangeDetectionStrategy.Checked],
|
||||
[2, ChangeDetectionStrategy.CheckAlways],
|
||||
[3, ChangeDetectionStrategy.Detached],
|
||||
[4, ChangeDetectionStrategy.OnPush],
|
||||
[5, ChangeDetectionStrategy.Default],
|
||||
[6, ChangeDetectionStrategy.OnPushObserve]
|
||||
]);
|
||||
|
||||
export function changeDetectionStrategyFromJson(value: number): ChangeDetectionStrategy {
|
||||
return deserializeEnum(value, strategyMap);
|
||||
}
|
||||
export var CHANGE_DECTION_STRATEGY_VALUES = [
|
||||
ChangeDetectionStrategy.CheckOnce,
|
||||
ChangeDetectionStrategy.Checked,
|
||||
ChangeDetectionStrategy.CheckAlways,
|
||||
ChangeDetectionStrategy.Detached,
|
||||
ChangeDetectionStrategy.OnPush,
|
||||
ChangeDetectionStrategy.Default,
|
||||
ChangeDetectionStrategy.OnPushObserve
|
||||
];
|
||||
|
||||
export function isDefaultChangeDetectionStrategy(changeDetectionStrategy: ChangeDetectionStrategy):
|
||||
boolean {
|
||||
|
||||
@@ -117,7 +117,6 @@ export class DirectiveResolver {
|
||||
properties: mergedProperties,
|
||||
events: mergedEvents,
|
||||
host: mergedHost,
|
||||
dynamicLoadable: dm.dynamicLoadable,
|
||||
bindings: dm.bindings,
|
||||
exportAs: dm.exportAs,
|
||||
moduleId: dm.moduleId,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {StringMap} from 'angular2/src/core/facade/collection';
|
||||
import {StringMap, MapWrapper} from 'angular2/src/core/facade/collection';
|
||||
|
||||
export enum LifecycleHooks {
|
||||
OnInit,
|
||||
@@ -11,6 +11,17 @@ export enum LifecycleHooks {
|
||||
AfterViewChecked
|
||||
}
|
||||
|
||||
export var LIFECYCLE_HOOKS_VALUES = [
|
||||
LifecycleHooks.OnInit,
|
||||
LifecycleHooks.OnDestroy,
|
||||
LifecycleHooks.DoCheck,
|
||||
LifecycleHooks.OnChanges,
|
||||
LifecycleHooks.AfterContentInit,
|
||||
LifecycleHooks.AfterContentChecked,
|
||||
LifecycleHooks.AfterViewInit,
|
||||
LifecycleHooks.AfterViewChecked
|
||||
];
|
||||
|
||||
/**
|
||||
* Lifecycle hooks are guaranteed to be called in the following order:
|
||||
* - `OnChanges` (if any bindings have changed),
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from 'angular2/src/core/render/render';
|
||||
|
||||
export class CompiledTemplate {
|
||||
private _changeDetectorFactories: Function[] = null;
|
||||
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!
|
||||
@@ -19,15 +19,15 @@ export class CompiledTemplate {
|
||||
private _init() {
|
||||
if (isBlank(this._commands)) {
|
||||
var params = this._paramGetter();
|
||||
this._changeDetectorFactories = params[0];
|
||||
this._changeDetectorFactory = params[0];
|
||||
this._commands = params[1];
|
||||
this._styles = params[2];
|
||||
}
|
||||
}
|
||||
|
||||
get changeDetectorFactories(): Function[] {
|
||||
get changeDetectorFactory(): Function {
|
||||
this._init();
|
||||
return this._changeDetectorFactories;
|
||||
return this._changeDetectorFactory;
|
||||
}
|
||||
|
||||
get styles(): string[] {
|
||||
@@ -69,26 +69,28 @@ export function ngContent(ngContentIndex: number): NgContentCmd {
|
||||
}
|
||||
|
||||
export interface IBeginElementCmd extends TemplateCmd, RenderBeginElementCmd {
|
||||
variableNameAndValues: string[];
|
||||
eventNames: string[];
|
||||
variableNameAndValues: Array<string | number>;
|
||||
eventTargetAndNames: string[];
|
||||
directives: Type[];
|
||||
visit(visitor: CommandVisitor, context: any): any;
|
||||
}
|
||||
|
||||
export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeginElementCmd {
|
||||
constructor(public name: string, public attrNameAndValues: string[], public eventNames: string[],
|
||||
public variableNameAndValues: string[], public directives: Type[],
|
||||
constructor(public name: string, public attrNameAndValues: string[],
|
||||
public eventTargetAndNames: string[],
|
||||
public variableNameAndValues: Array<string | number>, public directives: Type[],
|
||||
public isBound: boolean, public ngContentIndex: number) {}
|
||||
visit(visitor: CommandVisitor, context: any): any {
|
||||
return visitor.visitBeginElement(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
export function beginElement(name: string, attrNameAndValues: string[], eventNames: string[],
|
||||
variableNameAndValues: string[], directives: Type[], isBound: boolean,
|
||||
ngContentIndex: number): BeginElementCmd {
|
||||
return new BeginElementCmd(name, attrNameAndValues, eventNames, variableNameAndValues, directives,
|
||||
isBound, ngContentIndex);
|
||||
export function beginElement(name: string, attrNameAndValues: string[],
|
||||
eventTargetAndNames: string[],
|
||||
variableNameAndValues: Array<string | number>, directives: Type[],
|
||||
isBound: boolean, ngContentIndex: number): BeginElementCmd {
|
||||
return new BeginElementCmd(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues,
|
||||
directives, isBound, ngContentIndex);
|
||||
}
|
||||
|
||||
export class EndElementCmd implements TemplateCmd {
|
||||
@@ -103,8 +105,9 @@ export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderB
|
||||
isBound: boolean = true;
|
||||
templateId: number;
|
||||
component: Type;
|
||||
constructor(public name: string, public attrNameAndValues: string[], public eventNames: string[],
|
||||
public variableNameAndValues: string[], public directives: Type[],
|
||||
constructor(public name: string, public attrNameAndValues: string[],
|
||||
public eventTargetAndNames: string[],
|
||||
public variableNameAndValues: Array<string | number>, public directives: Type[],
|
||||
public nativeShadow: boolean, public ngContentIndex: number,
|
||||
public template: CompiledTemplate) {
|
||||
this.component = directives[0];
|
||||
@@ -115,11 +118,11 @@ export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderB
|
||||
}
|
||||
}
|
||||
|
||||
export function beginComponent(name: string, attrNameAnsValues: string[], eventNames: string[],
|
||||
variableNameAndValues: string[], directives: Type[],
|
||||
nativeShadow: boolean, ngContentIndex: number,
|
||||
template: CompiledTemplate): BeginComponentCmd {
|
||||
return new BeginComponentCmd(name, attrNameAnsValues, eventNames, variableNameAndValues,
|
||||
export function beginComponent(
|
||||
name: string, attrNameAnsValues: string[], eventTargetAndNames: string[],
|
||||
variableNameAndValues: Array<string | number>, directives: Type[], nativeShadow: boolean,
|
||||
ngContentIndex: number, template: CompiledTemplate): BeginComponentCmd {
|
||||
return new BeginComponentCmd(name, attrNameAnsValues, eventTargetAndNames, variableNameAndValues,
|
||||
directives, nativeShadow, ngContentIndex, template);
|
||||
}
|
||||
|
||||
@@ -135,10 +138,10 @@ export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd,
|
||||
RenderEmbeddedTemplateCmd {
|
||||
isBound: boolean = true;
|
||||
name: string = null;
|
||||
eventNames: string[] = EMPTY_ARR;
|
||||
eventTargetAndNames: string[] = EMPTY_ARR;
|
||||
constructor(public attrNameAndValues: string[], public variableNameAndValues: string[],
|
||||
public directives: Type[], public isMerged: boolean, public ngContentIndex: number,
|
||||
public children: TemplateCmd[]) {}
|
||||
public changeDetectorFactory: Function, public children: TemplateCmd[]) {}
|
||||
visit(visitor: CommandVisitor, context: any): any {
|
||||
return visitor.visitEmbeddedTemplate(this, context);
|
||||
}
|
||||
@@ -146,20 +149,13 @@ export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd,
|
||||
|
||||
export function embeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
|
||||
directives: Type[], isMerged: boolean, ngContentIndex: number,
|
||||
children: TemplateCmd[]): EmbeddedTemplateCmd {
|
||||
changeDetectorFactory: Function, children: TemplateCmd[]):
|
||||
EmbeddedTemplateCmd {
|
||||
return new EmbeddedTemplateCmd(attrNameAndValues, variableNameAndValues, directives, isMerged,
|
||||
ngContentIndex, children);
|
||||
ngContentIndex, changeDetectorFactory, children);
|
||||
}
|
||||
|
||||
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 interface CommandVisitor extends RenderCommandVisitor {}
|
||||
|
||||
export function visitAllCommands(visitor: CommandVisitor, cmds: TemplateCmd[],
|
||||
context: any = null) {
|
||||
|
||||
@@ -36,7 +36,7 @@ class Directive extends DirectiveMetadata {
|
||||
*/
|
||||
class Component extends ComponentMetadata {
|
||||
const Component({String selector, List<String> properties,
|
||||
List<String> events, Map<String, String> host, bool dynamicLoadable,
|
||||
List<String> events, Map<String, String> host,
|
||||
List bindings, String exportAs, String moduleId,
|
||||
Map<String, dynamic> queries,
|
||||
bool compileChildren, List viewBindings, ChangeDetectionStrategy changeDetection})
|
||||
@@ -45,7 +45,6 @@ class Component extends ComponentMetadata {
|
||||
properties: properties,
|
||||
events: events,
|
||||
host: host,
|
||||
dynamicLoadable: dynamicLoadable,
|
||||
bindings: bindings,
|
||||
exportAs: exportAs,
|
||||
moduleId: moduleId,
|
||||
|
||||
@@ -76,8 +76,8 @@ export interface ComponentDecorator extends TypeDecorator {
|
||||
View(obj: {
|
||||
templateUrl?: string,
|
||||
template?: string,
|
||||
directives?: Array<Type | any | any[]>,
|
||||
pipes?: Array<Type | any | any[]>,
|
||||
directives?: Array<Type | any[]>,
|
||||
pipes?: Array<Type | any[]>,
|
||||
renderer?: string,
|
||||
styles?: string[],
|
||||
styleUrls?: string[],
|
||||
@@ -96,8 +96,8 @@ export interface ViewDecorator extends TypeDecorator {
|
||||
View(obj: {
|
||||
templateUrl?: string,
|
||||
template?: string,
|
||||
directives?: Array<Type | any | any[]>,
|
||||
pipes?: Array<Type | any | any[]>,
|
||||
directives?: Array<Type | any[]>,
|
||||
pipes?: Array<Type | any[]>,
|
||||
renderer?: string,
|
||||
styles?: string[],
|
||||
styleUrls?: string[],
|
||||
@@ -218,7 +218,6 @@ export interface ComponentFactory {
|
||||
properties?: string[],
|
||||
events?: string[],
|
||||
host?: StringMap<string, string>,
|
||||
dynamicLoadable?: boolean,
|
||||
bindings?: any[],
|
||||
exportAs?: string,
|
||||
moduleId?: string,
|
||||
@@ -232,7 +231,6 @@ export interface ComponentFactory {
|
||||
properties?: string[],
|
||||
events?: string[],
|
||||
host?: StringMap<string, string>,
|
||||
dynamicLoadable?: boolean,
|
||||
bindings?: any[],
|
||||
exportAs?: string,
|
||||
moduleId?: string,
|
||||
@@ -290,7 +288,8 @@ export interface ViewFactory {
|
||||
(obj: {
|
||||
templateUrl?: string,
|
||||
template?: string,
|
||||
directives?: Array<Type | any | any[]>,
|
||||
directives?: Array<Type | any[]>,
|
||||
pipes?: Array<Type | any[]>,
|
||||
encapsulation?: ViewEncapsulation,
|
||||
styles?: string[],
|
||||
styleUrls?: string[],
|
||||
@@ -298,7 +297,8 @@ export interface ViewFactory {
|
||||
new (obj: {
|
||||
templateUrl?: string,
|
||||
template?: string,
|
||||
directives?: Array<Type | any | any[]>,
|
||||
directives?: Array<Type | any[]>,
|
||||
pipes?: Array<Type | any[]>,
|
||||
encapsulation?: ViewEncapsulation,
|
||||
styles?: string[],
|
||||
styleUrls?: string[],
|
||||
|
||||
@@ -826,27 +826,6 @@ export class DirectiveMetadata extends InjectableMetadata {
|
||||
*/
|
||||
@CONST()
|
||||
export class ComponentMetadata extends DirectiveMetadata {
|
||||
/**
|
||||
* Declare that this component can be programatically loaded.
|
||||
* Every component that is used in bootstrap, routing, ... has to be
|
||||
* annotated with this.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* @Component({
|
||||
* selector: 'root',
|
||||
* dynamicLoadable: true
|
||||
* })
|
||||
* @View({
|
||||
* template: 'hello world!'
|
||||
* })
|
||||
* class RootComponent {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
dynamicLoadable: boolean;
|
||||
|
||||
/**
|
||||
* Defines the used change detection strategy.
|
||||
*
|
||||
@@ -900,22 +879,21 @@ export class ComponentMetadata extends DirectiveMetadata {
|
||||
*/
|
||||
viewBindings: any[];
|
||||
|
||||
constructor({selector, properties, events, host, dynamicLoadable, exportAs, moduleId, bindings,
|
||||
queries, viewBindings, changeDetection = ChangeDetectionStrategy.Default,
|
||||
compileChildren = true}: {
|
||||
selector?: string,
|
||||
properties?: string[],
|
||||
events?: string[],
|
||||
host?: StringMap<string, string>,
|
||||
dynamicLoadable?: boolean,
|
||||
bindings?: any[],
|
||||
exportAs?: string,
|
||||
moduleId?: string,
|
||||
compileChildren?: boolean,
|
||||
viewBindings?: any[],
|
||||
queries?: StringMap<string, any>,
|
||||
changeDetection?: ChangeDetectionStrategy,
|
||||
} = {}) {
|
||||
constructor({selector, properties, events, host, exportAs, moduleId, bindings, viewBindings,
|
||||
changeDetection = ChangeDetectionStrategy.Default, queries, compileChildren = true}:
|
||||
{
|
||||
selector?: string,
|
||||
properties?: string[],
|
||||
events?: string[],
|
||||
host?: StringMap<string, string>,
|
||||
bindings?: any[],
|
||||
exportAs?: string,
|
||||
moduleId?: string,
|
||||
compileChildren?: boolean,
|
||||
viewBindings?: any[],
|
||||
queries?: StringMap<string, any>,
|
||||
changeDetection?: ChangeDetectionStrategy,
|
||||
} = {}) {
|
||||
super({
|
||||
selector: selector,
|
||||
properties: properties,
|
||||
@@ -930,7 +908,6 @@ export class ComponentMetadata extends DirectiveMetadata {
|
||||
|
||||
this.changeDetection = changeDetection;
|
||||
this.viewBindings = viewBindings;
|
||||
this.dynamicLoadable = dynamicLoadable;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,12 +82,9 @@ export class ViewMetadata {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
// TODO(tbosch): use Type | Binding | any[] when Dart supports union types,
|
||||
// as otherwise we would need to import Binding type and Dart would warn
|
||||
// for an unused import.
|
||||
directives: Array<Type | any | any[]>;
|
||||
directives: Array<Type | any[]>;
|
||||
|
||||
pipes: Array<Type | any | any[]>;
|
||||
pipes: Array<Type | any[]>;
|
||||
|
||||
/**
|
||||
* Specify how the template and the styles should be encapsulated.
|
||||
@@ -100,8 +97,8 @@ export class ViewMetadata {
|
||||
constructor({templateUrl, template, directives, pipes, encapsulation, styles, styleUrls}: {
|
||||
templateUrl?: string,
|
||||
template?: string,
|
||||
directives?: Array<Type | any | any[]>,
|
||||
pipes?: Array<Type | any | any[]>,
|
||||
directives?: Array<Type | any[]>,
|
||||
pipes?: Array<Type | any[]>,
|
||||
encapsulation?: ViewEncapsulation,
|
||||
styles?: string[],
|
||||
styleUrls?: string[],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {isPresent, isBlank, RegExpWrapper, deserializeEnum} from 'angular2/src/core/facade/lang';
|
||||
import {isPresent, isBlank, RegExpWrapper} from 'angular2/src/core/facade/lang';
|
||||
import {Promise} from 'angular2/src/core/facade/async';
|
||||
import {Map, MapWrapper, StringMap, StringMapWrapper} from 'angular2/src/core/facade/collection';
|
||||
import {
|
||||
@@ -309,11 +309,8 @@ export enum ViewEncapsulation {
|
||||
None
|
||||
}
|
||||
|
||||
var encapsulationMap: Map<number, ViewEncapsulation> = MapWrapper.createFromPairs(
|
||||
[[0, ViewEncapsulation.Emulated], [1, ViewEncapsulation.Native], [2, ViewEncapsulation.None]]);
|
||||
export function viewEncapsulationFromJson(value: number): ViewEncapsulation {
|
||||
return deserializeEnum(value, encapsulationMap);
|
||||
}
|
||||
export var VIEW_ENCAPSULATION_VALUES =
|
||||
[ViewEncapsulation.Emulated, ViewEncapsulation.Native, ViewEncapsulation.None];
|
||||
|
||||
export class ViewDefinition {
|
||||
componentId: string;
|
||||
@@ -409,7 +406,7 @@ export interface RenderNgContentCmd extends RenderBeginCmd { ngContentIndex: num
|
||||
export interface RenderBeginElementCmd extends RenderBeginCmd {
|
||||
name: string;
|
||||
attrNameAndValues: string[];
|
||||
eventNames: string[];
|
||||
eventTargetAndNames: string[];
|
||||
}
|
||||
|
||||
export interface RenderBeginComponentCmd extends RenderBeginElementCmd {
|
||||
@@ -422,15 +419,14 @@ export interface RenderEmbeddedTemplateCmd extends RenderBeginElementCmd {
|
||||
children: RenderTemplateCmd[];
|
||||
}
|
||||
|
||||
// TODO(tbosch): change ts2dart to allow to use `CMD` as type in these methods!
|
||||
export interface RenderCommandVisitor {
|
||||
visitText /*<CMD extends RenderTextCmd>*/ (cmd: any, context: any): any;
|
||||
visitNgContent /*<CMD extends RenderNgContentCmd>*/ (cmd: any, context: any): any;
|
||||
visitBeginElement /*<CMD extends RenderBeginElementCmd>*/ (cmd: any, context: any): any;
|
||||
visitText(cmd: any, context: any): any;
|
||||
visitNgContent(cmd: any, context: any): any;
|
||||
visitBeginElement(cmd: any, context: any): any;
|
||||
visitEndElement(context: any): any;
|
||||
visitBeginComponent /*<CMD extends RenderBeginComponentCmd>*/ (cmd: any, context: any): any;
|
||||
visitBeginComponent(cmd: any, context: any): any;
|
||||
visitEndComponent(context: any): any;
|
||||
visitEmbeddedTemplate /*<CMD extends RenderEmbeddedTemplateCmd>*/ (cmd: any, context: any): any;
|
||||
visitEmbeddedTemplate(cmd: any, context: any): any;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ export class MockDirectiveResolver extends DirectiveResolver {
|
||||
properties: dm.properties,
|
||||
events: dm.events,
|
||||
host: dm.host,
|
||||
dynamicLoadable: dm.dynamicLoadable,
|
||||
bindings: bindings,
|
||||
exportAs: dm.exportAs,
|
||||
moduleId: dm.moduleId,
|
||||
|
||||
@@ -14,9 +14,8 @@ import {
|
||||
} from 'angular2/test_lib';
|
||||
import {MapWrapper} from 'angular2/src/core/facade/collection';
|
||||
import {
|
||||
NormalizedDirectiveMetadata,
|
||||
TypeMetadata,
|
||||
ChangeDetectionMetadata
|
||||
CompileDirectiveMetadata,
|
||||
CompileTypeMetadata
|
||||
} from 'angular2/src/compiler/directive_metadata';
|
||||
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
||||
import {
|
||||
@@ -60,10 +59,10 @@ export function main() {
|
||||
pipes = new TestPipes();
|
||||
}));
|
||||
|
||||
function createChangeDetector(template: string, directives: NormalizedDirectiveMetadata[],
|
||||
function createChangeDetector(template: string, directives: CompileDirectiveMetadata[],
|
||||
protoViewIndex: number = 0): ChangeDetector {
|
||||
var protoChangeDetectors =
|
||||
createChangeDetectorDefinitions(new TypeMetadata({name: 'SomeComp'}),
|
||||
createChangeDetectorDefinitions(new CompileTypeMetadata({name: 'SomeComp'}),
|
||||
ChangeDetectionStrategy.Default,
|
||||
new ChangeDetectorGenConfig(true, true, false, false),
|
||||
parser.parse(template, directives, 'TestComp'))
|
||||
@@ -97,6 +96,14 @@ export function main() {
|
||||
expect(context.eventLog).toEqual(['click']);
|
||||
});
|
||||
|
||||
it('should handle events with targets', () => {
|
||||
var changeDetector = createChangeDetector('<div (window:click)="onEvent($event)">', [], 0);
|
||||
|
||||
eventLocals.set('$event', 'click');
|
||||
changeDetector.handleEvent('window:click', 0, eventLocals);
|
||||
expect(context.eventLog).toEqual(['click']);
|
||||
});
|
||||
|
||||
it('should watch variables', () => {
|
||||
var changeDetector = createChangeDetector('<div #some-var [el-prop]="someVar">', [], 0);
|
||||
|
||||
@@ -106,10 +113,10 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should write directive properties', () => {
|
||||
var dirMeta = new NormalizedDirectiveMetadata({
|
||||
type: new TypeMetadata({name: 'SomeDir'}),
|
||||
selector: 'div',
|
||||
changeDetection: new ChangeDetectionMetadata({properties: ['dirProp']})
|
||||
var dirMeta = CompileDirectiveMetadata.create({
|
||||
type: new CompileTypeMetadata({name: 'SomeDir'}),
|
||||
selector: '[dir-prop]',
|
||||
properties: ['dirProp']
|
||||
});
|
||||
|
||||
var changeDetector = createChangeDetector('<div [dir-prop]="someProp">', [dirMeta], 0);
|
||||
@@ -119,11 +126,25 @@ export function main() {
|
||||
expect(directive.dirProp).toEqual('someValue');
|
||||
});
|
||||
|
||||
it('should write template directive properties', () => {
|
||||
var dirMeta = CompileDirectiveMetadata.create({
|
||||
type: new CompileTypeMetadata({name: 'SomeDir'}),
|
||||
selector: '[dir-prop]',
|
||||
properties: ['dirProp']
|
||||
});
|
||||
|
||||
var changeDetector = createChangeDetector('<template [dir-prop]="someProp">', [dirMeta], 0);
|
||||
|
||||
context.someProp = 'someValue';
|
||||
changeDetector.detectChanges();
|
||||
expect(directive.dirProp).toEqual('someValue');
|
||||
});
|
||||
|
||||
it('should watch directive host properties', () => {
|
||||
var dirMeta = new NormalizedDirectiveMetadata({
|
||||
type: new TypeMetadata({name: 'SomeDir'}),
|
||||
var dirMeta = CompileDirectiveMetadata.create({
|
||||
type: new CompileTypeMetadata({name: 'SomeDir'}),
|
||||
selector: 'div',
|
||||
changeDetection: new ChangeDetectionMetadata({hostProperties: {'elProp': 'dirProp'}})
|
||||
host: {'[elProp]': 'dirProp'}
|
||||
});
|
||||
|
||||
var changeDetector = createChangeDetector('<div>', [dirMeta], 0);
|
||||
@@ -134,11 +155,10 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should handle directive events', () => {
|
||||
var dirMeta = new NormalizedDirectiveMetadata({
|
||||
type: new TypeMetadata({name: 'SomeDir'}),
|
||||
var dirMeta = CompileDirectiveMetadata.create({
|
||||
type: new CompileTypeMetadata({name: 'SomeDir'}),
|
||||
selector: 'div',
|
||||
changeDetection:
|
||||
new ChangeDetectionMetadata({hostListeners: {'click': 'onEvent($event)'}})
|
||||
host: {'(click)': 'onEvent($event)'}
|
||||
});
|
||||
|
||||
var changeDetector = createChangeDetector('<div>', [dirMeta], 0);
|
||||
|
||||
@@ -21,11 +21,15 @@ import {Promise} from 'angular2/src/core/facade/async';
|
||||
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
|
||||
|
||||
import {
|
||||
NormalizedDirectiveMetadata,
|
||||
TypeMetadata,
|
||||
ChangeDetectionMetadata
|
||||
CompileDirectiveMetadata,
|
||||
CompileTypeMetadata
|
||||
} from 'angular2/src/compiler/directive_metadata';
|
||||
import {SourceModule, SourceExpression, moduleRef} from 'angular2/src/compiler/source_module';
|
||||
import {
|
||||
SourceModule,
|
||||
SourceExpression,
|
||||
SourceExpressions,
|
||||
moduleRef
|
||||
} from 'angular2/src/compiler/source_module';
|
||||
|
||||
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
||||
|
||||
@@ -63,8 +67,8 @@ export function main() {
|
||||
|
||||
describe('compileComponentRuntime', () => {
|
||||
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
|
||||
directives: NormalizedDirectiveMetadata[] = CONST_EXPR([])): string[] {
|
||||
var type = new TypeMetadata({name: 'SomeComp'});
|
||||
directives: CompileDirectiveMetadata[] = CONST_EXPR([])): string[] {
|
||||
var type = new CompileTypeMetadata({name: 'SomeComp'});
|
||||
var parsedTemplate = parser.parse(template, directives, 'TestComp');
|
||||
var factories =
|
||||
compiler.compileComponentRuntime(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
||||
@@ -99,13 +103,13 @@ export function main() {
|
||||
|
||||
describe('compileComponentCodeGen', () => {
|
||||
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
|
||||
directives: NormalizedDirectiveMetadata[] = CONST_EXPR([])):
|
||||
directives: CompileDirectiveMetadata[] = CONST_EXPR([])):
|
||||
Promise<string[]> {
|
||||
var type = new TypeMetadata({name: 'SomeComp'});
|
||||
var type = new CompileTypeMetadata({name: 'SomeComp'});
|
||||
var parsedTemplate = parser.parse(template, directives, 'TestComp');
|
||||
var sourceExpression =
|
||||
var sourceExpressions =
|
||||
compiler.compileComponentCodeGen(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
||||
var testableModule = createTestableModule(sourceExpression, 0).getSourceWithImports();
|
||||
var testableModule = createTestableModule(sourceExpressions, 0).getSourceWithImports();
|
||||
return evalModule(testableModule.source, testableModule.imports, null);
|
||||
}
|
||||
|
||||
@@ -122,9 +126,10 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
function createTestableModule(source: SourceExpression, changeDetectorIndex: number): SourceModule {
|
||||
function createTestableModule(source: SourceExpressions, changeDetectorIndex: number):
|
||||
SourceModule {
|
||||
var resultExpression =
|
||||
`${THIS_MODULE_REF}testChangeDetector((${source.expression})[${changeDetectorIndex}])`;
|
||||
`${THIS_MODULE_REF}testChangeDetector(([${source.expressions.join(',')}])[${changeDetectorIndex}])`;
|
||||
var testableSource = `${source.declarations.join('\n')}
|
||||
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
|
||||
return new SourceModule(null, testableSource);
|
||||
|
||||
@@ -29,9 +29,9 @@ import {
|
||||
} from 'angular2/src/core/compiler/template_commands';
|
||||
import {CommandCompiler} from 'angular2/src/compiler/command_compiler';
|
||||
import {
|
||||
NormalizedDirectiveMetadata,
|
||||
TypeMetadata,
|
||||
NormalizedTemplateMetadata
|
||||
CompileDirectiveMetadata,
|
||||
CompileTypeMetadata,
|
||||
CompileTemplateMetadata
|
||||
} from 'angular2/src/compiler/directive_metadata';
|
||||
import {SourceModule, SourceExpression, moduleRef} from 'angular2/src/compiler/source_module';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||
@@ -61,12 +61,12 @@ export class RootComp {}
|
||||
export class SomeDir {}
|
||||
export class AComp {}
|
||||
|
||||
var RootCompTypeMeta =
|
||||
new TypeMetadata({id: 1, name: 'RootComp', runtime: RootComp, moduleId: THIS_MODULE_NAME});
|
||||
var RootCompTypeMeta = new CompileTypeMetadata(
|
||||
{id: 1, name: 'RootComp', runtime: RootComp, moduleId: THIS_MODULE_NAME});
|
||||
var SomeDirTypeMeta =
|
||||
new TypeMetadata({id: 2, name: 'SomeDir', runtime: SomeDir, moduleId: THIS_MODULE_NAME});
|
||||
new CompileTypeMetadata({id: 2, name: 'SomeDir', runtime: SomeDir, moduleId: THIS_MODULE_NAME});
|
||||
var ACompTypeMeta =
|
||||
new TypeMetadata({id: 3, name: 'AComp', runtime: AComp, moduleId: THIS_MODULE_NAME});
|
||||
new CompileTypeMetadata({id: 3, name: 'AComp', runtime: AComp, moduleId: THIS_MODULE_NAME});
|
||||
|
||||
var NESTED_COMPONENT = new CompiledTemplate(45, () => []);
|
||||
|
||||
@@ -84,12 +84,12 @@ export function main() {
|
||||
}));
|
||||
|
||||
function createComp({type, selector, template, encapsulation, ngContentSelectors}: {
|
||||
type?: TypeMetadata,
|
||||
type?: CompileTypeMetadata,
|
||||
selector?: string,
|
||||
template?: string,
|
||||
encapsulation?: ViewEncapsulation,
|
||||
ngContentSelectors?: string[]
|
||||
}): NormalizedDirectiveMetadata {
|
||||
}): CompileDirectiveMetadata {
|
||||
if (isBlank(encapsulation)) {
|
||||
encapsulation = ViewEncapsulation.None;
|
||||
}
|
||||
@@ -102,11 +102,11 @@ export function main() {
|
||||
if (isBlank(template)) {
|
||||
template = '';
|
||||
}
|
||||
return new NormalizedDirectiveMetadata({
|
||||
return CompileDirectiveMetadata.create({
|
||||
selector: selector,
|
||||
isComponent: true,
|
||||
type: type,
|
||||
template: new NormalizedTemplateMetadata({
|
||||
template: new CompileTemplateMetadata({
|
||||
template: template,
|
||||
ngContentSelectors: ngContentSelectors,
|
||||
encapsulation: encapsulation
|
||||
@@ -114,8 +114,10 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
function createDirective(type: TypeMetadata, selector: string): NormalizedDirectiveMetadata {
|
||||
return new NormalizedDirectiveMetadata({selector: selector, isComponent: false, type: type});
|
||||
function createDirective(type: CompileTypeMetadata, selector: string, exportAs: string = null):
|
||||
CompileDirectiveMetadata {
|
||||
return CompileDirectiveMetadata.create(
|
||||
{selector: selector, exportAs: exportAs, isComponent: false, type: type});
|
||||
}
|
||||
|
||||
|
||||
@@ -159,18 +161,47 @@ export function main() {
|
||||
it('should create bound element commands', inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp = createComp({
|
||||
type: RootCompTypeMeta,
|
||||
template: '<div a="b" #some-var="someValue" (click)="someHandler">'
|
||||
template: '<div a="b" #some-var (click)="someHandler" (window:scroll)="scrollTo()">'
|
||||
});
|
||||
var dir = createDirective(SomeDirTypeMeta, '[a]');
|
||||
run(rootComp, [dir])
|
||||
run(rootComp, [])
|
||||
.then((data) => {
|
||||
expect(data).toEqual([
|
||||
[
|
||||
BEGIN_ELEMENT,
|
||||
'div',
|
||||
['a', 'b'],
|
||||
['click'],
|
||||
['someVar', 'someValue'],
|
||||
[null, 'click', 'window', 'scroll'],
|
||||
['someVar', '%implicit'],
|
||||
[],
|
||||
true,
|
||||
null
|
||||
],
|
||||
[END_ELEMENT]
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should create element commands with directives',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp =
|
||||
createComp({type: RootCompTypeMeta, template: '<div a #some-var="someExport">'});
|
||||
var dir = CompileDirectiveMetadata.create({
|
||||
selector: '[a]',
|
||||
exportAs: 'someExport',
|
||||
isComponent: false,
|
||||
type: SomeDirTypeMeta,
|
||||
host: {'(click)': 'doIt()', '(window:scroll)': 'doIt()', 'role': 'button'}
|
||||
});
|
||||
run(rootComp, [dir])
|
||||
.then((data) => {
|
||||
expect(data).toEqual([
|
||||
[
|
||||
BEGIN_ELEMENT,
|
||||
'div',
|
||||
['a', '', 'role', 'button'],
|
||||
[null, 'click', 'window', 'scroll'],
|
||||
['someVar', 0],
|
||||
['SomeDirType'],
|
||||
true,
|
||||
null
|
||||
@@ -214,10 +245,8 @@ export function main() {
|
||||
describe('components', () => {
|
||||
|
||||
it('should create component commands', inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp = createComp({
|
||||
type: RootCompTypeMeta,
|
||||
template: '<a a="b" #some-var="someValue" (click)="someHandler">'
|
||||
});
|
||||
var rootComp = createComp(
|
||||
{type: RootCompTypeMeta, template: '<a a="b" #some-var (click)="someHandler">'});
|
||||
var comp = createComp({type: ACompTypeMeta, selector: 'a'});
|
||||
run(rootComp, [comp])
|
||||
.then((data) => {
|
||||
@@ -226,8 +255,8 @@ export function main() {
|
||||
BEGIN_COMPONENT,
|
||||
'a',
|
||||
['a', 'b'],
|
||||
['click'],
|
||||
['someVar', 'someValue'],
|
||||
[null, 'click'],
|
||||
['someVar', 0],
|
||||
['ACompType'],
|
||||
false,
|
||||
null,
|
||||
@@ -305,7 +334,7 @@ export function main() {
|
||||
template: '<template a="b" #some-var="someValue"></template>'
|
||||
});
|
||||
var dir = createDirective(SomeDirTypeMeta, '[a]');
|
||||
run(rootComp, [dir])
|
||||
run(rootComp, [dir], 1)
|
||||
.then((data) => {
|
||||
expect(data).toEqual([
|
||||
[
|
||||
@@ -315,6 +344,7 @@ export function main() {
|
||||
['SomeDirType'],
|
||||
false,
|
||||
null,
|
||||
'cd1',
|
||||
[]
|
||||
]
|
||||
]);
|
||||
@@ -325,10 +355,20 @@ export function main() {
|
||||
it('should created nested nodes', inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp =
|
||||
createComp({type: RootCompTypeMeta, template: '<template>t</template>'});
|
||||
run(rootComp, [])
|
||||
run(rootComp, [], 1)
|
||||
.then((data) => {
|
||||
expect(data).toEqual(
|
||||
[[EMBEDDED_TEMPLATE, [], [], [], false, null, [[TEXT, 't', false, null]]]]);
|
||||
expect(data).toEqual([
|
||||
[
|
||||
EMBEDDED_TEMPLATE,
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
false,
|
||||
null,
|
||||
'cd1',
|
||||
[[TEXT, 't', false, null]]
|
||||
]
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -339,10 +379,10 @@ export function main() {
|
||||
type: RootCompTypeMeta,
|
||||
template: '<template><ng-content></ng-content></template>'
|
||||
});
|
||||
run(rootComp, [])
|
||||
run(rootComp, [], 1)
|
||||
.then((data) => {
|
||||
expect(data).toEqual(
|
||||
[[EMBEDDED_TEMPLATE, [], [], [], true, null, [[NG_CONTENT, null]]]]);
|
||||
[[EMBEDDED_TEMPLATE, [], [], [], true, null, 'cd1', [[NG_CONTENT, null]]]]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -364,17 +404,21 @@ export function main() {
|
||||
|
||||
describe('compileComponentRuntime', () => {
|
||||
beforeEach(() => {
|
||||
componentTemplateFactory = (directive: NormalizedDirectiveMetadata) => {
|
||||
componentTemplateFactory = (directive: CompileDirectiveMetadata) => {
|
||||
return new CompiledTemplate(directive.type.id, () => []);
|
||||
};
|
||||
});
|
||||
|
||||
function run(component: NormalizedDirectiveMetadata,
|
||||
directives: NormalizedDirectiveMetadata[]): Promise<any[][]> {
|
||||
function run(component: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
|
||||
embeddedTemplateCount: number = 0): Promise<any[][]> {
|
||||
var changeDetectorFactories = [];
|
||||
for (var i = 0; i < embeddedTemplateCount + 1; i++) {
|
||||
(function(i) { changeDetectorFactories.push((_) => `cd${i}`); })(i);
|
||||
}
|
||||
var parsedTemplate =
|
||||
parser.parse(component.template.template, directives, component.type.name);
|
||||
var commands = commandCompiler.compileComponentRuntime(component, parsedTemplate,
|
||||
componentTemplateFactory);
|
||||
var commands = commandCompiler.compileComponentRuntime(
|
||||
component, parsedTemplate, changeDetectorFactories, componentTemplateFactory);
|
||||
return PromiseWrapper.resolve(humanize(commands));
|
||||
}
|
||||
|
||||
@@ -384,17 +428,21 @@ export function main() {
|
||||
|
||||
describe('compileComponentCodeGen', () => {
|
||||
beforeEach(() => {
|
||||
componentTemplateFactory = (directive: NormalizedDirectiveMetadata) => {
|
||||
componentTemplateFactory = (directive: CompileDirectiveMetadata) => {
|
||||
return `new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${directive.type.id}, ${codeGenValueFn([], '{}')})`;
|
||||
};
|
||||
});
|
||||
|
||||
function run(component: NormalizedDirectiveMetadata,
|
||||
directives: NormalizedDirectiveMetadata[]): Promise<any[][]> {
|
||||
function run(component: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
|
||||
embeddedTemplateCount: number = 0): Promise<any[][]> {
|
||||
var changeDetectorFactoryExpressions = [];
|
||||
for (var i = 0; i < embeddedTemplateCount + 1; i++) {
|
||||
changeDetectorFactoryExpressions.push(codeGenValueFn(['_'], `'cd${i}'`));
|
||||
}
|
||||
var parsedTemplate =
|
||||
parser.parse(component.template.template, directives, component.type.name);
|
||||
var sourceModule = commandCompiler.compileComponentCodeGen(component, parsedTemplate,
|
||||
componentTemplateFactory);
|
||||
var sourceModule = commandCompiler.compileComponentCodeGen(
|
||||
component, parsedTemplate, changeDetectorFactoryExpressions, componentTemplateFactory);
|
||||
var testableModule = createTestableModule(sourceModule).getSourceWithImports();
|
||||
return evalModule(testableModule.source, testableModule.imports, null);
|
||||
}
|
||||
@@ -432,7 +480,7 @@ class CommandHumanizer implements CommandVisitor {
|
||||
BEGIN_ELEMENT,
|
||||
cmd.name,
|
||||
cmd.attrNameAndValues,
|
||||
cmd.eventNames,
|
||||
cmd.eventTargetAndNames,
|
||||
cmd.variableNameAndValues,
|
||||
cmd.directives.map(checkAndStringifyType),
|
||||
cmd.isBound,
|
||||
@@ -449,12 +497,11 @@ class CommandHumanizer implements CommandVisitor {
|
||||
BEGIN_COMPONENT,
|
||||
cmd.name,
|
||||
cmd.attrNameAndValues,
|
||||
cmd.eventNames,
|
||||
cmd.eventTargetAndNames,
|
||||
cmd.variableNameAndValues,
|
||||
cmd.directives.map(checkAndStringifyType),
|
||||
cmd.nativeShadow,
|
||||
cmd.ngContentIndex,
|
||||
// TODO humanizeTemplate(cmd.template)
|
||||
cmd.template.id
|
||||
]);
|
||||
return null;
|
||||
@@ -471,6 +518,7 @@ class CommandHumanizer implements CommandVisitor {
|
||||
cmd.directives.map(checkAndStringifyType),
|
||||
cmd.isMerged,
|
||||
cmd.ngContentIndex,
|
||||
cmd.changeDetectorFactory(null),
|
||||
humanize(cmd.children)
|
||||
]);
|
||||
return null;
|
||||
|
||||
@@ -13,98 +13,76 @@ import {
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {
|
||||
NormalizedDirectiveMetadata,
|
||||
TypeMetadata,
|
||||
NormalizedTemplateMetadata,
|
||||
ChangeDetectionMetadata
|
||||
CompileDirectiveMetadata,
|
||||
CompileTypeMetadata,
|
||||
CompileTemplateMetadata
|
||||
} from 'angular2/src/compiler/directive_metadata';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/render/api';
|
||||
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection';
|
||||
import {LifecycleHooks} from 'angular2/src/core/compiler/interfaces';
|
||||
|
||||
export function main() {
|
||||
describe('DirectiveMetadata', () => {
|
||||
var fullTypeMeta: TypeMetadata;
|
||||
var fullTemplateMeta: NormalizedTemplateMetadata;
|
||||
var fullChangeDetectionMeta: ChangeDetectionMetadata;
|
||||
var fullDirectiveMeta: NormalizedDirectiveMetadata;
|
||||
var fullTypeMeta: CompileTypeMetadata;
|
||||
var fullTemplateMeta: CompileTemplateMetadata;
|
||||
var fullDirectiveMeta: CompileDirectiveMetadata;
|
||||
|
||||
beforeEach(() => {
|
||||
fullTypeMeta = new TypeMetadata({id: 23, name: 'SomeType', moduleId: 'someUrl'});
|
||||
fullTemplateMeta = new NormalizedTemplateMetadata({
|
||||
fullTypeMeta = new CompileTypeMetadata({id: 23, name: 'SomeType', moduleId: 'someUrl'});
|
||||
fullTemplateMeta = new CompileTemplateMetadata({
|
||||
encapsulation: ViewEncapsulation.Emulated,
|
||||
template: '<a></a>',
|
||||
templateUrl: 'someTemplateUrl',
|
||||
styles: ['someStyle'],
|
||||
styleAbsUrls: ['someStyleUrl'],
|
||||
hostAttributes: {'attr1': 'attrValue2'},
|
||||
styleUrls: ['someStyleUrl'],
|
||||
ngContentSelectors: ['*']
|
||||
});
|
||||
fullChangeDetectionMeta = new ChangeDetectionMetadata({
|
||||
changeDetection: ChangeDetectionStrategy.Default,
|
||||
properties: ['someProp'],
|
||||
events: ['someEvent'],
|
||||
hostListeners: {'event1': 'handler1'},
|
||||
hostProperties: {'prop1': 'expr1'},
|
||||
callAfterContentInit: true,
|
||||
callAfterContentChecked: true,
|
||||
callAfterViewInit: true,
|
||||
callAfterViewChecked: true,
|
||||
callOnChanges: true,
|
||||
callDoCheck: true,
|
||||
callOnInit: true
|
||||
});
|
||||
fullDirectiveMeta = new NormalizedDirectiveMetadata({
|
||||
fullDirectiveMeta = CompileDirectiveMetadata.create({
|
||||
selector: 'someSelector',
|
||||
isComponent: true,
|
||||
dynamicLoadable: true,
|
||||
type: fullTypeMeta, template: fullTemplateMeta,
|
||||
changeDetection: fullChangeDetectionMeta,
|
||||
changeDetection: ChangeDetectionStrategy.Default,
|
||||
properties: ['someProp'],
|
||||
events: ['someEvent'],
|
||||
host: {'(event1)': 'handler1', '[prop1]': 'expr1', 'attr1': 'attrValue2'},
|
||||
lifecycleHooks: [LifecycleHooks.OnChanges]
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('DirectiveMetadata', () => {
|
||||
it('should serialize with full data', () => {
|
||||
expect(NormalizedDirectiveMetadata.fromJson(fullDirectiveMeta.toJson()))
|
||||
expect(CompileDirectiveMetadata.fromJson(fullDirectiveMeta.toJson()))
|
||||
.toEqual(fullDirectiveMeta);
|
||||
});
|
||||
|
||||
it('should serialize with no data', () => {
|
||||
var empty = new NormalizedDirectiveMetadata();
|
||||
expect(NormalizedDirectiveMetadata.fromJson(empty.toJson())).toEqual(empty);
|
||||
var empty = CompileDirectiveMetadata.create();
|
||||
expect(CompileDirectiveMetadata.fromJson(empty.toJson())).toEqual(empty);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TypeMetadata', () => {
|
||||
it('should serialize with full data',
|
||||
() => { expect(TypeMetadata.fromJson(fullTypeMeta.toJson())).toEqual(fullTypeMeta); });
|
||||
it('should serialize with full data', () => {
|
||||
expect(CompileTypeMetadata.fromJson(fullTypeMeta.toJson())).toEqual(fullTypeMeta);
|
||||
});
|
||||
|
||||
it('should serialize with no data', () => {
|
||||
var empty = new TypeMetadata();
|
||||
expect(TypeMetadata.fromJson(empty.toJson())).toEqual(empty);
|
||||
var empty = new CompileTypeMetadata();
|
||||
expect(CompileTypeMetadata.fromJson(empty.toJson())).toEqual(empty);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TemplateMetadata', () => {
|
||||
it('should serialize with full data', () => {
|
||||
expect(NormalizedTemplateMetadata.fromJson(fullTemplateMeta.toJson()))
|
||||
expect(CompileTemplateMetadata.fromJson(fullTemplateMeta.toJson()))
|
||||
.toEqual(fullTemplateMeta);
|
||||
});
|
||||
|
||||
it('should serialize with no data', () => {
|
||||
var empty = new NormalizedTemplateMetadata();
|
||||
expect(NormalizedTemplateMetadata.fromJson(empty.toJson())).toEqual(empty);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ChangeDetectionMetadata', () => {
|
||||
it('should serialize with full data', () => {
|
||||
expect(ChangeDetectionMetadata.fromJson(fullChangeDetectionMeta.toJson()))
|
||||
.toEqual(fullChangeDetectionMeta);
|
||||
});
|
||||
|
||||
it('should serialize with no data', () => {
|
||||
var empty = new ChangeDetectionMetadata();
|
||||
expect(ChangeDetectionMetadata.fromJson(empty.toJson())).toEqual(empty);
|
||||
var empty = new CompileTemplateMetadata();
|
||||
expect(CompileTemplateMetadata.fromJson(empty.toJson())).toEqual(empty);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -80,22 +80,6 @@ export function main() {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ng-non-bindable', () => {
|
||||
it('should ignore text nodes and elements inside of elements with ng-non-bindable', () => {
|
||||
expect(humanizeDom(
|
||||
parser.parse('<div ng-non-bindable>hello<span></span></div>', 'TestComp')))
|
||||
.toEqual([
|
||||
[HtmlElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||
[
|
||||
HtmlAttrAst,
|
||||
'ng-non-bindable',
|
||||
'',
|
||||
'TestComp > div:nth-child(0)[ng-non-bindable=]'
|
||||
]
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('unparse', () => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user