refactor(compiler): move host properties into DirectiveWrapper

Part of #11683
This commit is contained in:
Tobias Bosch
2016-10-24 09:58:52 -07:00
committed by vsavkin
parent 5a7a58b1e0
commit 178fb79b5c
19 changed files with 409 additions and 175 deletions

View File

@ -9,9 +9,9 @@
import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
import {CompileIdentifierMetadata} from '../compile_metadata';
import {Identifiers, resolveEnumIdentifier, resolveIdentifier} from '../identifiers';
import {createEnumExpression} from '../compiler_util/identifier_util';
import {Identifiers, resolveEnumIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
import {ChangeDetectorStatus, ViewType} from '../private_import_core';
function _enumExpression(classIdentifier: CompileIdentifierMetadata, name: string): o.Expression {
@ -20,69 +20,25 @@ function _enumExpression(classIdentifier: CompileIdentifierMetadata, name: strin
export class ViewTypeEnum {
static fromValue(value: ViewType): o.Expression {
const viewType = resolveIdentifier(Identifiers.ViewType);
switch (value) {
case ViewType.HOST:
return _enumExpression(viewType, 'HOST');
case ViewType.COMPONENT:
return _enumExpression(viewType, 'COMPONENT');
case ViewType.EMBEDDED:
return _enumExpression(viewType, 'EMBEDDED');
default:
throw Error(`Inavlid ViewType value: ${value}`);
}
return createEnumExpression(Identifiers.ViewType, value);
}
}
export class ViewEncapsulationEnum {
static fromValue(value: ViewEncapsulation): o.Expression {
const viewEncapsulation = resolveIdentifier(Identifiers.ViewEncapsulation);
switch (value) {
case ViewEncapsulation.Emulated:
return _enumExpression(viewEncapsulation, 'Emulated');
case ViewEncapsulation.Native:
return _enumExpression(viewEncapsulation, 'Native');
case ViewEncapsulation.None:
return _enumExpression(viewEncapsulation, 'None');
default:
throw Error(`Inavlid ViewEncapsulation value: ${value}`);
}
return createEnumExpression(Identifiers.ViewEncapsulation, value);
}
}
export class ChangeDetectionStrategyEnum {
static fromValue(value: ChangeDetectionStrategy): o.Expression {
const changeDetectionStrategy = resolveIdentifier(Identifiers.ChangeDetectionStrategy);
switch (value) {
case ChangeDetectionStrategy.OnPush:
return _enumExpression(changeDetectionStrategy, 'OnPush');
case ChangeDetectionStrategy.Default:
return _enumExpression(changeDetectionStrategy, 'Default');
default:
throw Error(`Inavlid ChangeDetectionStrategy value: ${value}`);
}
return createEnumExpression(Identifiers.ChangeDetectionStrategy, value);
}
}
export class ChangeDetectorStatusEnum {
static fromValue(value: ChangeDetectorStatusEnum): o.Expression {
const changeDetectorStatus = resolveIdentifier(Identifiers.ChangeDetectorStatus);
switch (value) {
case ChangeDetectorStatus.CheckOnce:
return _enumExpression(changeDetectorStatus, 'CheckOnce');
case ChangeDetectorStatus.Checked:
return _enumExpression(changeDetectorStatus, 'Checked');
case ChangeDetectorStatus.CheckAlways:
return _enumExpression(changeDetectorStatus, 'CheckAlways');
case ChangeDetectorStatus.Detached:
return _enumExpression(changeDetectorStatus, 'Detached');
case ChangeDetectorStatus.Errored:
return _enumExpression(changeDetectorStatus, 'Errored');
case ChangeDetectorStatus.Destroyed:
return _enumExpression(changeDetectorStatus, 'Destroyed');
default:
throw Error(`Inavlid ChangeDetectorStatus value: ${value}`);
}
return createEnumExpression(Identifiers.ChangeDetectorStatus, value);
}
}

View File

@ -10,12 +10,14 @@ import {SecurityContext} from '@angular/core';
import {createCheckBindingField, createCheckBindingStmt} from '../compiler_util/binding_util';
import {ConvertPropertyBindingResult, convertPropertyBinding} from '../compiler_util/expression_converter';
import {createEnumExpression} from '../compiler_util/identifier_util';
import {writeToRenderer} from '../compiler_util/render_util';
import * as cdAst from '../expression_parser/ast';
import {isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
import {EMPTY_STATE as EMPTY_ANIMATION_STATE, LifecycleHooks, isDefaultChangeDetectionStrategy} from '../private_import_core';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {BoundElementPropertyAst, BoundTextAst, DirectiveAst, PropertyBindingType} from '../template_parser/template_ast';
import {camelCaseToDashCase} from '../util';
@ -121,10 +123,39 @@ export function bindRenderInputs(
}
export function bindDirectiveHostProps(
directiveAst: DirectiveAst, directiveInstance: o.Expression, compileElement: CompileElement,
eventListeners: CompileEventListener[]): void {
directiveAst: DirectiveAst, directiveWrapperInstance: o.Expression,
compileElement: CompileElement, eventListeners: CompileEventListener[], elementName: string,
schemaRegistry: ElementSchemaRegistry): void {
// host properties are change detected by the DirectiveWrappers,
// except for the animation properties as they need close integration with animation events
// and DirectiveWrappers don't support
// event listeners right now.
bindAndWriteToRenderer(
directiveAst.hostProperties, directiveInstance, compileElement, true, eventListeners);
directiveAst.hostProperties.filter(boundProp => boundProp.isAnimation),
directiveWrapperInstance.prop('context'), compileElement, true, eventListeners);
const methodArgs: o.Expression[] =
[o.THIS_EXPR, compileElement.renderNode, DetectChangesVars.throwOnChange];
// We need to provide the SecurityContext for properties that could need sanitization.
directiveAst.hostProperties.filter(boundProp => boundProp.needsRuntimeSecurityContext)
.forEach((boundProp) => {
let ctx: SecurityContext;
switch (boundProp.type) {
case PropertyBindingType.Property:
ctx = schemaRegistry.securityContext(elementName, boundProp.name, false);
break;
case PropertyBindingType.Attribute:
ctx = schemaRegistry.securityContext(elementName, boundProp.name, true);
break;
default:
throw new Error(
`Illegal state: Only property / attribute bindings can have an unknown security context! Binding ${boundProp.name}`);
}
methodArgs.push(createEnumExpression(Identifiers.SecurityContext, ctx));
});
compileElement.view.detectChangesRenderPropertiesMethod.addStmt(
directiveWrapperInstance.callMethod('detectChangesInHostProps', methodArgs).toStmt());
}
export function bindDirectiveInputs(
@ -157,7 +188,7 @@ export function bindDirectiveInputs(
var isOnPushComp = directiveAst.directive.isComponent &&
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
let directiveDetectChangesExpr = directiveWrapperInstance.callMethod(
'detectChangesInternal',
'detectChangesInInputProps',
[o.THIS_EXPR, compileElement.renderNode, DetectChangesVars.throwOnChange]);
const directiveDetectChangesStmt = isOnPushComp ?
new o.IfStmt(directiveDetectChangesExpr, [compileElement.appElement.prop('componentView')

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
import {CompileElement} from './compile_element';
@ -14,8 +15,9 @@ import {CompileEventListener, bindDirectiveOutputs, bindRenderOutputs, collectEv
import {bindDirectiveAfterContentLifecycleCallbacks, bindDirectiveAfterViewLifecycleCallbacks, bindInjectableDestroyLifecycleCallbacks, bindPipeDestroyLifecycleCallbacks} from './lifecycle_binder';
import {bindDirectiveHostProps, bindDirectiveInputs, bindRenderInputs, bindRenderText} from './property_binder';
export function bindView(view: CompileView, parsedTemplate: TemplateAst[]): void {
var visitor = new ViewBinderVisitor(view);
export function bindView(
view: CompileView, parsedTemplate: TemplateAst[], schemaRegistry: ElementSchemaRegistry): void {
var visitor = new ViewBinderVisitor(view, schemaRegistry);
templateVisitAll(visitor, parsedTemplate);
view.pipes.forEach(
(pipe) => { bindPipeDestroyLifecycleCallbacks(pipe.meta, pipe.instance, pipe.view); });
@ -24,7 +26,7 @@ export function bindView(view: CompileView, parsedTemplate: TemplateAst[]): void
class ViewBinderVisitor implements TemplateAstVisitor {
private _nodeIndex: number = 0;
constructor(public view: CompileView) {}
constructor(public view: CompileView, private _schemaRegistry: ElementSchemaRegistry) {}
visitBoundText(ast: BoundTextAst, parent: CompileElement): any {
var node = this.view.nodes[this._nodeIndex++];
@ -52,7 +54,9 @@ class ViewBinderVisitor implements TemplateAstVisitor {
compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference);
bindDirectiveInputs(directiveAst, directiveWrapperInstance, dirIndex, compileElement);
bindDirectiveHostProps(directiveAst, directiveInstance, compileElement, eventListeners);
bindDirectiveHostProps(
directiveAst, directiveWrapperInstance, compileElement, eventListeners, ast.name,
this._schemaRegistry);
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
});
templateVisitAll(this, ast.children, compileElement);
@ -91,7 +95,7 @@ class ViewBinderVisitor implements TemplateAstVisitor {
var providerInstance = compileElement.instances.get(providerAst.token.reference);
bindInjectableDestroyLifecycleCallbacks(providerAst, providerInstance, compileElement);
});
bindView(compileElement.embeddedView, ast.children);
bindView(compileElement.embeddedView, ast.children, this._schemaRegistry);
return null;
}

View File

@ -12,6 +12,7 @@ import {AnimationEntryCompileResult} from '../animation/animation_compiler';
import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata';
import {CompilerConfig} from '../config';
import * as o from '../output/output_ast';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {TemplateAst} from '../template_parser/template_ast';
import {CompileElement} from './compile_element';
@ -31,7 +32,7 @@ export class ViewCompileResult {
@Injectable()
export class ViewCompiler {
constructor(private _genConfig: CompilerConfig) {}
constructor(private _genConfig: CompilerConfig, private _schemaRegistry: ElementSchemaRegistry) {}
compileComponent(
component: CompileDirectiveMetadata, template: TemplateAst[], styles: o.Expression,
@ -47,7 +48,7 @@ export class ViewCompiler {
buildView(view, template, dependencies);
// Need to separate binding from creation to be able to refer to
// variables that have been declared after usage.
bindView(view, template);
bindView(view, template, this._schemaRegistry);
finishView(view, statements);
return new ViewCompileResult(statements, view.viewFactory.name, dependencies);