135 lines
5.6 KiB
TypeScript
135 lines
5.6 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 {EventHandlerVars, convertActionBinding} from '../compiler_util/expression_converter';
|
|
import {createInlineArray} from '../compiler_util/identifier_util';
|
|
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
|
import {Identifiers, createIdentifier} from '../identifiers';
|
|
import * as o from '../output/output_ast';
|
|
import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
|
|
|
|
import {CompileElement} from './compile_element';
|
|
import {CompileMethod} from './compile_method';
|
|
import {getHandleEventMethodName} from './util';
|
|
|
|
export function bindOutputs(
|
|
boundEvents: BoundEventAst[], directives: DirectiveAst[], compileElement: CompileElement,
|
|
bindToRenderer: boolean): boolean {
|
|
const usedEvents = collectEvents(boundEvents, directives);
|
|
if (!usedEvents.size) {
|
|
return false;
|
|
}
|
|
if (bindToRenderer) {
|
|
subscribeToRenderEvents(usedEvents, compileElement);
|
|
}
|
|
subscribeToDirectiveEvents(usedEvents, directives, compileElement);
|
|
generateHandleEventMethod(boundEvents, directives, compileElement);
|
|
return true;
|
|
}
|
|
|
|
function collectEvents(
|
|
boundEvents: BoundEventAst[], directives: DirectiveAst[]): Map<string, EventSummary> {
|
|
const usedEvents = new Map<string, EventSummary>();
|
|
boundEvents.forEach((event) => { usedEvents.set(event.fullName, event); });
|
|
directives.forEach((dirAst) => {
|
|
dirAst.hostEvents.forEach((event) => { usedEvents.set(event.fullName, event); });
|
|
});
|
|
return usedEvents;
|
|
}
|
|
|
|
function subscribeToRenderEvents(
|
|
usedEvents: Map<string, EventSummary>, compileElement: CompileElement) {
|
|
const eventAndTargetExprs: o.Expression[] = [];
|
|
usedEvents.forEach((event) => {
|
|
if (!event.phase) {
|
|
eventAndTargetExprs.push(o.literal(event.name), o.literal(event.target));
|
|
}
|
|
});
|
|
if (eventAndTargetExprs.length) {
|
|
const disposableVar = o.variable(`disposable_${compileElement.view.disposables.length}`);
|
|
compileElement.view.disposables.push(disposableVar);
|
|
compileElement.view.createMethod.addStmt(
|
|
disposableVar
|
|
.set(o.importExpr(createIdentifier(Identifiers.subscribeToRenderElement)).callFn([
|
|
o.THIS_EXPR, compileElement.renderNode, createInlineArray(eventAndTargetExprs),
|
|
handleEventExpr(compileElement)
|
|
]))
|
|
.toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private]));
|
|
}
|
|
}
|
|
|
|
function subscribeToDirectiveEvents(
|
|
usedEvents: Map<string, EventSummary>, directives: DirectiveAst[],
|
|
compileElement: CompileElement) {
|
|
const usedEventNames = Array.from(usedEvents.keys());
|
|
directives.forEach((dirAst) => {
|
|
const dirWrapper = compileElement.directiveWrapperInstance.get(dirAst.directive.type.reference);
|
|
compileElement.view.createMethod.addStmts(DirectiveWrapperExpressions.subscribe(
|
|
dirAst.directive, dirAst.hostProperties, usedEventNames, dirWrapper, o.THIS_EXPR,
|
|
handleEventExpr(compileElement)));
|
|
});
|
|
}
|
|
|
|
function generateHandleEventMethod(
|
|
boundEvents: BoundEventAst[], directives: DirectiveAst[], compileElement: CompileElement) {
|
|
const hasComponentHostListener =
|
|
directives.some((dirAst) => dirAst.hostEvents.some((event) => dirAst.directive.isComponent));
|
|
|
|
const markPathToRootStart = hasComponentHostListener ? compileElement.compViewExpr : o.THIS_EXPR;
|
|
const handleEventStmts = new CompileMethod(compileElement.view);
|
|
handleEventStmts.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
|
handleEventStmts.push(markPathToRootStart.callMethod('markPathToRootAsCheckOnce', []).toStmt());
|
|
const eventNameVar = o.variable('eventName');
|
|
const resultVar = o.variable('result');
|
|
handleEventStmts.push(resultVar.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE));
|
|
|
|
directives.forEach((dirAst, dirIdx) => {
|
|
const dirWrapper = compileElement.directiveWrapperInstance.get(dirAst.directive.type.reference);
|
|
if (dirAst.hostEvents.length > 0) {
|
|
handleEventStmts.push(
|
|
resultVar
|
|
.set(DirectiveWrapperExpressions
|
|
.handleEvent(
|
|
dirAst.hostEvents, dirWrapper, eventNameVar, EventHandlerVars.event)
|
|
.and(resultVar))
|
|
.toStmt());
|
|
}
|
|
});
|
|
boundEvents.forEach((renderEvent, renderEventIdx) => {
|
|
const evalResult = convertActionBinding(
|
|
compileElement.view, compileElement.view, compileElement.view.componentContext,
|
|
renderEvent.handler, `sub_${renderEventIdx}`);
|
|
const trueStmts = evalResult.stmts;
|
|
if (evalResult.preventDefault) {
|
|
trueStmts.push(resultVar.set(evalResult.preventDefault.and(resultVar)).toStmt());
|
|
}
|
|
// TODO(tbosch): convert this into a `switch` once our OutputAst supports it.
|
|
handleEventStmts.push(
|
|
new o.IfStmt(eventNameVar.equals(o.literal(renderEvent.fullName)), trueStmts));
|
|
});
|
|
|
|
handleEventStmts.push(new o.ReturnStatement(resultVar));
|
|
compileElement.view.methods.push(new o.ClassMethod(
|
|
getHandleEventMethodName(compileElement.nodeIndex),
|
|
[
|
|
new o.FnParam(eventNameVar.name, o.STRING_TYPE),
|
|
new o.FnParam(EventHandlerVars.event.name, o.DYNAMIC_TYPE)
|
|
],
|
|
handleEventStmts.finish(), o.BOOL_TYPE));
|
|
}
|
|
|
|
function handleEventExpr(compileElement: CompileElement) {
|
|
const handleEventMethodName = getHandleEventMethodName(compileElement.nodeIndex);
|
|
return o.THIS_EXPR.callMethod('eventHandler', [o.THIS_EXPR.prop(handleEventMethodName)]);
|
|
}
|
|
|
|
type EventSummary = {
|
|
name: string,
|
|
target: string,
|
|
phase: string
|
|
}; |