feat(core): speed up view creation via code gen for view factories.
BREAKING CHANGE: - Platform pipes can only contain types and arrays of types, but no bindings any more. - When using transformers, platform pipes need to be specified explicitly in the pubspec.yaml via the new config option `platform_pipes`. - `Compiler.compileInHost` now returns a `HostViewFactoryRef` - Component view is not yet created when component constructor is called. -> use `onInit` lifecycle callback to access the view of a component - `ViewRef#setLocal` has been moved to new type `EmbeddedViewRef` - `internalView` is gone, use `EmbeddedViewRef.rootNodes` to access the root nodes of an embedded view - `renderer.setElementProperty`, `..setElementStyle`, `..setElementAttribute` now take a native element instead of an ElementRef - `Renderer` interface now operates on plain native nodes, instead of `RenderElementRef`s or `RenderViewRef`s Closes #5993
This commit is contained in:
@@ -169,10 +169,10 @@ export class NgClass implements DoCheck, OnDestroy {
|
||||
if (className.indexOf(' ') > -1) {
|
||||
var classes = className.split(/\s+/g);
|
||||
for (var i = 0, len = classes.length; i < len; i++) {
|
||||
this._renderer.setElementClass(this._ngEl, classes[i], enabled);
|
||||
this._renderer.setElementClass(this._ngEl.nativeElement, classes[i], enabled);
|
||||
}
|
||||
} else {
|
||||
this._renderer.setElementClass(this._ngEl, className, enabled);
|
||||
this._renderer.setElementClass(this._ngEl.nativeElement, className, enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
IterableDiffers,
|
||||
ViewContainerRef,
|
||||
TemplateRef,
|
||||
ViewRef
|
||||
EmbeddedViewRef
|
||||
} from 'angular2/core';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
|
||||
@@ -110,7 +110,8 @@ export class NgFor implements DoCheck {
|
||||
}
|
||||
|
||||
for (var i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
||||
this._viewContainer.get(i).setLocal('last', i === ilen - 1);
|
||||
var viewRef = <EmbeddedViewRef>this._viewContainer.get(i);
|
||||
viewRef.setLocal('last', i === ilen - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +154,7 @@ export class NgFor implements DoCheck {
|
||||
}
|
||||
|
||||
class RecordViewTuple {
|
||||
view: ViewRef;
|
||||
view: EmbeddedViewRef;
|
||||
record: any;
|
||||
constructor(record, view) {
|
||||
this.record = record;
|
||||
|
||||
@@ -92,6 +92,6 @@ export class NgStyle implements DoCheck {
|
||||
}
|
||||
|
||||
private _setStyle(name: string, val: string): void {
|
||||
this._renderer.setElementStyle(this._ngEl, name, val);
|
||||
this._renderer.setElementStyle(this._ngEl.nativeElement, name, val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export class CheckboxControlValueAccessor implements ControlValueAccessor {
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: any): void {
|
||||
this._renderer.setElementProperty(this._elementRef, 'checked', value);
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'checked', value);
|
||||
}
|
||||
registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; }
|
||||
registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
|
||||
|
||||
@@ -31,7 +31,7 @@ export class DefaultValueAccessor implements ControlValueAccessor {
|
||||
|
||||
writeValue(value: any): void {
|
||||
var normalizedValue = isBlank(value) ? '' : value;
|
||||
this._renderer.setElementProperty(this._elementRef, 'value', normalizedValue);
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', normalizedValue);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
|
||||
|
||||
@@ -31,7 +31,7 @@ export class NumberValueAccessor implements ControlValueAccessor {
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: number): void {
|
||||
this._renderer.setElementProperty(this._elementRef, 'value', value);
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', value);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: number) => void): void {
|
||||
|
||||
@@ -51,7 +51,7 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||
|
||||
writeValue(value: any): void {
|
||||
this.value = value;
|
||||
this._renderer.setElementProperty(this._elementRef, 'value', value);
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', value);
|
||||
}
|
||||
|
||||
registerOnChange(fn: () => any): void { this.onChange = fn; }
|
||||
|
||||
@@ -80,7 +80,6 @@ export function selectValueAccessor(dir: NgControl,
|
||||
var defaultAccessor;
|
||||
var builtinAccessor;
|
||||
var customAccessor;
|
||||
|
||||
valueAccessors.forEach(v => {
|
||||
if (v instanceof DefaultValueAccessor) {
|
||||
defaultAccessor = v;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
|
||||
@@ -43,7 +43,7 @@ export function createChangeDetectorDefinitions(
|
||||
|
||||
class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
viewIndex: number;
|
||||
boundTextCount: number = 0;
|
||||
nodeCount: number = 0;
|
||||
boundElementCount: number = 0;
|
||||
variableNames: string[] = [];
|
||||
bindingRecords: BindingRecord[] = [];
|
||||
@@ -57,6 +57,7 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
}
|
||||
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
||||
this.nodeCount++;
|
||||
this.boundElementCount++;
|
||||
templateVisitAll(this, ast.outputs);
|
||||
for (var i = 0; i < ast.directives.length; i++) {
|
||||
@@ -73,6 +74,7 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
}
|
||||
|
||||
visitElement(ast: ElementAst, context: any): any {
|
||||
this.nodeCount++;
|
||||
if (ast.isBound()) {
|
||||
this.boundElementCount++;
|
||||
}
|
||||
@@ -132,14 +134,20 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
}
|
||||
visitAttr(ast: AttrAst, context: any): any { return null; }
|
||||
visitBoundText(ast: BoundTextAst, context: any): any {
|
||||
var boundTextIndex = this.boundTextCount++;
|
||||
this.bindingRecords.push(BindingRecord.createForTextNode(ast.value, boundTextIndex));
|
||||
var nodeIndex = this.nodeCount++;
|
||||
this.bindingRecords.push(BindingRecord.createForTextNode(ast.value, nodeIndex));
|
||||
return null;
|
||||
}
|
||||
visitText(ast: TextAst, context: any): any {
|
||||
this.nodeCount++;
|
||||
return null;
|
||||
}
|
||||
visitText(ast: TextAst, context: any): any { return null; }
|
||||
visitDirective(ast: DirectiveAst, directiveIndexAsNumber: number): any {
|
||||
var directiveIndex = new DirectiveIndex(this.boundElementCount - 1, directiveIndexAsNumber);
|
||||
var directiveMetadata = ast.directive;
|
||||
var outputsArray = [];
|
||||
StringMapWrapper.forEach(ast.directive.outputs, (eventName, dirProperty) => outputsArray.push(
|
||||
[dirProperty, eventName]));
|
||||
var directiveRecord = new DirectiveRecord({
|
||||
directiveIndex: directiveIndex,
|
||||
callAfterContentInit:
|
||||
@@ -153,7 +161,9 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
callOnChanges: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1,
|
||||
callDoCheck: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1,
|
||||
callOnInit: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1,
|
||||
changeDetection: directiveMetadata.changeDetection
|
||||
callOnDestroy: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1,
|
||||
changeDetection: directiveMetadata.changeDetection,
|
||||
outputs: outputsArray
|
||||
});
|
||||
this.directiveRecords.push(directiveRecord);
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@ import {SourceExpressions, moduleRef} from './source_module';
|
||||
import {
|
||||
ChangeDetectorJITGenerator
|
||||
} from 'angular2/src/core/change_detection/change_detection_jit_generator';
|
||||
import {AbstractChangeDetector} from 'angular2/src/core/change_detection/abstract_change_detector';
|
||||
import {ChangeDetectionUtil} from 'angular2/src/core/change_detection/change_detection_util';
|
||||
import {ChangeDetectorState} from 'angular2/src/core/change_detection/constants';
|
||||
|
||||
import {createChangeDetectorDefinitions} from './change_definition_factory';
|
||||
import {IS_DART, isJsObject, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
@@ -23,6 +26,12 @@ const ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
|
||||
const UTIL = "ChangeDetectionUtil";
|
||||
const CHANGE_DETECTOR_STATE = "ChangeDetectorState";
|
||||
|
||||
export const CHANGE_DETECTION_JIT_IMPORTS = CONST_EXPR({
|
||||
'AbstractChangeDetector': AbstractChangeDetector,
|
||||
'ChangeDetectionUtil': ChangeDetectionUtil,
|
||||
'ChangeDetectorState': ChangeDetectorState
|
||||
});
|
||||
|
||||
var ABSTRACT_CHANGE_DETECTOR_MODULE = moduleRef(
|
||||
`package:angular2/src/core/change_detection/abstract_change_detector${MODULE_SUFFIX}`);
|
||||
var UTIL_MODULE =
|
||||
@@ -45,14 +54,8 @@ export class ChangeDetectionCompiler {
|
||||
}
|
||||
|
||||
private _createChangeDetectorFactory(definition: ChangeDetectorDefinition): Function {
|
||||
if (IS_DART || !this._genConfig.useJit) {
|
||||
var proto = new DynamicProtoChangeDetector(definition);
|
||||
return (dispatcher) => proto.instantiate(dispatcher);
|
||||
} else {
|
||||
return new ChangeDetectorJITGenerator(definition, UTIL, ABSTRACT_CHANGE_DETECTOR,
|
||||
CHANGE_DETECTOR_STATE)
|
||||
.generate();
|
||||
}
|
||||
var proto = new DynamicProtoChangeDetector(definition);
|
||||
return () => proto.instantiate();
|
||||
}
|
||||
|
||||
compileComponentCodeGen(componentType: CompileTypeMetadata, strategy: ChangeDetectionStrategy,
|
||||
@@ -81,7 +84,7 @@ export class ChangeDetectionCompiler {
|
||||
definition, `${UTIL_MODULE}${UTIL}`,
|
||||
`${ABSTRACT_CHANGE_DETECTOR_MODULE}${ABSTRACT_CHANGE_DETECTOR}`,
|
||||
`${CONSTANTS_MODULE}${CHANGE_DETECTOR_STATE}`);
|
||||
factories.push(`function(dispatcher) { return new ${codegen.typeName}(dispatcher); }`);
|
||||
factories.push(`function() { return new ${codegen.typeName}(); }`);
|
||||
sourcePart = codegen.generateSource();
|
||||
}
|
||||
index++;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,8 @@ import {TemplateNormalizer} from 'angular2/src/compiler/template_normalizer';
|
||||
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
|
||||
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
|
||||
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
|
||||
import {CommandCompiler} from 'angular2/src/compiler/command_compiler';
|
||||
import {ViewCompiler} from 'angular2/src/compiler/view_compiler';
|
||||
import {ProtoViewCompiler} from 'angular2/src/compiler/proto_view_compiler';
|
||||
import {TemplateCompiler} from 'angular2/src/compiler/template_compiler';
|
||||
import {ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {Compiler} from 'angular2/src/core/linker/compiler';
|
||||
@@ -44,7 +45,8 @@ export const COMPILER_PROVIDERS: Array<Type | Provider | any[]> = CONST_EXPR([
|
||||
RuntimeMetadataResolver,
|
||||
DEFAULT_PACKAGE_URL_PROVIDER,
|
||||
StyleCompiler,
|
||||
CommandCompiler,
|
||||
ProtoViewCompiler,
|
||||
ViewCompiler,
|
||||
ChangeDetectionCompiler,
|
||||
new Provider(ChangeDetectorGenConfig, {useFactory: _createChangeDetectorGenConfig, deps: []}),
|
||||
TemplateCompiler,
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
RegExpWrapper,
|
||||
StringWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
@@ -21,6 +22,16 @@ import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/i
|
||||
// group 2: "event" from "(event)"
|
||||
var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
|
||||
|
||||
export abstract class CompileMetadataWithType {
|
||||
static fromJson(data: {[key: string]: any}): CompileMetadataWithType {
|
||||
return _COMPILE_METADATA_FROM_JSON[data['class']](data);
|
||||
}
|
||||
|
||||
abstract toJson(): {[key: string]: any};
|
||||
|
||||
get type(): CompileTypeMetadata { return unimplemented(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata regarding compilation of a type.
|
||||
*/
|
||||
@@ -107,7 +118,7 @@ export class CompileTemplateMetadata {
|
||||
/**
|
||||
* Metadata regarding compilation of a directive.
|
||||
*/
|
||||
export class CompileDirectiveMetadata {
|
||||
export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||
static create({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, inputs,
|
||||
outputs, host, lifecycleHooks, template}: {
|
||||
type?: CompileTypeMetadata,
|
||||
@@ -241,6 +252,7 @@ export class CompileDirectiveMetadata {
|
||||
|
||||
toJson(): {[key: string]: any} {
|
||||
return {
|
||||
'class': 'Directive',
|
||||
'isComponent': this.isComponent,
|
||||
'dynamicLoadable': this.dynamicLoadable,
|
||||
'selector': this.selector,
|
||||
@@ -284,3 +296,38 @@ export function createHostComponentMeta(componentType: CompileTypeMetadata,
|
||||
selector: '*'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export class CompilePipeMetadata implements CompileMetadataWithType {
|
||||
type: CompileTypeMetadata;
|
||||
name: string;
|
||||
pure: boolean;
|
||||
constructor({type, name,
|
||||
pure}: {type?: CompileTypeMetadata, name?: string, pure?: boolean} = {}) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.pure = normalizeBool(pure);
|
||||
}
|
||||
|
||||
static fromJson(data: {[key: string]: any}): CompilePipeMetadata {
|
||||
return new CompilePipeMetadata({
|
||||
type: isPresent(data['type']) ? CompileTypeMetadata.fromJson(data['type']) : data['type'],
|
||||
name: data['name'],
|
||||
pure: data['pure']
|
||||
});
|
||||
}
|
||||
|
||||
toJson(): {[key: string]: any} {
|
||||
return {
|
||||
'class': 'Pipe',
|
||||
'type': isPresent(this.type) ? this.type.toJson() : null,
|
||||
'name': this.name,
|
||||
'pure': this.pure
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var _COMPILE_METADATA_FROM_JSON = {
|
||||
'Directive': CompileDirectiveMetadata.fromJson,
|
||||
'Pipe': CompilePipeMetadata.fromJson
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,23 +1,23 @@
|
||||
import {Compiler, Compiler_, internalCreateProtoView} from 'angular2/src/core/linker/compiler';
|
||||
import {ProtoViewRef} from 'angular2/src/core/linker/view_ref';
|
||||
import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
|
||||
import {Compiler, Compiler_} from 'angular2/src/core/linker/compiler';
|
||||
import {HostViewFactoryRef, HostViewFactoryRef_} from 'angular2/src/core/linker/view_ref';
|
||||
import {TemplateCompiler} from './template_compiler';
|
||||
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {Type} from 'angular2/src/facade/lang';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
export abstract class RuntimeCompiler extends Compiler {}
|
||||
export abstract class RuntimeCompiler extends Compiler {
|
||||
abstract compileInHost(componentType: Type): Promise<HostViewFactoryRef>;
|
||||
abstract clearCache();
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class RuntimeCompiler_ extends Compiler_ implements RuntimeCompiler {
|
||||
constructor(_protoViewFactory: ProtoViewFactory, private _templateCompiler: TemplateCompiler) {
|
||||
super(_protoViewFactory);
|
||||
}
|
||||
constructor(private _templateCompiler: TemplateCompiler) { super(); }
|
||||
|
||||
compileInHost(componentType: Type): Promise<ProtoViewRef> {
|
||||
compileInHost(componentType: Type): Promise<HostViewFactoryRef_> {
|
||||
return this._templateCompiler.compileHostComponentRuntime(componentType)
|
||||
.then(compiledHostTemplate => internalCreateProtoView(this, compiledHostTemplate));
|
||||
.then(hostViewFactory => new HostViewFactoryRef_(hostViewFactory));
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
|
||||
@@ -11,25 +11,29 @@ import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import * as cpl from './directive_metadata';
|
||||
import * as md from 'angular2/src/core/metadata/directives';
|
||||
import {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver';
|
||||
import {PipeResolver} from 'angular2/src/core/linker/pipe_resolver';
|
||||
import {ViewResolver} from 'angular2/src/core/linker/view_resolver';
|
||||
import {ViewMetadata} from 'angular2/src/core/metadata/view';
|
||||
import {hasLifecycleHook} from 'angular2/src/core/linker/directive_lifecycle_reflector';
|
||||
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/interfaces';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
import {Injectable, Inject, Optional} from 'angular2/src/core/di';
|
||||
import {PLATFORM_DIRECTIVES} from 'angular2/src/core/platform_directives_and_pipes';
|
||||
import {PLATFORM_DIRECTIVES, PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes';
|
||||
import {MODULE_SUFFIX} from './util';
|
||||
import {getUrlScheme} from 'angular2/src/compiler/url_resolver';
|
||||
|
||||
@Injectable()
|
||||
export class RuntimeMetadataResolver {
|
||||
private _cache = new Map<Type, cpl.CompileDirectiveMetadata>();
|
||||
private _directiveCache = new Map<Type, cpl.CompileDirectiveMetadata>();
|
||||
private _pipeCache = new Map<Type, cpl.CompilePipeMetadata>();
|
||||
|
||||
constructor(private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver,
|
||||
@Optional() @Inject(PLATFORM_DIRECTIVES) private _platformDirectives: Type[]) {}
|
||||
constructor(private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver,
|
||||
private _viewResolver: ViewResolver,
|
||||
@Optional() @Inject(PLATFORM_DIRECTIVES) private _platformDirectives: Type[],
|
||||
@Optional() @Inject(PLATFORM_PIPES) private _platformPipes: Type[]) {}
|
||||
|
||||
getMetadata(directiveType: Type): cpl.CompileDirectiveMetadata {
|
||||
var meta = this._cache.get(directiveType);
|
||||
getDirectiveMetadata(directiveType: Type): cpl.CompileDirectiveMetadata {
|
||||
var meta = this._directiveCache.get(directiveType);
|
||||
if (isBlank(meta)) {
|
||||
var dirMeta = this._directiveResolver.resolve(directiveType);
|
||||
var moduleUrl = null;
|
||||
@@ -63,7 +67,23 @@ export class RuntimeMetadataResolver {
|
||||
host: dirMeta.host,
|
||||
lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, directiveType))
|
||||
});
|
||||
this._cache.set(directiveType, meta);
|
||||
this._directiveCache.set(directiveType, meta);
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
|
||||
getPipeMetadata(pipeType: Type): cpl.CompilePipeMetadata {
|
||||
var meta = this._pipeCache.get(pipeType);
|
||||
if (isBlank(meta)) {
|
||||
var pipeMeta = this._pipeResolver.resolve(pipeType);
|
||||
var moduleUrl = reflector.importUri(pipeType);
|
||||
meta = new cpl.CompilePipeMetadata({
|
||||
type: new cpl.CompileTypeMetadata(
|
||||
{name: stringify(pipeType), moduleUrl: moduleUrl, runtime: pipeType}),
|
||||
name: pipeMeta.name,
|
||||
pure: pipeMeta.pure
|
||||
});
|
||||
this._pipeCache.set(pipeType, meta);
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
@@ -72,13 +92,25 @@ export class RuntimeMetadataResolver {
|
||||
var view = this._viewResolver.resolve(component);
|
||||
var directives = flattenDirectives(view, this._platformDirectives);
|
||||
for (var i = 0; i < directives.length; i++) {
|
||||
if (!isValidDirective(directives[i])) {
|
||||
if (!isValidType(directives[i])) {
|
||||
throw new BaseException(
|
||||
`Unexpected directive value '${stringify(directives[i])}' on the View of component '${stringify(component)}'`);
|
||||
}
|
||||
}
|
||||
|
||||
return directives.map(type => this.getMetadata(type));
|
||||
return directives.map(type => this.getDirectiveMetadata(type));
|
||||
}
|
||||
|
||||
getViewPipesMetadata(component: Type): cpl.CompilePipeMetadata[] {
|
||||
var view = this._viewResolver.resolve(component);
|
||||
var pipes = flattenPipes(view, this._platformPipes);
|
||||
for (var i = 0; i < pipes.length; i++) {
|
||||
if (!isValidType(pipes[i])) {
|
||||
throw new BaseException(
|
||||
`Unexpected piped value '${stringify(pipes[i])}' on the View of component '${stringify(component)}'`);
|
||||
}
|
||||
}
|
||||
return pipes.map(type => this.getPipeMetadata(type));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +125,17 @@ function flattenDirectives(view: ViewMetadata, platformDirectives: any[]): Type[
|
||||
return directives;
|
||||
}
|
||||
|
||||
function flattenPipes(view: ViewMetadata, platformPipes: any[]): Type[] {
|
||||
let pipes = [];
|
||||
if (isPresent(platformPipes)) {
|
||||
flattenArray(platformPipes, pipes);
|
||||
}
|
||||
if (isPresent(view.pipes)) {
|
||||
flattenArray(view.pipes, pipes);
|
||||
}
|
||||
return pipes;
|
||||
}
|
||||
|
||||
function flattenArray(tree: any[], out: Array<Type | any[]>): void {
|
||||
for (var i = 0; i < tree.length; i++) {
|
||||
var item = resolveForwardRef(tree[i]);
|
||||
@@ -104,7 +147,7 @@ function flattenArray(tree: any[], out: Array<Type | any[]>): void {
|
||||
}
|
||||
}
|
||||
|
||||
function isValidDirective(value: Type): boolean {
|
||||
function isValidType(value: Type): boolean {
|
||||
return isPresent(value) && (value instanceof Type);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ export function moduleRef(moduleUrl): string {
|
||||
* Represents generated source code with module references. Internal to the Angular compiler.
|
||||
*/
|
||||
export class SourceModule {
|
||||
static getSourceWithoutImports(sourceWithModuleRefs: string): string {
|
||||
return StringWrapper.replaceAllMapped(sourceWithModuleRefs, MODULE_REGEXP, (match) => '');
|
||||
}
|
||||
|
||||
constructor(public moduleUrl: string, public sourceWithModuleRefs: string) {}
|
||||
|
||||
getSourceWithImports(): SourceWithImports {
|
||||
|
||||
@@ -14,7 +14,10 @@ import {
|
||||
MODULE_SUFFIX
|
||||
} from './util';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {COMPONENT_VARIABLE, HOST_ATTR, CONTENT_ATTR} from 'angular2/src/core/render/view_factory';
|
||||
|
||||
const COMPONENT_VARIABLE = '%COMP%';
|
||||
const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
|
||||
const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
||||
|
||||
@Injectable()
|
||||
export class StyleCompiler {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,10 +5,11 @@ import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/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 {CompileDirectiveMetadata} from './directive_metadata';
|
||||
import {CompileDirectiveMetadata, CompilePipeMetadata} from './directive_metadata';
|
||||
import {HtmlParser} from './html_parser';
|
||||
import {splitNsName} from './html_tags';
|
||||
import {ParseSourceSpan, ParseError, ParseLocation} from './parse_util';
|
||||
import {RecursiveAstVisitor, BindingPipe} from 'angular2/src/core/change_detection/parser/ast';
|
||||
|
||||
|
||||
import {
|
||||
@@ -88,9 +89,10 @@ export class TemplateParser {
|
||||
private _htmlParser: HtmlParser,
|
||||
@Optional() @Inject(TEMPLATE_TRANSFORMS) public transforms: TemplateAstVisitor[]) {}
|
||||
|
||||
parse(template: string, directives: CompileDirectiveMetadata[],
|
||||
parse(template: string, directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
||||
templateUrl: string): TemplateAst[] {
|
||||
var parseVisitor = new TemplateParseVisitor(directives, this._exprParser, this._schemaRegistry);
|
||||
var parseVisitor =
|
||||
new TemplateParseVisitor(directives, pipes, this._exprParser, this._schemaRegistry);
|
||||
var htmlAstWithErrors = this._htmlParser.parse(template, templateUrl);
|
||||
var result = htmlVisitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_COMPONENT);
|
||||
var errors: ParseError[] = htmlAstWithErrors.errors.concat(parseVisitor.errors);
|
||||
@@ -111,9 +113,10 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
errors: TemplateParseError[] = [];
|
||||
directivesIndex = new Map<CompileDirectiveMetadata, number>();
|
||||
ngContentCount: number = 0;
|
||||
pipesByName: Map<string, CompilePipeMetadata>;
|
||||
|
||||
constructor(directives: CompileDirectiveMetadata[], private _exprParser: Parser,
|
||||
private _schemaRegistry: ElementSchemaRegistry) {
|
||||
constructor(directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
||||
private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry) {
|
||||
this.selectorMatcher = new SelectorMatcher();
|
||||
ListWrapper.forEachWithIndex(directives,
|
||||
(directive: CompileDirectiveMetadata, index: number) => {
|
||||
@@ -121,6 +124,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
this.selectorMatcher.addSelectables(selector, directive);
|
||||
this.directivesIndex.set(directive, index);
|
||||
});
|
||||
this.pipesByName = new Map<string, CompilePipeMetadata>();
|
||||
pipes.forEach(pipe => this.pipesByName.set(pipe.name, pipe));
|
||||
}
|
||||
|
||||
private _reportError(message: string, sourceSpan: ParseSourceSpan) {
|
||||
@@ -130,7 +135,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
private _parseInterpolation(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
||||
var sourceInfo = sourceSpan.start.toString();
|
||||
try {
|
||||
return this._exprParser.parseInterpolation(value, sourceInfo);
|
||||
var ast = this._exprParser.parseInterpolation(value, sourceInfo);
|
||||
this._checkPipes(ast, sourceSpan);
|
||||
return ast;
|
||||
} catch (e) {
|
||||
this._reportError(`${e}`, sourceSpan);
|
||||
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
||||
@@ -140,7 +147,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
private _parseAction(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
||||
var sourceInfo = sourceSpan.start.toString();
|
||||
try {
|
||||
return this._exprParser.parseAction(value, sourceInfo);
|
||||
var ast = this._exprParser.parseAction(value, sourceInfo);
|
||||
this._checkPipes(ast, sourceSpan);
|
||||
return ast;
|
||||
} catch (e) {
|
||||
this._reportError(`${e}`, sourceSpan);
|
||||
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
||||
@@ -150,7 +159,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
private _parseBinding(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
||||
var sourceInfo = sourceSpan.start.toString();
|
||||
try {
|
||||
return this._exprParser.parseBinding(value, sourceInfo);
|
||||
var ast = this._exprParser.parseBinding(value, sourceInfo);
|
||||
this._checkPipes(ast, sourceSpan);
|
||||
return ast;
|
||||
} catch (e) {
|
||||
this._reportError(`${e}`, sourceSpan);
|
||||
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
||||
@@ -160,13 +171,31 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
private _parseTemplateBindings(value: string, sourceSpan: ParseSourceSpan): TemplateBinding[] {
|
||||
var sourceInfo = sourceSpan.start.toString();
|
||||
try {
|
||||
return this._exprParser.parseTemplateBindings(value, sourceInfo);
|
||||
var bindings = this._exprParser.parseTemplateBindings(value, sourceInfo);
|
||||
bindings.forEach((binding) => {
|
||||
if (isPresent(binding.expression)) {
|
||||
this._checkPipes(binding.expression, sourceSpan);
|
||||
}
|
||||
});
|
||||
return bindings;
|
||||
} catch (e) {
|
||||
this._reportError(`${e}`, sourceSpan);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private _checkPipes(ast: ASTWithSource, sourceSpan: ParseSourceSpan) {
|
||||
if (isPresent(ast)) {
|
||||
var collector = new PipeCollector();
|
||||
ast.visit(collector);
|
||||
collector.pipes.forEach((pipeName) => {
|
||||
if (!this.pipesByName.has(pipeName)) {
|
||||
this._reportError(`The pipe '${pipeName}' could not be found`, sourceSpan);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
visitText(ast: HtmlTextAst, component: Component): any {
|
||||
var ngContentIndex = component.findNgContentIndex(TEXT_CSS_SELECTOR);
|
||||
var expr = this._parseInterpolation(ast.value, ast.sourceSpan);
|
||||
@@ -714,3 +743,14 @@ function createElementCssSelector(elementName: string, matchableAttrs: string[][
|
||||
|
||||
var EMPTY_COMPONENT = new Component(new SelectorMatcher(), null);
|
||||
var NON_BINDABLE_VISITOR = new NonBindableVisitor();
|
||||
|
||||
|
||||
export class PipeCollector extends RecursiveAstVisitor {
|
||||
pipes: Set<string> = new Set<string>();
|
||||
visitPipe(ast: BindingPipe): any {
|
||||
this.pipes.add(ast.name);
|
||||
ast.exp.visit(this);
|
||||
this.visitAll(ast.args);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import {IS_DART, StringWrapper, isBlank} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
IS_DART,
|
||||
StringWrapper,
|
||||
isBlank,
|
||||
isPresent,
|
||||
isString,
|
||||
isArray
|
||||
} from 'angular2/src/facade/lang';
|
||||
|
||||
var CAMEL_CASE_REGEXP = /([A-Z])/g;
|
||||
var DASH_CASE_REGEXP = /-([a-z])/g;
|
||||
@@ -7,6 +14,8 @@ var DOUBLE_QUOTE_ESCAPE_STRING_RE = /"|\\|\n|\r|\$/g;
|
||||
|
||||
export var MODULE_SUFFIX = IS_DART ? '.dart' : '.js';
|
||||
|
||||
export var CONST_VAR = IS_DART ? 'const' : 'var';
|
||||
|
||||
export function camelCaseToDashCase(input: string): string {
|
||||
return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP,
|
||||
(m) => { return '-' + m[1].toLowerCase(); });
|
||||
@@ -63,12 +72,19 @@ export function codeGenConstConstructorCall(name: string): string {
|
||||
|
||||
export function codeGenValueFn(params: string[], value: string, fnName: string = ''): string {
|
||||
if (IS_DART) {
|
||||
return `${fnName}(${params.join(',')}) => ${value}`;
|
||||
return `${codeGenFnHeader(params, fnName)} => ${value}`;
|
||||
} else {
|
||||
return `function ${fnName}(${params.join(',')}) { return ${value}; }`;
|
||||
return `${codeGenFnHeader(params, fnName)} { return ${value}; }`;
|
||||
}
|
||||
}
|
||||
|
||||
export function codeGenFnHeader(params: string[], fnName: string = ''): string {
|
||||
if (IS_DART) {
|
||||
return `${fnName}(${params.join(',')})`;
|
||||
} else {
|
||||
return `function ${fnName}(${params.join(',')})`;
|
||||
}
|
||||
}
|
||||
export function codeGenToString(expr: string): string {
|
||||
if (IS_DART) {
|
||||
return `'\${${expr}}'`;
|
||||
@@ -86,3 +102,77 @@ export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
||||
return defaultValues;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class Statement {
|
||||
constructor(public statement: string) {}
|
||||
}
|
||||
|
||||
export class Expression {
|
||||
constructor(public expression: string, public isArray = false) {}
|
||||
}
|
||||
|
||||
export function escapeValue(value: any): string {
|
||||
if (value instanceof Expression) {
|
||||
return value.expression;
|
||||
} else if (isString(value)) {
|
||||
return escapeSingleQuoteString(value);
|
||||
} else if (isBlank(value)) {
|
||||
return 'null';
|
||||
} else {
|
||||
return `${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function codeGenArray(data: any[]): string {
|
||||
return `[${data.map(escapeValue).join(',')}]`;
|
||||
}
|
||||
|
||||
export function codeGenFlatArray(values: any[]): string {
|
||||
var result = '([';
|
||||
var isFirstArrayEntry = true;
|
||||
var concatFn = IS_DART ? '.addAll' : 'concat';
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var value = values[i];
|
||||
if (value instanceof Expression && (<Expression>value).isArray) {
|
||||
result += `]).${concatFn}(${value.expression}).${concatFn}([`;
|
||||
isFirstArrayEntry = true;
|
||||
} else {
|
||||
if (!isFirstArrayEntry) {
|
||||
result += ',';
|
||||
}
|
||||
isFirstArrayEntry = false;
|
||||
result += escapeValue(value);
|
||||
}
|
||||
}
|
||||
result += '])';
|
||||
return result;
|
||||
}
|
||||
|
||||
export function codeGenStringMap(keyValueArray: any[][]): string {
|
||||
return `{${keyValueArray.map(codeGenKeyValue).join(',')}}`;
|
||||
}
|
||||
|
||||
function codeGenKeyValue(keyValue: any[]): string {
|
||||
return `${escapeValue(keyValue[0])}:${escapeValue(keyValue[1])}`;
|
||||
}
|
||||
|
||||
export function addAll(source: any[], target: any[]) {
|
||||
for (var i = 0; i < source.length; i++) {
|
||||
target.push(source[i]);
|
||||
}
|
||||
}
|
||||
|
||||
export function flattenArray(source: any[], target: any[]): any[] {
|
||||
if (isPresent(source)) {
|
||||
for (var i = 0; i < source.length; i++) {
|
||||
var item = source[i];
|
||||
if (isArray(item)) {
|
||||
flattenArray(item, target);
|
||||
} else {
|
||||
target.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,13 +11,11 @@ import {
|
||||
KeyValueDiffers,
|
||||
defaultKeyValueDiffers
|
||||
} from './change_detection/change_detection';
|
||||
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from './linker/view_pool';
|
||||
import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
|
||||
import {AppViewManager} from './linker/view_manager';
|
||||
import {AppViewManager_} from "./linker/view_manager";
|
||||
import {AppViewManagerUtils} from './linker/view_manager_utils';
|
||||
import {ViewResolver} from './linker/view_resolver';
|
||||
import {AppViewListener} from './linker/view_listener';
|
||||
import {ProtoViewFactory} from './linker/proto_view_factory';
|
||||
import {DirectiveResolver} from './linker/directive_resolver';
|
||||
import {PipeResolver} from './linker/pipe_resolver';
|
||||
import {Compiler} from './linker/compiler';
|
||||
@@ -32,12 +30,9 @@ import {DynamicComponentLoader_} from "./linker/dynamic_component_loader";
|
||||
export const APPLICATION_COMMON_PROVIDERS: Array<Type | Provider | any[]> = CONST_EXPR([
|
||||
new Provider(Compiler, {useClass: Compiler_}),
|
||||
APP_ID_RANDOM_PROVIDER,
|
||||
AppViewPool,
|
||||
new Provider(APP_VIEW_POOL_CAPACITY, {useValue: 10000}),
|
||||
ResolvedMetadataCache,
|
||||
new Provider(AppViewManager, {useClass: AppViewManager_}),
|
||||
AppViewManagerUtils,
|
||||
AppViewListener,
|
||||
ProtoViewFactory,
|
||||
ViewResolver,
|
||||
new Provider(IterableDiffers, {useValue: defaultIterableDiffers}),
|
||||
new Provider(KeyValueDiffers, {useValue: defaultKeyValueDiffers}),
|
||||
|
||||
@@ -33,11 +33,11 @@ import {
|
||||
ExceptionHandler,
|
||||
unimplemented
|
||||
} from 'angular2/src/facade/exceptions';
|
||||
import {internalView} from 'angular2/src/core/linker/view_ref';
|
||||
import {Console} from 'angular2/src/core/console';
|
||||
import {wtfLeave, wtfCreateScope, WtfScopeFn} from './profile/profile';
|
||||
import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detector_ref';
|
||||
import {lockMode} from 'angular2/src/facade/lang';
|
||||
import {ElementRef_} from 'angular2/src/core/linker/element_ref';
|
||||
|
||||
/**
|
||||
* Construct providers specific to an individual root component.
|
||||
@@ -56,10 +56,10 @@ function _componentProviders(appComponentType: Type): Array<Type | Provider | an
|
||||
() => { appRef._unloadComponent(ref); })
|
||||
.then((componentRef) => {
|
||||
ref = componentRef;
|
||||
if (isPresent(componentRef.location.nativeElement)) {
|
||||
var testability = injector.getOptional(Testability);
|
||||
if (isPresent(testability)) {
|
||||
injector.get(TestabilityRegistry)
|
||||
.registerApplication(componentRef.location.nativeElement,
|
||||
injector.get(Testability));
|
||||
.registerApplication(componentRef.location.nativeElement, testability);
|
||||
}
|
||||
return componentRef;
|
||||
});
|
||||
@@ -439,7 +439,7 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||
|
||||
/** @internal */
|
||||
_loadComponent(ref): void {
|
||||
var appChangeDetector = internalView(ref.hostView).changeDetector;
|
||||
var appChangeDetector = (<ElementRef_>ref.location).internalElement.parentView.changeDetector;
|
||||
this._changeDetectorRefs.push(appChangeDetector.ref);
|
||||
this.tick();
|
||||
this._rootComponents.push(ref);
|
||||
@@ -451,7 +451,8 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||
if (!ListWrapper.contains(this._rootComponents, ref)) {
|
||||
return;
|
||||
}
|
||||
this.unregisterChangeDetector(internalView(ref.hostView).changeDetector.ref);
|
||||
this.unregisterChangeDetector(
|
||||
(<ElementRef_>ref.location).internalElement.parentView.changeDetector.ref);
|
||||
ListWrapper.remove(this._rootComponents, ref);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@ import {Pipes} from './pipes';
|
||||
import {
|
||||
ChangeDetectionError,
|
||||
ExpressionChangedAfterItHasBeenCheckedException,
|
||||
DehydratedException
|
||||
DehydratedException,
|
||||
EventEvaluationErrorContext,
|
||||
EventEvaluationError
|
||||
} from './exceptions';
|
||||
import {BindingTarget} from './binding_record';
|
||||
import {Locals} from './parser/locals';
|
||||
@@ -43,9 +45,12 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
subscriptions: any[];
|
||||
streams: any[];
|
||||
|
||||
constructor(public id: string, public dispatcher: ChangeDispatcher,
|
||||
public numberOfPropertyProtoRecords: number, public bindingTargets: BindingTarget[],
|
||||
public directiveIndices: DirectiveIndex[], public strategy: ChangeDetectionStrategy) {
|
||||
dispatcher: ChangeDispatcher;
|
||||
|
||||
|
||||
constructor(public id: string, public numberOfPropertyProtoRecords: number,
|
||||
public bindingTargets: BindingTarget[], public directiveIndices: DirectiveIndex[],
|
||||
public strategy: ChangeDetectionStrategy) {
|
||||
this.ref = new ChangeDetectorRef_(this);
|
||||
}
|
||||
|
||||
@@ -65,10 +70,24 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
|
||||
remove(): void { this.parent.removeContentChild(this); }
|
||||
|
||||
handleEvent(eventName: string, elIndex: number, locals: Locals): boolean {
|
||||
var res = this.handleEventInternal(eventName, elIndex, locals);
|
||||
this.markPathToRootAsCheckOnce();
|
||||
return res;
|
||||
handleEvent(eventName: string, elIndex: number, event: any): boolean {
|
||||
if (!this.hydrated()) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
var locals = new Map<string, any>();
|
||||
locals.set('$event', event);
|
||||
var res = !this.handleEventInternal(eventName, elIndex, new Locals(this.locals, locals));
|
||||
this.markPathToRootAsCheckOnce();
|
||||
return res;
|
||||
} catch (e) {
|
||||
var c = this.dispatcher.getDebugContext(null, elIndex, null);
|
||||
var context = isPresent(c) ?
|
||||
new EventEvaluationErrorContext(c.element, c.componentElement, c.context,
|
||||
c.locals, c.injector) :
|
||||
null;
|
||||
throw new EventEvaluationError(eventName, e, e.stack, context);
|
||||
}
|
||||
}
|
||||
|
||||
handleEventInternal(eventName: string, elIndex: number, locals: Locals): boolean { return false; }
|
||||
@@ -133,7 +152,8 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
|
||||
// This method is not intended to be overridden. Subclasses should instead provide an
|
||||
// implementation of `hydrateDirectives`.
|
||||
hydrate(context: T, locals: Locals, directives: any, pipes: Pipes): void {
|
||||
hydrate(context: T, locals: Locals, dispatcher: ChangeDispatcher, pipes: Pipes): void {
|
||||
this.dispatcher = dispatcher;
|
||||
this.mode = ChangeDetectionUtil.changeDetectionMode(this.strategy);
|
||||
this.context = context;
|
||||
|
||||
@@ -143,12 +163,12 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
|
||||
this.locals = locals;
|
||||
this.pipes = pipes;
|
||||
this.hydrateDirectives(directives);
|
||||
this.hydrateDirectives(dispatcher);
|
||||
this.state = ChangeDetectorState.NeverChecked;
|
||||
}
|
||||
|
||||
// Subclasses should override this method to hydrate any directives.
|
||||
hydrateDirectives(directives: any): void {}
|
||||
hydrateDirectives(dispatcher: ChangeDispatcher): void {}
|
||||
|
||||
// This method is not intended to be overridden. Subclasses should instead provide an
|
||||
// implementation of `dehydrateDirectives`.
|
||||
@@ -160,6 +180,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
this._unsubsribeFromObservables();
|
||||
}
|
||||
|
||||
this.dispatcher = null;
|
||||
this.context = null;
|
||||
this.locals = null;
|
||||
this.pipes = null;
|
||||
@@ -171,6 +192,19 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
|
||||
hydrated(): boolean { return isPresent(this.context); }
|
||||
|
||||
destroyRecursive(): void {
|
||||
this.dispatcher.notifyOnDestroy();
|
||||
this.dehydrate();
|
||||
var children = this.contentChildren;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
children[i].destroyRecursive();
|
||||
}
|
||||
children = this.viewChildren;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
children[i].destroyRecursive();
|
||||
}
|
||||
}
|
||||
|
||||
afterContentLifecycleCallbacks(): void {
|
||||
this.dispatcher.notifyAfterContentChecked();
|
||||
this.afterContentLifecycleCallbacksInternal();
|
||||
@@ -298,7 +332,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
private _throwError(exception: any, stack: any): void {
|
||||
var error;
|
||||
try {
|
||||
var c = this.dispatcher.getDebugContext(this._currentBinding().elementIndex, null);
|
||||
var c = this.dispatcher.getDebugContext(null, this._currentBinding().elementIndex, null);
|
||||
var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.context, c.locals,
|
||||
c.injector, this._currentBinding().debug) :
|
||||
null;
|
||||
|
||||
@@ -66,8 +66,8 @@ export class ChangeDetectorJITGenerator {
|
||||
generate(): Function {
|
||||
var factorySource = `
|
||||
${this.generateSource()}
|
||||
return function(dispatcher) {
|
||||
return new ${this.typeName}(dispatcher);
|
||||
return function() {
|
||||
return new ${this.typeName}();
|
||||
}
|
||||
`;
|
||||
return new Function(this.abstractChangeDetectorVarName, this.changeDetectionUtilVarName,
|
||||
@@ -77,9 +77,9 @@ export class ChangeDetectorJITGenerator {
|
||||
|
||||
generateSource(): string {
|
||||
return `
|
||||
var ${this.typeName} = function ${this.typeName}(dispatcher) {
|
||||
var ${this.typeName} = function ${this.typeName}() {
|
||||
${this.abstractChangeDetectorVarName}.call(
|
||||
this, ${JSON.stringify(this.id)}, dispatcher, ${this.records.length},
|
||||
this, ${JSON.stringify(this.id)}, ${this.records.length},
|
||||
${this.typeName}.gen_propertyBindingTargets, ${this.typeName}.gen_directiveIndices,
|
||||
${codify(this.changeDetectionStrategy)});
|
||||
this.dehydrateDirectives(false);
|
||||
@@ -199,13 +199,14 @@ export class ChangeDetectorJITGenerator {
|
||||
/** @internal */
|
||||
_maybeGenDehydrateDirectives(): string {
|
||||
var destroyPipesCode = this._names.genPipeOnDestroy();
|
||||
if (destroyPipesCode) {
|
||||
destroyPipesCode = `if (destroyPipes) { ${destroyPipesCode} }`;
|
||||
}
|
||||
var destroyDirectivesCode = this._logic.genDirectivesOnDestroy(this.directiveRecords);
|
||||
var dehydrateFieldsCode = this._names.genDehydrateFields();
|
||||
if (!destroyPipesCode && !dehydrateFieldsCode) return '';
|
||||
if (!destroyPipesCode && !destroyDirectivesCode && !dehydrateFieldsCode) return '';
|
||||
return `${this.typeName}.prototype.dehydrateDirectives = function(destroyPipes) {
|
||||
${destroyPipesCode}
|
||||
if (destroyPipes) {
|
||||
${destroyPipesCode}
|
||||
${destroyDirectivesCode}
|
||||
}
|
||||
${dehydrateFieldsCode}
|
||||
}`;
|
||||
}
|
||||
|
||||
@@ -205,4 +205,4 @@ export class ChangeDetectorRef_ extends ChangeDetectorRef {
|
||||
this._cd.mode = ChangeDetectionStrategy.CheckAlways;
|
||||
this.markForCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,17 +155,49 @@ export class CodegenLogicUtil {
|
||||
var res = [];
|
||||
for (var i = 0; i < directiveRecords.length; ++i) {
|
||||
var r = directiveRecords[i];
|
||||
res.push(`${this._names.getDirectiveName(r.directiveIndex)} = ${this._genReadDirective(i)};`);
|
||||
var dirVarName = this._names.getDirectiveName(r.directiveIndex);
|
||||
res.push(`${dirVarName} = ${this._genReadDirective(i)};`);
|
||||
if (isPresent(r.outputs)) {
|
||||
r.outputs.forEach(output => {
|
||||
var eventHandlerExpr = this._genEventHandler(r.directiveIndex.elementIndex, output[1]);
|
||||
if (IS_DART) {
|
||||
res.push(`${dirVarName}.${output[0]}.listen(${eventHandlerExpr});`);
|
||||
} else {
|
||||
res.push(`${dirVarName}.${output[0]}.subscribe({next: ${eventHandlerExpr}});`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return res.join("\n");
|
||||
}
|
||||
|
||||
genDirectivesOnDestroy(directiveRecords: DirectiveRecord[]): string {
|
||||
var res = [];
|
||||
for (var i = 0; i < directiveRecords.length; ++i) {
|
||||
var r = directiveRecords[i];
|
||||
if (r.callOnDestroy) {
|
||||
var dirVarName = this._names.getDirectiveName(r.directiveIndex);
|
||||
res.push(`${dirVarName}.ngOnDestroy();`);
|
||||
}
|
||||
}
|
||||
return res.join("\n");
|
||||
}
|
||||
|
||||
private _genEventHandler(boundElementIndex: number, eventName: string): string {
|
||||
if (IS_DART) {
|
||||
return `(event) => this.handleEvent('${eventName}', ${boundElementIndex}, event)`;
|
||||
} else {
|
||||
return `(function(event) { return this.handleEvent('${eventName}', ${boundElementIndex}, event); }).bind(this)`;
|
||||
}
|
||||
}
|
||||
|
||||
private _genReadDirective(index: number) {
|
||||
var directiveExpr = `this.getDirectiveFor(directives, ${index})`;
|
||||
// This is an experimental feature. Works only in Dart.
|
||||
if (this._changeDetection === ChangeDetectionStrategy.OnPushObserve) {
|
||||
return `this.observeDirective(this.getDirectiveFor(directives, ${index}), ${index})`;
|
||||
return `this.observeDirective(${directiveExpr}, ${index})`;
|
||||
} else {
|
||||
return `this.getDirectiveFor(directives, ${index})`;
|
||||
return directiveExpr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,14 @@ export class DirectiveRecord {
|
||||
callOnChanges: boolean;
|
||||
callDoCheck: boolean;
|
||||
callOnInit: boolean;
|
||||
callOnDestroy: boolean;
|
||||
changeDetection: ChangeDetectionStrategy;
|
||||
// array of [emitter property name, eventName]
|
||||
outputs: string[][];
|
||||
|
||||
constructor({directiveIndex, callAfterContentInit, callAfterContentChecked, callAfterViewInit,
|
||||
callAfterViewChecked, callOnChanges, callDoCheck, callOnInit, changeDetection}: {
|
||||
callAfterViewChecked, callOnChanges, callDoCheck, callOnInit, callOnDestroy,
|
||||
changeDetection, outputs}: {
|
||||
directiveIndex?: DirectiveIndex,
|
||||
callAfterContentInit?: boolean,
|
||||
callAfterContentChecked?: boolean,
|
||||
@@ -28,7 +32,9 @@ export class DirectiveRecord {
|
||||
callOnChanges?: boolean,
|
||||
callDoCheck?: boolean,
|
||||
callOnInit?: boolean,
|
||||
changeDetection?: ChangeDetectionStrategy
|
||||
callOnDestroy?: boolean,
|
||||
changeDetection?: ChangeDetectionStrategy,
|
||||
outputs?: string[][]
|
||||
} = {}) {
|
||||
this.directiveIndex = directiveIndex;
|
||||
this.callAfterContentInit = normalizeBool(callAfterContentInit);
|
||||
@@ -38,7 +44,9 @@ export class DirectiveRecord {
|
||||
this.callAfterViewChecked = normalizeBool(callAfterViewChecked);
|
||||
this.callDoCheck = normalizeBool(callDoCheck);
|
||||
this.callOnInit = normalizeBool(callOnInit);
|
||||
this.callOnDestroy = normalizeBool(callOnDestroy);
|
||||
this.changeDetection = changeDetection;
|
||||
this.outputs = outputs;
|
||||
}
|
||||
|
||||
isDefaultChangeDetection(): boolean {
|
||||
|
||||
@@ -11,21 +11,21 @@ import {ChangeDispatcher, ChangeDetectorGenConfig} from './interfaces';
|
||||
import {ChangeDetectionUtil, SimpleChange} from './change_detection_util';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorState} from './constants';
|
||||
import {ProtoRecord, RecordType} from './proto_record';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
values: any[];
|
||||
changes: any[];
|
||||
localPipes: any[];
|
||||
prevContexts: any[];
|
||||
directives: any = null;
|
||||
|
||||
constructor(id: string, dispatcher: ChangeDispatcher, numberOfPropertyProtoRecords: number,
|
||||
constructor(id: string, numberOfPropertyProtoRecords: number,
|
||||
propertyBindingTargets: BindingTarget[], directiveIndices: DirectiveIndex[],
|
||||
strategy: ChangeDetectionStrategy, private _records: ProtoRecord[],
|
||||
private _eventBindings: EventBinding[], private _directiveRecords: DirectiveRecord[],
|
||||
private _genConfig: ChangeDetectorGenConfig) {
|
||||
super(id, dispatcher, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices,
|
||||
strategy);
|
||||
super(id, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices, strategy);
|
||||
var len = _records.length + 1;
|
||||
this.values = ListWrapper.createFixedSize(len);
|
||||
this.localPipes = ListWrapper.createFixedSize(len);
|
||||
@@ -104,24 +104,41 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
return this._eventBindings.filter(eb => eb.eventName == eventName && eb.elIndex === elIndex);
|
||||
}
|
||||
|
||||
hydrateDirectives(directives: any): void {
|
||||
hydrateDirectives(dispatcher: ChangeDispatcher): void {
|
||||
this.values[0] = this.context;
|
||||
this.directives = directives;
|
||||
this.dispatcher = dispatcher;
|
||||
|
||||
if (this.strategy === ChangeDetectionStrategy.OnPushObserve) {
|
||||
for (var i = 0; i < this.directiveIndices.length; ++i) {
|
||||
var index = this.directiveIndices[i];
|
||||
super.observeDirective(directives.getDirectiveFor(index), i);
|
||||
super.observeDirective(this._getDirectiveFor(index), i);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < this._directiveRecords.length; ++i) {
|
||||
var r = this._directiveRecords[i];
|
||||
if (isPresent(r.outputs)) {
|
||||
r.outputs.forEach(output => {
|
||||
var eventHandler =
|
||||
<any>this._createEventHandler(r.directiveIndex.elementIndex, output[1]);
|
||||
var directive = this._getDirectiveFor(r.directiveIndex);
|
||||
var getter = reflector.getter(output[0]);
|
||||
ObservableWrapper.subscribe(getter(directive), eventHandler);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _createEventHandler(boundElementIndex: number, eventName: string): Function {
|
||||
return (event) => this.handleEvent(eventName, boundElementIndex, event);
|
||||
}
|
||||
|
||||
|
||||
dehydrateDirectives(destroyPipes: boolean) {
|
||||
if (destroyPipes) {
|
||||
this._destroyPipes();
|
||||
this._destroyDirectives();
|
||||
}
|
||||
this.values[0] = null;
|
||||
this.directives = null;
|
||||
ListWrapper.fill(this.values, ChangeDetectionUtil.uninitialized, 1);
|
||||
ListWrapper.fill(this.changes, false);
|
||||
ListWrapper.fill(this.localPipes, null);
|
||||
@@ -137,6 +154,16 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_destroyDirectives() {
|
||||
for (var i = 0; i < this._directiveRecords.length; ++i) {
|
||||
var record = this._directiveRecords[i];
|
||||
if (record.callOnDestroy) {
|
||||
this._getDirectiveFor(record.directiveIndex).ngOnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkNoChanges(): void { this.runDetectChanges(true); }
|
||||
|
||||
detectChangesInRecordsInternal(throwOnChange: boolean) {
|
||||
@@ -241,12 +268,14 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
private _getDirectiveFor(directiveIndex) {
|
||||
return this.directives.getDirectiveFor(directiveIndex);
|
||||
private _getDirectiveFor(directiveIndex: DirectiveIndex) {
|
||||
return this.dispatcher.getDirectiveFor(directiveIndex);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
private _getDetectorFor(directiveIndex) { return this.directives.getDetectorFor(directiveIndex); }
|
||||
private _getDetectorFor(directiveIndex: DirectiveIndex) {
|
||||
return this.dispatcher.getDetectorFor(directiveIndex);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
private _check(proto: ProtoRecord, throwOnChange: boolean, values: any[],
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user