2016-06-23 09:47:54 -07:00
|
|
|
/**
|
|
|
|
* @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
|
|
|
|
*/
|
|
|
|
|
2016-08-02 15:53:34 -07:00
|
|
|
import {SecurityContext} from '@angular/core';
|
|
|
|
|
2016-10-24 08:43:06 -07:00
|
|
|
import {createCheckBindingField, createCheckBindingStmt} from '../compiler_util/binding_util';
|
2016-10-19 13:18:33 -07:00
|
|
|
import {ConvertPropertyBindingResult, convertPropertyBinding} from '../compiler_util/expression_converter';
|
2016-10-24 09:58:52 -07:00
|
|
|
import {createEnumExpression} from '../compiler_util/identifier_util';
|
2016-10-19 13:18:33 -07:00
|
|
|
import {writeToRenderer} from '../compiler_util/render_util';
|
2016-01-06 14:13:44 -08:00
|
|
|
import * as cdAst from '../expression_parser/ast';
|
2016-09-30 09:26:53 -07:00
|
|
|
import {isPresent} from '../facade/lang';
|
2016-08-24 17:39:49 -07:00
|
|
|
import {Identifiers, resolveIdentifier} from '../identifiers';
|
2016-06-08 16:38:52 -07:00
|
|
|
import * as o from '../output/output_ast';
|
2016-08-30 18:07:40 -07:00
|
|
|
import {EMPTY_STATE as EMPTY_ANIMATION_STATE, LifecycleHooks, isDefaultChangeDetectionStrategy} from '../private_import_core';
|
2016-10-24 09:58:52 -07:00
|
|
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
2016-08-02 15:53:34 -07:00
|
|
|
import {BoundElementPropertyAst, BoundTextAst, DirectiveAst, PropertyBindingType} from '../template_parser/template_ast';
|
|
|
|
import {camelCaseToDashCase} from '../util';
|
|
|
|
|
2016-01-06 14:13:44 -08:00
|
|
|
import {CompileElement, CompileNode} from './compile_element';
|
|
|
|
import {CompileMethod} from './compile_method';
|
2016-08-02 15:53:34 -07:00
|
|
|
import {CompileView} from './compile_view';
|
|
|
|
import {DetectChangesVars, ViewProperties} from './constants';
|
2016-10-18 17:16:51 -07:00
|
|
|
import {CompileEventListener} from './event_binder';
|
2016-04-28 17:50:03 -07:00
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
export function bindRenderText(
|
2016-10-19 13:18:33 -07:00
|
|
|
boundText: BoundTextAst, compileNode: CompileNode, view: CompileView): void {
|
2016-10-24 08:43:06 -07:00
|
|
|
const valueField = createCheckBindingField(view);
|
|
|
|
const evalResult = convertPropertyBinding(
|
|
|
|
view, view, view.componentContext, boundText.value, valueField.bindingId);
|
2016-10-19 13:18:33 -07:00
|
|
|
if (!evalResult) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2016-04-20 18:10:19 -07:00
|
|
|
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText);
|
2016-10-24 08:43:06 -07:00
|
|
|
view.detectChangesRenderPropertiesMethod.addStmts(createCheckBindingStmt(
|
|
|
|
evalResult, valueField.expression, DetectChangesVars.throwOnChange,
|
2016-06-08 16:38:52 -07:00
|
|
|
[o.THIS_EXPR.prop('renderer')
|
2016-10-19 13:18:33 -07:00
|
|
|
.callMethod('setText', [compileNode.renderNode, evalResult.currValExpr])
|
2016-10-24 08:43:06 -07:00
|
|
|
.toStmt()]));
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
function bindAndWriteToRenderer(
|
2016-07-08 17:11:12 -07:00
|
|
|
boundProps: BoundElementPropertyAst[], context: o.Expression, compileElement: CompileElement,
|
2016-10-18 17:16:51 -07:00
|
|
|
isHostProp: boolean, eventListeners: CompileEventListener[]) {
|
2016-01-06 14:13:44 -08:00
|
|
|
var view = compileElement.view;
|
|
|
|
var renderNode = compileElement.renderNode;
|
|
|
|
boundProps.forEach((boundProp) => {
|
2016-10-24 08:43:06 -07:00
|
|
|
const bindingField = createCheckBindingField(view);
|
2016-04-20 18:10:19 -07:00
|
|
|
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
|
2016-10-24 08:43:06 -07:00
|
|
|
const evalResult = convertPropertyBinding(
|
|
|
|
view, isHostProp ? null : view, context, boundProp.value, bindingField.bindingId);
|
2016-10-07 17:36:08 -07:00
|
|
|
var updateStmts: o.Statement[] = [];
|
2016-09-01 23:24:26 +03:00
|
|
|
var compileMethod = view.detectChangesRenderPropertiesMethod;
|
2016-01-06 14:13:44 -08:00
|
|
|
switch (boundProp.type) {
|
|
|
|
case PropertyBindingType.Property:
|
|
|
|
case PropertyBindingType.Attribute:
|
|
|
|
case PropertyBindingType.Class:
|
|
|
|
case PropertyBindingType.Style:
|
2016-10-19 13:18:33 -07:00
|
|
|
updateStmts.push(...writeToRenderer(
|
|
|
|
o.THIS_EXPR, boundProp, renderNode, evalResult.currValExpr,
|
|
|
|
view.genConfig.logBindingUpdate));
|
2016-05-25 12:46:22 -07:00
|
|
|
break;
|
|
|
|
case PropertyBindingType.Animation:
|
2016-09-01 23:24:26 +03:00
|
|
|
compileMethod = view.animationBindingsMethod;
|
2016-10-18 17:16:51 -07:00
|
|
|
const detachStmts: o.Statement[] = [];
|
|
|
|
|
|
|
|
const animationName = boundProp.name;
|
|
|
|
const targetViewExpr: o.Expression =
|
|
|
|
isHostProp ? compileElement.appElement.prop('componentView') : o.THIS_EXPR;
|
2016-09-01 23:24:26 +03:00
|
|
|
|
2016-10-18 17:16:51 -07:00
|
|
|
const animationFnExpr =
|
2016-07-08 17:11:12 -07:00
|
|
|
targetViewExpr.prop('componentType').prop('animations').key(o.literal(animationName));
|
2016-05-25 12:46:22 -07:00
|
|
|
|
|
|
|
// it's important to normalize the void value as `void` explicitly
|
|
|
|
// so that the styles data can be obtained from the stringmap
|
2016-10-18 17:16:51 -07:00
|
|
|
const emptyStateValue = o.literal(EMPTY_ANIMATION_STATE);
|
|
|
|
const unitializedValue = o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED));
|
|
|
|
const animationTransitionVar = o.variable('animationTransition_' + animationName);
|
2016-05-25 12:46:22 -07:00
|
|
|
|
2016-10-24 08:43:06 -07:00
|
|
|
updateStmts.push(animationTransitionVar
|
|
|
|
.set(animationFnExpr.callFn([
|
|
|
|
o.THIS_EXPR, renderNode,
|
|
|
|
bindingField.expression.equals(unitializedValue)
|
|
|
|
.conditional(emptyStateValue, bindingField.expression),
|
|
|
|
evalResult.currValExpr.equals(unitializedValue)
|
|
|
|
.conditional(emptyStateValue, evalResult.currValExpr)
|
|
|
|
]))
|
|
|
|
.toDeclStmt());
|
2016-10-18 17:16:51 -07:00
|
|
|
|
2016-10-19 13:18:33 -07:00
|
|
|
detachStmts.push(
|
|
|
|
animationTransitionVar
|
2016-10-24 08:43:06 -07:00
|
|
|
.set(animationFnExpr.callFn(
|
|
|
|
[o.THIS_EXPR, renderNode, bindingField.expression, emptyStateValue]))
|
2016-10-19 13:18:33 -07:00
|
|
|
.toDeclStmt());
|
2016-10-18 17:16:51 -07:00
|
|
|
|
|
|
|
eventListeners.forEach(listener => {
|
|
|
|
if (listener.isAnimation && listener.eventName === animationName) {
|
|
|
|
let animationStmt = listener.listenToAnimation(animationTransitionVar);
|
|
|
|
updateStmts.push(animationStmt);
|
|
|
|
detachStmts.push(animationStmt);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
view.detachMethod.addStmts(detachStmts);
|
2016-05-25 12:46:22 -07:00
|
|
|
|
2016-01-06 14:13:44 -08:00
|
|
|
break;
|
|
|
|
}
|
2016-10-24 08:43:06 -07:00
|
|
|
compileMethod.addStmts(createCheckBindingStmt(
|
|
|
|
evalResult, bindingField.expression, DetectChangesVars.throwOnChange, updateStmts));
|
2016-01-06 14:13:44 -08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
export function bindRenderInputs(
|
2016-10-18 17:16:51 -07:00
|
|
|
boundProps: BoundElementPropertyAst[], compileElement: CompileElement,
|
|
|
|
eventListeners: CompileEventListener[]): void {
|
|
|
|
bindAndWriteToRenderer(
|
|
|
|
boundProps, compileElement.view.componentContext, compileElement, false, eventListeners);
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
export function bindDirectiveHostProps(
|
2016-10-24 09:58:52 -07:00
|
|
|
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.
|
2016-10-18 17:16:51 -07:00
|
|
|
bindAndWriteToRenderer(
|
2016-10-24 09:58:52 -07:00
|
|
|
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());
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
export function bindDirectiveInputs(
|
2016-10-24 08:43:06 -07:00
|
|
|
directiveAst: DirectiveAst, directiveWrapperInstance: o.Expression, dirIndex: number,
|
2016-10-13 16:34:37 -07:00
|
|
|
compileElement: CompileElement) {
|
2016-01-06 14:13:44 -08:00
|
|
|
var view = compileElement.view;
|
|
|
|
var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
|
|
|
|
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
|
|
|
|
2016-10-24 08:43:06 -07:00
|
|
|
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}`;
|
2016-01-06 14:13:44 -08:00
|
|
|
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input);
|
2016-10-19 13:18:33 -07:00
|
|
|
const evalResult =
|
|
|
|
convertPropertyBinding(view, view, view.componentContext, input.value, bindingId);
|
2016-10-13 16:34:37 -07:00
|
|
|
if (!evalResult) {
|
|
|
|
return;
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|
2016-10-19 13:18:33 -07:00
|
|
|
detectChangesInInputsMethod.addStmts(evalResult.stmts);
|
|
|
|
detectChangesInInputsMethod.addStmt(
|
|
|
|
directiveWrapperInstance
|
|
|
|
.callMethod(
|
|
|
|
`check_${input.directiveName}`,
|
|
|
|
[
|
|
|
|
evalResult.currValExpr, DetectChangesVars.throwOnChange,
|
|
|
|
evalResult.forceUpdate || o.literal(false)
|
|
|
|
])
|
|
|
|
.toStmt());
|
2016-01-06 14:13:44 -08:00
|
|
|
});
|
2016-10-13 16:34:37 -07:00
|
|
|
var isOnPushComp = directiveAst.directive.isComponent &&
|
|
|
|
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
|
|
|
|
let directiveDetectChangesExpr = directiveWrapperInstance.callMethod(
|
2016-10-24 09:58:52 -07:00
|
|
|
'detectChangesInInputProps',
|
2016-10-13 16:34:37 -07:00
|
|
|
[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);
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|