2016-10-31 14:43:50 -07:00

156 lines
7.5 KiB
TypeScript

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
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 {triggerAnimation, writeToRenderer} from '../compiler_util/render_util';
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
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, BoundEventAst, BoundTextAst, DirectiveAst, PropertyBindingType} from '../template_parser/template_ast';
import {camelCaseToDashCase} from '../util';
import {CompileElement, CompileNode} from './compile_element';
import {CompileMethod} from './compile_method';
import {CompileView} from './compile_view';
import {DetectChangesVars, ViewProperties} from './constants';
import {getHandleEventMethodName} from './util';
export function bindRenderText(
boundText: BoundTextAst, compileNode: CompileNode, view: CompileView): void {
const valueField = createCheckBindingField(view);
const evalResult = convertPropertyBinding(
view, view, view.componentContext, boundText.value, valueField.bindingId);
if (!evalResult) {
return null;
}
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText);
view.detectChangesRenderPropertiesMethod.addStmts(createCheckBindingStmt(
evalResult, valueField.expression, DetectChangesVars.throwOnChange,
[o.THIS_EXPR.prop('renderer')
.callMethod('setText', [compileNode.renderNode, evalResult.currValExpr])
.toStmt()]));
}
export function bindRenderInputs(
boundProps: BoundElementPropertyAst[], hasEvents: boolean, compileElement: CompileElement) {
var view = compileElement.view;
var renderNode = compileElement.renderNode;
boundProps.forEach((boundProp) => {
const bindingField = createCheckBindingField(view);
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
const evalResult = convertPropertyBinding(
view, view, compileElement.view.componentContext, boundProp.value, bindingField.bindingId);
if (!evalResult) {
return;
}
var checkBindingStmts: o.Statement[] = [];
var compileMethod = view.detectChangesRenderPropertiesMethod;
switch (boundProp.type) {
case PropertyBindingType.Property:
case PropertyBindingType.Attribute:
case PropertyBindingType.Class:
case PropertyBindingType.Style:
checkBindingStmts.push(...writeToRenderer(
o.THIS_EXPR, boundProp, renderNode, evalResult.currValExpr,
view.genConfig.logBindingUpdate));
break;
case PropertyBindingType.Animation:
compileMethod = view.animationBindingsMethod;
const {updateStmts, detachStmts} = triggerAnimation(
o.THIS_EXPR, o.THIS_EXPR, boundProp,
(hasEvents ? o.THIS_EXPR.prop(getHandleEventMethodName(compileElement.nodeIndex)) :
o.importExpr(resolveIdentifier(Identifiers.noop)))
.callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR]),
compileElement.renderNode, evalResult.currValExpr, bindingField.expression);
checkBindingStmts.push(...updateStmts);
view.detachMethod.addStmts(detachStmts);
break;
}
compileMethod.addStmts(createCheckBindingStmt(
evalResult, bindingField.expression, DetectChangesVars.throwOnChange, checkBindingStmts));
});
}
export function bindDirectiveHostProps(
directiveAst: DirectiveAst, directiveWrapperInstance: o.Expression,
compileElement: CompileElement, elementName: string,
schemaRegistry: ElementSchemaRegistry): void {
// We need to provide the SecurityContext for properties that could need sanitization.
const runtimeSecurityCtxExprs =
directiveAst.hostProperties.filter(boundProp => boundProp.needsRuntimeSecurityContext)
.map((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}`);
}
return createEnumExpression(Identifiers.SecurityContext, ctx);
});
compileElement.view.detectChangesRenderPropertiesMethod.addStmts(
DirectiveWrapperExpressions.checkHost(
directiveAst.hostProperties, directiveWrapperInstance, o.THIS_EXPR,
compileElement.component ? compileElement.appElement.prop('componentView') : o.THIS_EXPR,
compileElement.renderNode, DetectChangesVars.throwOnChange, runtimeSecurityCtxExprs));
}
export function bindDirectiveInputs(
directiveAst: DirectiveAst, directiveWrapperInstance: o.Expression, dirIndex: number,
compileElement: CompileElement) {
var view = compileElement.view;
var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
directiveAst.inputs.forEach((input, inputIdx) => {
// Note: We can't use `fields.length` here, as we are not adding a field!
const bindingId = `${compileElement.nodeIndex}_${dirIndex}_${inputIdx}`;
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input);
const evalResult =
convertPropertyBinding(view, view, view.componentContext, input.value, bindingId);
if (!evalResult) {
return;
}
detectChangesInInputsMethod.addStmts(evalResult.stmts);
detectChangesInInputsMethod.addStmt(
directiveWrapperInstance
.callMethod(
`check_${input.directiveName}`,
[
evalResult.currValExpr, DetectChangesVars.throwOnChange,
evalResult.forceUpdate || o.literal(false)
])
.toStmt());
});
var isOnPushComp = directiveAst.directive.isComponent &&
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
let directiveDetectChangesExpr = DirectiveWrapperExpressions.ngDoCheck(
directiveWrapperInstance, o.THIS_EXPR, compileElement.renderNode,
DetectChangesVars.throwOnChange);
const directiveDetectChangesStmt = isOnPushComp ?
new o.IfStmt(directiveDetectChangesExpr, [compileElement.appElement.prop('componentView')
.callMethod('markAsCheckOnce', [])
.toStmt()]) :
directiveDetectChangesExpr.toStmt();
detectChangesInInputsMethod.addStmt(directiveDetectChangesStmt);
}