feat(ivy): render flags support in host bindings function (FW-649) (#27204)
PR Close #27204
This commit is contained in:
parent
bf71b107b3
commit
dc300c5c41
|
@ -145,8 +145,10 @@ describe('compiler compliance: bindings', () => {
|
|||
type: HostBindingDir,
|
||||
selectors: [["", "hostBindingDir", ""]],
|
||||
factory: function HostBindingDir_Factory(t) { return new (t || HostBindingDir)(); },
|
||||
hostBindings: function HostBindingDir_HostBindings(dirIndex, elIndex) {
|
||||
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵload(dirIndex).dirId));
|
||||
hostBindings: function HostBindingDir_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind(ctx.dirId));
|
||||
}
|
||||
},
|
||||
hostVars: 1
|
||||
});
|
||||
|
@ -188,8 +190,10 @@ describe('compiler compliance: bindings', () => {
|
|||
type: HostBindingComp,
|
||||
selectors: [["host-binding-comp"]],
|
||||
factory: function HostBindingComp_Factory(t) { return new (t || HostBindingComp)(); },
|
||||
hostBindings: function HostBindingComp_HostBindings(dirIndex, elIndex) {
|
||||
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵpureFunction1(1, $ff$, $r3$.ɵload(dirIndex).id)));
|
||||
hostBindings: function HostBindingComp_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵpureFunction1(1, $ff$, ctx.id)));
|
||||
}
|
||||
},
|
||||
hostVars: 3,
|
||||
consts: 0,
|
||||
|
@ -232,8 +236,10 @@ describe('compiler compliance: bindings', () => {
|
|||
type: HostAttributeDir,
|
||||
selectors: [["", "hostAttributeDir", ""]],
|
||||
factory: function HostAttributeDir_Factory(t) { return new (t || HostAttributeDir)(); },
|
||||
hostBindings: function HostAttributeDir_HostBindings(dirIndex, elIndex) {
|
||||
$r3$.ɵelementAttribute(elIndex, "required", $r3$.ɵbind($r3$.ɵload(dirIndex).required));
|
||||
hostBindings: function HostAttributeDir_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementAttribute(elIndex, "required", $r3$.ɵbind(ctx.required));
|
||||
}
|
||||
},
|
||||
hostVars: 1
|
||||
});
|
||||
|
|
|
@ -771,12 +771,16 @@ describe('compiler compliance: styling', () => {
|
|||
const _c0 = ["foo", "baz", ${InitialStylingFlags.VALUES_MODE}, "foo", true, "baz", true];
|
||||
const _c1 = ["width", "height", "color", ${InitialStylingFlags.VALUES_MODE}, "width", "200px", "height", "500px"];
|
||||
…
|
||||
hostBindings: function MyComponent_HostBindings(dirIndex, elIndex) {
|
||||
$r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, dirIndex);
|
||||
$r3$.ɵelementStylingMap(elIndex, $r3$.ɵload(dirIndex).myClass, $r3$.ɵload(dirIndex).myStyle, dirIndex);
|
||||
$r3$.ɵelementStyleProp(elIndex, 2, $r3$.ɵload(dirIndex).myColorProp, null, dirIndex);
|
||||
$r3$.ɵelementClassProp(elIndex, 0, $r3$.ɵload(dirIndex).myFooClass, dirIndex);
|
||||
$r3$.ɵelementStylingApply(elIndex, dirIndex);
|
||||
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, ctx);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementStylingMap(elIndex, ctx.myClass, ctx.myStyle, ctx);
|
||||
$r3$.ɵelementStyleProp(elIndex, 2, ctx.myColorProp, null, ctx);
|
||||
$r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClass, ctx);
|
||||
$r3$.ɵelementStylingApply(elIndex, ctx);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -825,14 +829,18 @@ describe('compiler compliance: styling', () => {
|
|||
const _c0 = ["bar", "foo"];
|
||||
const _c1 = ["height", "width"];
|
||||
…
|
||||
hostBindings: function MyComponent_HostBindings(dirIndex, elIndex) {
|
||||
$r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, dirIndex);
|
||||
$r3$.ɵelementStylingMap(elIndex, $r3$.ɵload(dirIndex).myClasses, $r3$.ɵload(dirIndex).myStyle, dirIndex);
|
||||
$r3$.ɵelementStyleProp(elIndex, 0, $r3$.ɵload(dirIndex).myHeightProp, "pt", dirIndex);
|
||||
$r3$.ɵelementStyleProp(elIndex, 1, $r3$.ɵload(dirIndex).myWidthProp, null, dirIndex);
|
||||
$r3$.ɵelementClassProp(elIndex, 0, $r3$.ɵload(dirIndex).myBarClass, dirIndex);
|
||||
$r3$.ɵelementClassProp(elIndex, 1, $r3$.ɵload(dirIndex).myFooClass, dirIndex);
|
||||
$r3$.ɵelementStylingApply(elIndex, dirIndex);
|
||||
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStyling(_c0, _c1, $r3$.ɵdefaultStyleSanitizer, ctx);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementStylingMap(elIndex, ctx.myClasses, ctx.myStyle, ctx);
|
||||
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myHeightProp, "pt", ctx);
|
||||
$r3$.ɵelementStyleProp(elIndex, 1, ctx.myWidthProp, null, ctx);
|
||||
$r3$.ɵelementClassProp(elIndex, 0, ctx.myBarClass, ctx);
|
||||
$r3$.ɵelementClassProp(elIndex, 1, ctx.myFooClass, ctx);
|
||||
$r3$.ɵelementStylingApply(elIndex, ctx);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -886,18 +894,26 @@ describe('compiler compliance: styling', () => {
|
|||
const _c2 = ["bar"];
|
||||
const _c3 = ["height"];
|
||||
…
|
||||
function WidthDirective_HostBindings(dirIndex, elIndex) {
|
||||
$r3$.ɵelementStyling(_c0, _c1, null, dirIndex);
|
||||
$r3$.ɵelementStyleProp(elIndex, 0, $r3$.ɵload(dirIndex).myWidth, null, dirIndex);
|
||||
$r3$.ɵelementClassProp(elIndex, 0, $r3$.ɵload(dirIndex).myFooClass, dirIndex);
|
||||
$r3$.ɵelementStylingApply(elIndex, dirIndex);
|
||||
function WidthDirective_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStyling(_c0, _c1, null, ctx);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myWidth, null, ctx);
|
||||
$r3$.ɵelementClassProp(elIndex, 0, ctx.myFooClass, ctx);
|
||||
$r3$.ɵelementStylingApply(elIndex, ctx);
|
||||
}
|
||||
}
|
||||
…
|
||||
function HeightDirective_HostBindings(dirIndex, elIndex) {
|
||||
$r3$.ɵelementStyling(_c2, _c3, null, dirIndex);
|
||||
$r3$.ɵelementStyleProp(elIndex, 0, $r3$.ɵload(dirIndex).myHeight, null, dirIndex);
|
||||
$r3$.ɵelementClassProp(elIndex, 0, $r3$.ɵload(dirIndex).myBarClass, dirIndex);
|
||||
$r3$.ɵelementStylingApply(elIndex, dirIndex);
|
||||
function HeightDirective_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStyling(_c2, _c3, null, ctx);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementStyleProp(elIndex, 0, ctx.myHeight, null, ctx);
|
||||
$r3$.ɵelementClassProp(elIndex, 0, ctx.myBarClass, ctx);
|
||||
$r3$.ɵelementStylingApply(elIndex, ctx);
|
||||
}
|
||||
}
|
||||
…
|
||||
`;
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
import {NgtscTestEnvironment} from './env';
|
||||
|
||||
const trim = (input: string): string => input.replace(/\s+/g, ' ').trim();
|
||||
|
||||
describe('ngtsc behavioral tests', () => {
|
||||
if (!NgtscTestEnvironment.supported) {
|
||||
// These tests should be excluded from the non-Bazel build.
|
||||
|
@ -449,6 +451,37 @@ describe('ngtsc behavioral tests', () => {
|
|||
expect(jsContents).toContain(`i0.ɵquery(null, ViewContainerRef, true)`);
|
||||
});
|
||||
|
||||
it('should generate host listeners for components', () => {
|
||||
env.tsconfig();
|
||||
env.write(`test.ts`, `
|
||||
import {Component, HostListener} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'test',
|
||||
template: 'Test'
|
||||
})
|
||||
class FooCmp {
|
||||
@HostListener('document:click', ['$event.target'])
|
||||
onClick(eventTarget: HTMLElement): void {}
|
||||
|
||||
@HostListener('window:scroll')
|
||||
onScroll(event: any): void {}
|
||||
}
|
||||
`);
|
||||
|
||||
env.driveMain();
|
||||
const jsContents = env.getContents('test.js');
|
||||
const hostBindingsFn = `
|
||||
hostBindings: function FooCmp_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 1) {
|
||||
i0.ɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onClick($event.target); });
|
||||
i0.ɵlistener("scroll", function FooCmp_scroll_HostBindingHandler($event) { return ctx.onScroll(); });
|
||||
}
|
||||
}
|
||||
`;
|
||||
expect(trim(jsContents)).toContain(trim(hostBindingsFn));
|
||||
});
|
||||
|
||||
it('should generate host bindings for directives', () => {
|
||||
env.tsconfig();
|
||||
env.write(`test.ts`, `
|
||||
|
@ -476,39 +509,33 @@ describe('ngtsc behavioral tests', () => {
|
|||
|
||||
env.driveMain();
|
||||
const jsContents = env.getContents('test.js');
|
||||
expect(jsContents)
|
||||
.toContain(`i0.ɵelementAttribute(elIndex, "hello", i0.ɵbind(i0.ɵload(dirIndex).foo));`);
|
||||
expect(jsContents)
|
||||
.toContain(`i0.ɵelementProperty(elIndex, "prop", i0.ɵbind(i0.ɵload(dirIndex).bar));`);
|
||||
expect(jsContents)
|
||||
.toContain('i0.ɵelementClassProp(elIndex, 0, i0.ɵload(dirIndex).someClass, dirIndex)');
|
||||
|
||||
const factoryDef = `
|
||||
factory: function FooCmp_Factory(t) {
|
||||
var f = new (t || FooCmp)();
|
||||
i0.ɵlistener("click", function FooCmp_click_HostBindingHandler($event) {
|
||||
return f.onClick($event);
|
||||
});
|
||||
i0.ɵlistener("change", function FooCmp_change_HostBindingHandler($event) {
|
||||
return f.onChange(f.arg1, f.arg2, f.arg3);
|
||||
});
|
||||
return f;
|
||||
const hostBindingsFn = `
|
||||
hostBindings: function FooCmp_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 1) {
|
||||
i0.ɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onClick($event); });
|
||||
i0.ɵlistener("change", function FooCmp_change_HostBindingHandler($event) { return ctx.onChange(ctx.arg1, ctx.arg2, ctx.arg3); });
|
||||
i0.ɵelementStyling(_c0, null, null, ctx);
|
||||
}
|
||||
if (rf & 2) {
|
||||
i0.ɵelementAttribute(elIndex, "hello", i0.ɵbind(ctx.foo));
|
||||
i0.ɵelementProperty(elIndex, "prop", i0.ɵbind(ctx.bar));
|
||||
i0.ɵelementClassProp(elIndex, 0, ctx.someClass, ctx);
|
||||
i0.ɵelementStylingApply(elIndex, ctx);
|
||||
}
|
||||
}
|
||||
`;
|
||||
expect(jsContents).toContain(factoryDef.replace(/\s+/g, ' ').trim());
|
||||
expect(trim(jsContents)).toContain(trim(hostBindingsFn));
|
||||
});
|
||||
|
||||
it('should generate host listeners for directives with base factories', () => {
|
||||
it('should generate host listeners for directives within hostBindings section', () => {
|
||||
env.tsconfig();
|
||||
env.write(`test.ts`, `
|
||||
import {Directive, HostListener} from '@angular/core';
|
||||
|
||||
class Base {}
|
||||
|
||||
@Directive({
|
||||
selector: '[test]',
|
||||
})
|
||||
class Dir extends Base {
|
||||
class Dir {
|
||||
@HostListener('change', ['arg'])
|
||||
onChange(event: any, arg: any): void {}
|
||||
}
|
||||
|
@ -516,17 +543,14 @@ describe('ngtsc behavioral tests', () => {
|
|||
|
||||
env.driveMain();
|
||||
const jsContents = env.getContents('test.js');
|
||||
const factoryDef = `
|
||||
factory: function Dir_Factory(t) {
|
||||
var f = ɵDir_BaseFactory((t || Dir));
|
||||
i0.ɵlistener("change", function Dir_change_HostBindingHandler($event) {
|
||||
return f.onChange(f.arg);
|
||||
});
|
||||
return f;
|
||||
const hostBindingsFn = `
|
||||
hostBindings: function Dir_HostBindings(rf, ctx, elIndex) {
|
||||
if (rf & 1) {
|
||||
i0.ɵlistener("change", function Dir_change_HostBindingHandler($event) { return ctx.onChange(ctx.arg); });
|
||||
}
|
||||
}
|
||||
`;
|
||||
expect(jsContents).toContain(factoryDef.replace(/\s+/g, ' ').trim());
|
||||
expect(jsContents).toContain('var ɵDir_BaseFactory = i0.ɵgetInheritedFactory(Dir)');
|
||||
expect(trim(jsContents)).toContain(trim(hostBindingsFn));
|
||||
});
|
||||
|
||||
it('should correctly recognize local symbols', () => {
|
||||
|
|
|
@ -42,7 +42,6 @@ export function compileInjectable(meta: R3InjectableMetadata): InjectableDef {
|
|||
type: meta.type,
|
||||
deps: meta.ctorDeps,
|
||||
injectFn: Identifiers.inject,
|
||||
extraStatementFn: null,
|
||||
};
|
||||
|
||||
if (meta.useClass !== undefined) {
|
||||
|
|
|
@ -49,11 +49,6 @@ export interface R3ConstructorFactoryMetadata {
|
|||
* function could be different, and other options control how it will be invoked.
|
||||
*/
|
||||
injectFn: o.ExternalReference;
|
||||
|
||||
/**
|
||||
* Function that allows extra statements to be inserted into factory function.
|
||||
*/
|
||||
extraStatementFn: ((instance: o.Expression) => o.Statement[])|null;
|
||||
}
|
||||
|
||||
export enum R3FactoryDelegateType {
|
||||
|
@ -208,22 +203,10 @@ export function compileFactoryFunction(meta: R3FactoryMetadata):
|
|||
} else if (isExpressionFactoryMetadata(meta)) {
|
||||
// TODO(alxhub): decide whether to lower the value here or in the caller
|
||||
retExpr = makeConditionalFactory(meta.expression);
|
||||
} else if (meta.extraStatementFn) {
|
||||
// if extraStatementsFn is specified and the 'makeConditionalFactory' function
|
||||
// was not invoked, we need to create a reference to the instance, so we can
|
||||
// pass it as an argument to the 'extraStatementFn' function while calling it
|
||||
const variable = o.variable('f');
|
||||
body.push(variable.set(ctorExpr).toDeclStmt());
|
||||
retExpr = variable;
|
||||
} else {
|
||||
retExpr = ctorExpr;
|
||||
}
|
||||
|
||||
if (meta.extraStatementFn) {
|
||||
const extraStmts = meta.extraStatementFn(retExpr);
|
||||
body.push(...extraStmts);
|
||||
}
|
||||
|
||||
return {
|
||||
factory: o.fn(
|
||||
[new o.FnParam('t', o.DYNAMIC_TYPE)], [...body, new o.ReturnStatement(retExpr)],
|
||||
|
|
|
@ -101,7 +101,6 @@ export function compileInjector(meta: R3InjectorMetadata): R3InjectorDef {
|
|||
type: meta.type,
|
||||
deps: meta.deps,
|
||||
injectFn: R3.inject,
|
||||
extraStatementFn: null,
|
||||
});
|
||||
const expression = o.importExpr(R3.defineInjector).callFn([mapToMapExpression({
|
||||
factory: result.factory,
|
||||
|
|
|
@ -43,7 +43,6 @@ export function compilePipeFromMetadata(metadata: R3PipeMetadata) {
|
|||
type: metadata.type,
|
||||
deps: metadata.deps,
|
||||
injectFn: R3.directiveInject,
|
||||
extraStatementFn: null,
|
||||
});
|
||||
definitionMapValues.push({key: 'factory', value: templateFactory.factory, quoted: false});
|
||||
|
||||
|
|
|
@ -55,7 +55,6 @@ function baseDirectiveFields(
|
|||
type: meta.type,
|
||||
deps: meta.deps,
|
||||
injectFn: R3.directiveInject,
|
||||
extraStatementFn: createFactoryExtraStatementsFn(meta, bindingParser)
|
||||
});
|
||||
definitionMap.set('factory', result.factory);
|
||||
|
||||
|
@ -67,8 +66,8 @@ function baseDirectiveFields(
|
|||
let hostVars = Object.keys(meta.host.properties).length;
|
||||
|
||||
const elVarExp = o.variable('elIndex');
|
||||
const dirVarExp = o.variable('dirIndex');
|
||||
const styleBuilder = new StylingBuilder(elVarExp, dirVarExp);
|
||||
const contextVarExp = o.variable(CONTEXT_NAME);
|
||||
const styleBuilder = new StylingBuilder(elVarExp, contextVarExp);
|
||||
|
||||
const allOtherAttributes: any = {};
|
||||
const attrNames = Object.getOwnPropertyNames(meta.host.attributes);
|
||||
|
@ -93,15 +92,15 @@ function baseDirectiveFields(
|
|||
// e.g. `attributes: ['role', 'listbox']`
|
||||
definitionMap.set('attributes', createHostAttributesArray(allOtherAttributes));
|
||||
|
||||
// e.g. `hostBindings: (dirIndex, elIndex) => { ... }
|
||||
// e.g. `hostBindings: (rf, ctx, elIndex) => { ... }
|
||||
definitionMap.set(
|
||||
'hostBindings',
|
||||
createHostBindingsFunction(
|
||||
meta, elVarExp, dirVarExp, styleBuilder, bindingParser, constantPool, (slots: number) => {
|
||||
const originalSlots = hostVars;
|
||||
hostVars += slots;
|
||||
return originalSlots;
|
||||
}));
|
||||
'hostBindings', createHostBindingsFunction(
|
||||
meta, elVarExp, contextVarExp, styleBuilder, bindingParser, constantPool,
|
||||
(slots: number) => {
|
||||
const originalSlots = hostVars;
|
||||
hostVars += slots;
|
||||
return originalSlots;
|
||||
}));
|
||||
|
||||
if (hostVars) {
|
||||
// e.g. `hostVars: 2
|
||||
|
@ -643,18 +642,26 @@ function createViewQueriesFunction(
|
|||
|
||||
// Return a host binding function or null if one is not necessary.
|
||||
function createHostBindingsFunction(
|
||||
meta: R3DirectiveMetadata, elVarExp: o.ReadVarExpr, dirVarExp: o.ReadVarExpr,
|
||||
meta: R3DirectiveMetadata, elVarExp: o.ReadVarExpr, bindingContext: o.ReadVarExpr,
|
||||
styleBuilder: StylingBuilder, bindingParser: BindingParser, constantPool: ConstantPool,
|
||||
allocatePureFunctionSlots: (slots: number) => number): o.Expression|null {
|
||||
const statements: o.Statement[] = [];
|
||||
const createStatements: o.Statement[] = [];
|
||||
const updateStatements: o.Statement[] = [];
|
||||
|
||||
const hostBindingSourceSpan = meta.typeSourceSpan;
|
||||
|
||||
const directiveSummary = metadataAsSummary(meta);
|
||||
|
||||
// Calculate host event bindings
|
||||
const eventBindings =
|
||||
bindingParser.createDirectiveHostEventAsts(directiveSummary, hostBindingSourceSpan);
|
||||
if (eventBindings && eventBindings.length) {
|
||||
const listeners = createHostListeners(bindingContext, eventBindings, meta);
|
||||
createStatements.push(...listeners);
|
||||
}
|
||||
|
||||
// Calculate the host property bindings
|
||||
const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
|
||||
const bindingContext = o.importExpr(R3.load).callFn([dirVarExp]);
|
||||
|
||||
const bindingFn = (implicit: any, value: AST) => {
|
||||
return convertPropertyBinding(
|
||||
|
@ -683,14 +690,14 @@ function createHostBindingsFunction(
|
|||
|
||||
const {bindingName, instruction} = getBindingNameAndInstruction(name);
|
||||
|
||||
statements.push(...bindingExpr.stmts);
|
||||
statements.push(o.importExpr(instruction)
|
||||
.callFn([
|
||||
elVarExp,
|
||||
o.literal(bindingName),
|
||||
o.importExpr(R3.bind).callFn([bindingExpr.currValExpr]),
|
||||
])
|
||||
.toStmt());
|
||||
updateStatements.push(...bindingExpr.stmts);
|
||||
updateStatements.push(o.importExpr(instruction)
|
||||
.callFn([
|
||||
elVarExp,
|
||||
o.literal(bindingName),
|
||||
o.importExpr(R3.bind).callFn([bindingExpr.currValExpr]),
|
||||
])
|
||||
.toStmt());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -698,24 +705,31 @@ function createHostBindingsFunction(
|
|||
const createInstruction = styleBuilder.buildCreateLevelInstruction(null, constantPool);
|
||||
if (createInstruction) {
|
||||
const createStmt = createStylingStmt(createInstruction, bindingContext, bindingFn);
|
||||
statements.push(createStmt);
|
||||
createStatements.push(createStmt);
|
||||
}
|
||||
|
||||
styleBuilder.buildUpdateLevelInstructions(valueConverter).forEach(instruction => {
|
||||
const updateStmt = createStylingStmt(instruction, bindingContext, bindingFn);
|
||||
statements.push(updateStmt);
|
||||
updateStatements.push(updateStmt);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (statements.length > 0) {
|
||||
const typeName = meta.name;
|
||||
if (createStatements.length > 0 || updateStatements.length > 0) {
|
||||
const hostBindingsFnName = meta.name ? `${meta.name}_HostBindings` : null;
|
||||
const statements: o.Statement[] = [];
|
||||
if (createStatements.length > 0) {
|
||||
statements.push(renderFlagCheckIfStmt(core.RenderFlags.Create, createStatements));
|
||||
}
|
||||
if (updateStatements.length > 0) {
|
||||
statements.push(renderFlagCheckIfStmt(core.RenderFlags.Update, updateStatements));
|
||||
}
|
||||
return o.fn(
|
||||
[
|
||||
new o.FnParam(dirVarExp.name !, o.NUMBER_TYPE),
|
||||
new o.FnParam(elVarExp.name !, o.NUMBER_TYPE),
|
||||
new o.FnParam(RENDER_FLAGS, o.NUMBER_TYPE), new o.FnParam(CONTEXT_NAME, null),
|
||||
new o.FnParam(elVarExp.name !, o.NUMBER_TYPE)
|
||||
],
|
||||
statements, o.INFERRED_TYPE, null, typeName ? `${typeName}_HostBindings` : null);
|
||||
statements, o.INFERRED_TYPE, null, hostBindingsFnName);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -745,15 +759,6 @@ function getBindingNameAndInstruction(bindingName: string):
|
|||
return {bindingName, instruction};
|
||||
}
|
||||
|
||||
function createFactoryExtraStatementsFn(meta: R3DirectiveMetadata, bindingParser: BindingParser):
|
||||
((instance: o.Expression) => o.Statement[])|null {
|
||||
const eventBindings =
|
||||
bindingParser.createDirectiveHostEventAsts(metadataAsSummary(meta), meta.typeSourceSpan);
|
||||
return eventBindings && eventBindings.length ?
|
||||
(instance: o.Expression) => createHostListeners(instance, eventBindings, meta) :
|
||||
null;
|
||||
}
|
||||
|
||||
function createHostListeners(
|
||||
bindingContext: o.Expression, eventBindings: ParsedEvent[],
|
||||
meta: R3DirectiveMetadata): o.Statement[] {
|
||||
|
|
|
@ -80,8 +80,7 @@ export class StylingBuilder {
|
|||
private _useDefaultSanitizer = false;
|
||||
private _applyFnRequired = false;
|
||||
|
||||
constructor(
|
||||
private _elementIndexExpr: o.Expression, private _directiveIndexExpr: o.Expression|null) {}
|
||||
constructor(private _elementIndexExpr: o.Expression, private _directiveExpr: o.Expression|null) {}
|
||||
|
||||
registerBoundInput(input: t.BoundAttribute): boolean {
|
||||
// [attr.style] or [attr.class] are skipped in the code below,
|
||||
|
@ -217,15 +216,15 @@ export class StylingBuilder {
|
|||
// can be processed during runtime. These initial styles values are bound to
|
||||
// a constant because the inital style values do not change (since they're static).
|
||||
params.push(constantPool.getConstLiteral(initialStyles, true));
|
||||
} else if (useSanitizer || this._directiveIndexExpr) {
|
||||
} else if (useSanitizer || this._directiveExpr) {
|
||||
// no point in having an extra `null` value unless there are follow-up params
|
||||
params.push(o.NULL_EXPR);
|
||||
}
|
||||
|
||||
if (useSanitizer || this._directiveIndexExpr) {
|
||||
if (useSanitizer || this._directiveExpr) {
|
||||
params.push(useSanitizer ? o.importExpr(R3.defaultStyleSanitizer) : o.NULL_EXPR);
|
||||
if (this._directiveIndexExpr) {
|
||||
params.push(this._directiveIndexExpr);
|
||||
if (this._directiveExpr) {
|
||||
params.push(this._directiveExpr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -260,12 +259,12 @@ export class StylingBuilder {
|
|||
|
||||
if (mapBasedStyleValue) {
|
||||
params.push(convertFn(mapBasedStyleValue));
|
||||
} else if (this._directiveIndexExpr) {
|
||||
} else if (this._directiveExpr) {
|
||||
params.push(o.NULL_EXPR);
|
||||
}
|
||||
|
||||
if (this._directiveIndexExpr) {
|
||||
params.push(this._directiveIndexExpr);
|
||||
if (this._directiveExpr) {
|
||||
params.push(this._directiveExpr);
|
||||
}
|
||||
|
||||
return params;
|
||||
|
@ -289,13 +288,13 @@ export class StylingBuilder {
|
|||
if (allowUnits) {
|
||||
if (input.unit) {
|
||||
params.push(o.literal(input.unit));
|
||||
} else if (this._directiveIndexExpr) {
|
||||
} else if (this._directiveExpr) {
|
||||
params.push(o.NULL_EXPR);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._directiveIndexExpr) {
|
||||
params.push(this._directiveIndexExpr);
|
||||
if (this._directiveExpr) {
|
||||
params.push(this._directiveExpr);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
@ -325,8 +324,8 @@ export class StylingBuilder {
|
|||
reference: R3.elementStylingApply,
|
||||
buildParams: () => {
|
||||
const params: o.Expression[] = [this._elementIndexExpr];
|
||||
if (this._directiveIndexExpr) {
|
||||
params.push(this._directiveIndexExpr);
|
||||
if (this._directiveExpr) {
|
||||
params.push(this._directiveExpr);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import {Mutable, Type} from '../type';
|
|||
import {noSideEffects} from '../util';
|
||||
|
||||
import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF, NG_MODULE_DEF, NG_PIPE_DEF} from './fields';
|
||||
import {BaseDef, ComponentDef, ComponentDefFeature, ComponentQuery, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFeature, DirectiveType, DirectiveTypesOrFactory, PipeDef, PipeType, PipeTypesOrFactory} from './interfaces/definition';
|
||||
import {BaseDef, ComponentDef, ComponentDefFeature, ComponentQuery, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFeature, DirectiveType, DirectiveTypesOrFactory, HostBindingsFunction, PipeDef, PipeType, PipeTypesOrFactory} from './interfaces/definition';
|
||||
import {CssSelectorList, SelectorFlags} from './interfaces/projection';
|
||||
|
||||
export const EMPTY: {} = {};
|
||||
|
@ -149,7 +149,7 @@ export function defineComponent<T>(componentDefinition: {
|
|||
/**
|
||||
* Function executed by the parent template to allow child directive to apply host bindings.
|
||||
*/
|
||||
hostBindings?: (directiveIndex: number, elementIndex: number) => void;
|
||||
hostBindings?: HostBindingsFunction<T>;
|
||||
|
||||
/**
|
||||
* Function to create instances of content queries associated with a given directive.
|
||||
|
@ -597,7 +597,7 @@ export const defineDirective = defineComponent as any as<T>(directiveDefinition:
|
|||
/**
|
||||
* Function executed by the parent template to allow child directive to apply host bindings.
|
||||
*/
|
||||
hostBindings?: (directiveIndex: number, elementIndex: number) => void;
|
||||
hostBindings?: HostBindingsFunction<T>;
|
||||
|
||||
/**
|
||||
* Function to create instances of content queries associated with a given directive.
|
||||
|
|
|
@ -72,9 +72,9 @@ export function InheritDefinitionFeature(definition: DirectiveDef<any>| Componen
|
|||
const superHostBindings = superDef.hostBindings;
|
||||
if (superHostBindings) {
|
||||
if (prevHostBindings) {
|
||||
definition.hostBindings = (directiveIndex: number, elementIndex: number) => {
|
||||
superHostBindings(directiveIndex, elementIndex);
|
||||
prevHostBindings(directiveIndex, elementIndex);
|
||||
definition.hostBindings = (rf: RenderFlags, ctx: any, elementIndex: number) => {
|
||||
superHostBindings(rf, ctx, elementIndex);
|
||||
prevHostBindings(rf, ctx, elementIndex);
|
||||
};
|
||||
(definition as any).hostVars += superDef.hostVars;
|
||||
} else {
|
||||
|
|
|
@ -124,15 +124,9 @@ export function setHostBindings(tView: TView, viewData: LViewData): void {
|
|||
} else {
|
||||
// If it's not a number, it's a host binding function that needs to be executed.
|
||||
viewData[BINDING_INDEX] = bindingRootIndex;
|
||||
// We must subtract the header offset because the load() instruction
|
||||
// expects a raw, unadjusted index.
|
||||
// <HACK(misko)>: set the `previousOrParentTNode` so that hostBindings functions can
|
||||
// correctly retrieve it. This should be removed once we call the hostBindings function
|
||||
// inline as part of the `RenderFlags.Create` because in that case the value will already be
|
||||
// correctly set.
|
||||
setPreviousOrParentTNode(getTView().data[currentElementIndex + HEADER_OFFSET] as TNode);
|
||||
// </HACK>
|
||||
instruction(currentDirectiveIndex - HEADER_OFFSET, currentElementIndex);
|
||||
instruction(
|
||||
RenderFlags.Update, readElementValue(viewData[currentDirectiveIndex]),
|
||||
currentElementIndex);
|
||||
currentDirectiveIndex++;
|
||||
}
|
||||
}
|
||||
|
@ -1077,14 +1071,13 @@ function generatePropertyAliases(
|
|||
* @param classIndex Index of class to toggle. Because it is going to DOM, this is not subject to
|
||||
* renaming as part of minification.
|
||||
* @param value A value indicating if a given class should be added or removed.
|
||||
* @param directiveIndex the index for the directive that is attempting to change styling.
|
||||
* @param directive the ref to the directive that is attempting to change styling.
|
||||
*/
|
||||
export function elementClassProp(
|
||||
index: number, classIndex: number, value: boolean | PlayerFactory,
|
||||
directiveIndex?: number): void {
|
||||
if (directiveIndex != undefined) {
|
||||
index: number, classIndex: number, value: boolean | PlayerFactory, directive?: {}): void {
|
||||
if (directive != undefined) {
|
||||
return hackImplementationOfElementClassProp(
|
||||
index, classIndex, value, directiveIndex); // proper supported in next PR
|
||||
index, classIndex, value, directive); // proper supported in next PR
|
||||
}
|
||||
const val =
|
||||
(value instanceof BoundPlayerFactory) ? (value as BoundPlayerFactory<boolean>) : (!!value);
|
||||
|
@ -1118,17 +1111,17 @@ export function elementClassProp(
|
|||
* values that are passed in here will be applied to the element (if matched).
|
||||
* @param styleSanitizer An optional sanitizer function that will be used (if provided)
|
||||
* to sanitize the any CSS property values that are applied to the element (during rendering).
|
||||
* @param directiveIndex the index for the directive that is attempting to change styling.
|
||||
* @param directive the ref to the directive that is attempting to change styling.
|
||||
*/
|
||||
export function elementStyling(
|
||||
classDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
|
||||
styleDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
|
||||
styleSanitizer?: StyleSanitizeFn | null, directiveIndex?: number): void {
|
||||
if (directiveIndex !== undefined) {
|
||||
styleSanitizer?: StyleSanitizeFn | null, directive?: {}): void {
|
||||
if (directive != undefined) {
|
||||
getCreationMode() &&
|
||||
hackImplementationOfElementStyling(
|
||||
classDeclarations || null, styleDeclarations || null, styleSanitizer || null,
|
||||
directiveIndex); // supported in next PR
|
||||
directive); // supported in next PR
|
||||
return;
|
||||
}
|
||||
const tNode = getPreviousOrParentTNode();
|
||||
|
@ -1171,11 +1164,11 @@ export function elementStyling(
|
|||
* (Note that this is not the element index, but rather an index value allocated
|
||||
* specifically for element styling--the index must be the next index after the element
|
||||
* index.)
|
||||
* @param directiveIndex the index for the directive that is attempting to change styling.
|
||||
* @param directive the ref to the directive that is attempting to change styling.
|
||||
*/
|
||||
export function elementStylingApply(index: number, directiveIndex?: number): void {
|
||||
if (directiveIndex != undefined) {
|
||||
return hackImplementationOfElementStylingApply(index, directiveIndex); // supported in next PR
|
||||
export function elementStylingApply(index: number, directive?: {}): void {
|
||||
if (directive != undefined) {
|
||||
return hackImplementationOfElementStylingApply(index, directive); // supported in next PR
|
||||
}
|
||||
const viewData = getViewData();
|
||||
const isFirstRender = (viewData[FLAGS] & LViewFlags.CreationMode) !== 0;
|
||||
|
@ -1206,14 +1199,14 @@ export function elementStylingApply(index: number, directiveIndex?: number): voi
|
|||
* @param suffix Optional suffix. Used with scalar values to add unit such as `px`.
|
||||
* Note that when a suffix is provided then the underlying sanitizer will
|
||||
* be ignored.
|
||||
* @param directiveIndex the index for the directive that is attempting to change styling.
|
||||
* @param directive the ref to the directive that is attempting to change styling.
|
||||
*/
|
||||
export function elementStyleProp(
|
||||
index: number, styleIndex: number, value: string | number | String | PlayerFactory | null,
|
||||
suffix?: string, directiveIndex?: number): void {
|
||||
if (directiveIndex != undefined)
|
||||
suffix?: string, directive?: {}): void {
|
||||
if (directive != undefined)
|
||||
return hackImplementationOfElementStyleProp(
|
||||
index, styleIndex, value, suffix, directiveIndex); // supported in next PR
|
||||
index, styleIndex, value, suffix, directive); // supported in next PR
|
||||
let valueToAdd: string|null = null;
|
||||
if (value) {
|
||||
if (suffix) {
|
||||
|
@ -1251,14 +1244,14 @@ export function elementStyleProp(
|
|||
* @param styles A key/value style map of the styles that will be applied to the given element.
|
||||
* Any missing styles (that have already been applied to the element beforehand) will be
|
||||
* removed (unset) from the element's styling.
|
||||
* @param directiveIndex the index for the directive that is attempting to change styling.
|
||||
* @param directive the ref to the directive that is attempting to change styling.
|
||||
*/
|
||||
export function elementStylingMap<T>(
|
||||
index: number, classes: {[key: string]: any} | string | NO_CHANGE | null,
|
||||
styles?: {[styleName: string]: any} | NO_CHANGE | null, directiveIndex?: number): void {
|
||||
if (directiveIndex != undefined)
|
||||
styles?: {[styleName: string]: any} | NO_CHANGE | null, directive?: {}): void {
|
||||
if (directive != undefined)
|
||||
return hackImplementationOfElementStylingMap(
|
||||
index, classes, styles, directiveIndex); // supported in next PR
|
||||
index, classes, styles, directive); // supported in next PR
|
||||
const viewData = getViewData();
|
||||
const tNode = getTNode(index, viewData);
|
||||
const stylingContext = getStylingContext(index, viewData);
|
||||
|
@ -1282,22 +1275,20 @@ interface HostStylingHack {
|
|||
styleDeclarations: string[];
|
||||
styleSanitizer: StyleSanitizeFn|null;
|
||||
}
|
||||
interface HostStylingHackMap {
|
||||
[directiveIndex: number]: HostStylingHack;
|
||||
}
|
||||
type HostStylingHackMap = Map<{}, HostStylingHack>;
|
||||
|
||||
function hackImplementationOfElementStyling(
|
||||
classDeclarations: (string | boolean | InitialStylingFlags)[] | null,
|
||||
styleDeclarations: (string | boolean | InitialStylingFlags)[] | null,
|
||||
styleSanitizer: StyleSanitizeFn | null, directiveIndex: number): void {
|
||||
styleSanitizer: StyleSanitizeFn | null, directive: {}): void {
|
||||
const node = getNativeByTNode(getPreviousOrParentTNode(), getViewData());
|
||||
ngDevMode && assertDefined(node, 'expecting parent DOM node');
|
||||
const hostStylingHackMap: HostStylingHackMap =
|
||||
((node as any).hostStylingHack || ((node as any).hostStylingHack = {}));
|
||||
hostStylingHackMap[directiveIndex] = {
|
||||
((node as any).hostStylingHack || ((node as any).hostStylingHack = new Map()));
|
||||
hostStylingHackMap.set(directive, {
|
||||
classDeclarations: hackSquashDeclaration(classDeclarations),
|
||||
styleDeclarations: hackSquashDeclaration(styleDeclarations), styleSanitizer
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function hackSquashDeclaration(declarations: (string | boolean | InitialStylingFlags)[] | null):
|
||||
|
@ -1307,11 +1298,10 @@ function hackSquashDeclaration(declarations: (string | boolean | InitialStylingF
|
|||
}
|
||||
|
||||
function hackImplementationOfElementClassProp(
|
||||
index: number, classIndex: number, value: boolean | PlayerFactory,
|
||||
directiveIndex: number): void {
|
||||
index: number, classIndex: number, value: boolean | PlayerFactory, directive: {}): void {
|
||||
const node = getNativeByIndex(index, getViewData());
|
||||
ngDevMode && assertDefined(node, 'could not locate node');
|
||||
const hostStylingHack: HostStylingHack = (node as any).hostStylingHack[directiveIndex];
|
||||
const hostStylingHack: HostStylingHack = (node as any).hostStylingHack.get(directive);
|
||||
const className = hostStylingHack.classDeclarations[classIndex];
|
||||
const renderer = getRenderer();
|
||||
if (isProceduralRenderer(renderer)) {
|
||||
|
@ -1322,19 +1312,19 @@ function hackImplementationOfElementClassProp(
|
|||
}
|
||||
}
|
||||
|
||||
function hackImplementationOfElementStylingApply(index: number, directiveIndex?: number): void {
|
||||
function hackImplementationOfElementStylingApply(index: number, directive?: {}): void {
|
||||
// Do nothing because the hack implementation is eager.
|
||||
}
|
||||
|
||||
function hackImplementationOfElementStyleProp(
|
||||
index: number, styleIndex: number, value: string | number | String | PlayerFactory | null,
|
||||
suffix?: string, directiveIndex?: number): void {
|
||||
suffix?: string, directive?: {}): void {
|
||||
throw new Error('unimplemented. Should not be needed by ViewEngine compatibility');
|
||||
}
|
||||
|
||||
function hackImplementationOfElementStylingMap<T>(
|
||||
index: number, classes: {[key: string]: any} | string | NO_CHANGE | null,
|
||||
styles?: {[styleName: string]: any} | NO_CHANGE | null, directiveIndex?: number): void {
|
||||
styles?: {[styleName: string]: any} | NO_CHANGE | null, directive?: {}): void {
|
||||
throw new Error('unimplemented. Should not be needed by ViewEngine compatibility');
|
||||
}
|
||||
|
||||
|
@ -1531,6 +1521,10 @@ function postProcessBaseDirective<T>(
|
|||
'directives should be created before any bindings');
|
||||
ngDevMode && assertPreviousIsParent();
|
||||
|
||||
if (def.hostBindings) {
|
||||
def.hostBindings(RenderFlags.Create, directive, previousOrParentTNode.index);
|
||||
}
|
||||
|
||||
attachPatchData(directive, viewData);
|
||||
if (native) {
|
||||
attachPatchData(native, viewData);
|
||||
|
|
|
@ -145,7 +145,7 @@ export interface DirectiveDef<T> extends BaseDef<T> {
|
|||
readonly hostVars: number;
|
||||
|
||||
/** Refreshes host bindings on the associated directive. */
|
||||
hostBindings: HostBindingsFunction|null;
|
||||
hostBindings: HostBindingsFunction<T>|null;
|
||||
|
||||
/**
|
||||
* Static attributes to set on host element.
|
||||
|
@ -333,7 +333,7 @@ export type DirectiveTypeList =
|
|||
(DirectiveDef<any>| ComponentDef<any>|
|
||||
Type<any>/* Type as workaround for: Microsoft/TypeScript/issues/4881 */)[];
|
||||
|
||||
export type HostBindingsFunction = (directiveIndex: number, elementIndex: number) => void;
|
||||
export type HostBindingsFunction<T> = (rf: RenderFlags, ctx: T, elementIndex: number) => void;
|
||||
|
||||
/**
|
||||
* Type used for PipeDefs on component definition.
|
||||
|
|
|
@ -342,7 +342,7 @@ export interface TView {
|
|||
*
|
||||
* See VIEW_DATA.md for more information.
|
||||
*/
|
||||
expandoInstructions: (number|HostBindingsFunction)[]|null;
|
||||
expandoInstructions: (number|HostBindingsFunction<any>)[]|null;
|
||||
|
||||
/**
|
||||
* Full registry of directives and components that may be found in this view.
|
||||
|
|
|
@ -308,9 +308,10 @@ describe('InheritDefinitionFeature', () => {
|
|||
static ngDirectiveDef = defineDirective({
|
||||
type: SuperDirective,
|
||||
selectors: [['', 'superDir', '']],
|
||||
hostBindings: (directiveIndex: number, elementIndex: number) => {
|
||||
const instance = load(directiveIndex) as SuperDirective;
|
||||
elementProperty(elementIndex, 'id', bind(instance.id));
|
||||
hostBindings: (rf: RenderFlags, ctx: SuperDirective, elementIndex: number) => {
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elementIndex, 'id', bind(ctx.id));
|
||||
}
|
||||
},
|
||||
hostVars: 1,
|
||||
factory: () => new SuperDirective(),
|
||||
|
@ -323,9 +324,10 @@ describe('InheritDefinitionFeature', () => {
|
|||
static ngDirectiveDef = defineDirective({
|
||||
type: SubDirective,
|
||||
selectors: [['', 'subDir', '']],
|
||||
hostBindings: (directiveIndex: number, elementIndex: number) => {
|
||||
const instance = load(directiveIndex) as SubDirective;
|
||||
elementProperty(elementIndex, 'title', bind(instance.title));
|
||||
hostBindings: (rf: RenderFlags, ctx: SubDirective, elementIndex: number) => {
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elementIndex, 'title', bind(ctx.title));
|
||||
}
|
||||
},
|
||||
hostVars: 1,
|
||||
factory: () => subDir = new SubDirective(),
|
||||
|
|
|
@ -655,8 +655,10 @@ describe('di', () => {
|
|||
selectors: [['', 'hostBindingDir', '']],
|
||||
factory: () => hostBindingDir = new HostBindingDir(),
|
||||
hostVars: 1,
|
||||
hostBindings: (directiveIndex: number, elementIndex: number) => {
|
||||
elementProperty(elementIndex, 'id', bind(load<HostBindingDir>(directiveIndex).id));
|
||||
hostBindings: (rf: RenderFlags, ctx: any, elementIndex: number) => {
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elementIndex, 'id', bind(ctx.id));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -50,8 +50,10 @@ describe('host bindings', () => {
|
|||
selectors: [['', 'hostBindingDir', '']],
|
||||
factory: () => hostBindingDir = new HostBindingDir(),
|
||||
hostVars: 1,
|
||||
hostBindings: (directiveIndex: number, elementIndex: number) => {
|
||||
elementProperty(elementIndex, 'id', bind(load<HostBindingDir>(directiveIndex).id));
|
||||
hostBindings: (rf: RenderFlags, ctx: any, elementIndex: number) => {
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elementIndex, 'id', bind(ctx.id));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -67,9 +69,10 @@ describe('host bindings', () => {
|
|||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 1,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
const ctx = load(dirIndex) as HostBindingComp;
|
||||
elementProperty(elIndex, 'id', bind(ctx.id));
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingComp, elIndex: number) => {
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'id', bind(ctx.id));
|
||||
}
|
||||
},
|
||||
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
|
||||
});
|
||||
|
@ -87,8 +90,10 @@ describe('host bindings', () => {
|
|||
selectors: [['', 'dir', '']],
|
||||
factory: () => directiveInstance = new Directive,
|
||||
hostVars: 1,
|
||||
hostBindings: (directiveIndex: number, elementIndex: number) => {
|
||||
elementProperty(elementIndex, 'className', bind(load<Directive>(directiveIndex).klass));
|
||||
hostBindings: (rf: RenderFlags, ctx: any, elementIndex: number) => {
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elementIndex, 'className', bind(ctx.klass));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -135,9 +140,10 @@ describe('host bindings', () => {
|
|||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 1,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
const instance = load(dirIndex) as CompWithProviders;
|
||||
elementProperty(elIndex, 'id', bind(instance.id));
|
||||
hostBindings: (rf: RenderFlags, ctx: CompWithProviders, elIndex: number) => {
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'id', bind(ctx.id));
|
||||
}
|
||||
},
|
||||
template: (rf: RenderFlags, ctx: CompWithProviders) => {},
|
||||
features: [ProvidersFeature([[ServiceOne], [ServiceTwo]])]
|
||||
|
@ -168,9 +174,10 @@ describe('host bindings', () => {
|
|||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 1,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
const ctx = load(dirIndex) as HostTitleComp;
|
||||
elementProperty(elIndex, 'title', bind(ctx.title));
|
||||
hostBindings: (rf: RenderFlags, ctx: HostTitleComp, elIndex: number) => {
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'title', bind(ctx.title));
|
||||
}
|
||||
},
|
||||
template: (rf: RenderFlags, ctx: HostTitleComp) => {}
|
||||
});
|
||||
|
@ -248,9 +255,10 @@ describe('host bindings', () => {
|
|||
vars: 0,
|
||||
hostVars: 1,
|
||||
features: [NgOnChangesFeature],
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
const ctx = load(dirIndex) as InitHookComp;
|
||||
elementProperty(elIndex, 'title', bind(ctx.value));
|
||||
hostBindings: (rf: RenderFlags, ctx: InitHookComp, elIndex: number) => {
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'title', bind(ctx.value));
|
||||
}
|
||||
},
|
||||
inputs: {inputValue: 'inputValue'}
|
||||
});
|
||||
|
@ -413,12 +421,14 @@ describe('host bindings', () => {
|
|||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 8,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
const ctx = load(dirIndex) as HostBindingComp;
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingComp, elIndex: number) => {
|
||||
// LViewData: [..., id, dir, title, ctx.id, pf1, ctx.title, ctx.otherTitle, pf2]
|
||||
elementProperty(elIndex, 'id', bind(pureFunction1(3, ff, ctx.id)));
|
||||
elementProperty(elIndex, 'dir', bind(ctx.dir));
|
||||
elementProperty(elIndex, 'title', bind(pureFunction2(5, ff2, ctx.title, ctx.otherTitle)));
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'id', bind(pureFunction1(3, ff, ctx.id)));
|
||||
elementProperty(elIndex, 'dir', bind(ctx.dir));
|
||||
elementProperty(
|
||||
elIndex, 'title', bind(pureFunction2(5, ff2, ctx.title, ctx.otherTitle)));
|
||||
}
|
||||
},
|
||||
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
|
||||
});
|
||||
|
@ -487,10 +497,11 @@ describe('host bindings', () => {
|
|||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 3,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingComp, elIndex: number) => {
|
||||
// LViewData: [..., id, ctx.id, pf1]
|
||||
const ctx = load(dirIndex) as HostBindingComp;
|
||||
elementProperty(elIndex, 'id', bind(pureFunction1(1, ff, ctx.id)));
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'id', bind(pureFunction1(1, ff, ctx.id)));
|
||||
}
|
||||
},
|
||||
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
|
||||
});
|
||||
|
@ -515,10 +526,11 @@ describe('host bindings', () => {
|
|||
selectors: [['', 'hostDir', '']],
|
||||
factory: () => hostBindingDir = new HostBindingDir(),
|
||||
hostVars: 3,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingDir, elIndex: number) => {
|
||||
// LViewData [..., title, ctx.title, pf1]
|
||||
const ctx = load(dirIndex) as HostBindingDir;
|
||||
elementProperty(elIndex, 'title', bind(pureFunction1(1, ff1, ctx.title)));
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'title', bind(pureFunction1(1, ff1, ctx.title)));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -576,14 +588,15 @@ describe('host bindings', () => {
|
|||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 6,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingComp, elIndex: number) => {
|
||||
// LViewData: [..., id, title, ctx.id, pf1, ctx.title, pf1]
|
||||
const ctx = load(dirIndex) as HostBindingComp;
|
||||
elementProperty(
|
||||
elIndex, 'id', bind(ctx.condition ? pureFunction1(2, ff, ctx.id) : 'green'));
|
||||
elementProperty(
|
||||
elIndex, 'title',
|
||||
bind(ctx.otherCondition ? pureFunction1(4, ff1, ctx.title) : 'other title'));
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(
|
||||
elIndex, 'id', bind(ctx.condition ? pureFunction1(2, ff, ctx.id) : 'green'));
|
||||
elementProperty(
|
||||
elIndex, 'title',
|
||||
bind(ctx.otherCondition ? pureFunction1(4, ff1, ctx.title) : 'other title'));
|
||||
}
|
||||
},
|
||||
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
|
||||
});
|
||||
|
@ -665,9 +678,10 @@ describe('host bindings', () => {
|
|||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 1,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
elementProperty(
|
||||
elIndex, 'id', bind(load<HostBindingWithContentChildren>(dirIndex).foos.length));
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingWithContentChildren, elIndex: number) => {
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'id', bind(ctx.foos.length));
|
||||
}
|
||||
},
|
||||
contentQueries: (dirIndex) => { registerContentQuery(query(null, ['foo']), dirIndex); },
|
||||
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
||||
|
@ -721,8 +735,10 @@ describe('host bindings', () => {
|
|||
consts: 0,
|
||||
vars: 0,
|
||||
hostVars: 1,
|
||||
hostBindings: (dirIndex: number, elIndex: number) => {
|
||||
elementProperty(elIndex, 'id', bind(load<HostBindingWithContentHooks>(dirIndex).myValue));
|
||||
hostBindings: (rf: RenderFlags, ctx: HostBindingWithContentHooks, elIndex: number) => {
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'id', bind(ctx.myValue));
|
||||
}
|
||||
},
|
||||
template: (rf: RenderFlags, cmp: HostBindingWithContentHooks) => {}
|
||||
});
|
||||
|
|
|
@ -447,10 +447,11 @@ describe('render3 integration test', () => {
|
|||
},
|
||||
factory: () => cmptInstance = new TodoComponentHostBinding,
|
||||
hostVars: 1,
|
||||
hostBindings: function(directiveIndex: number, elementIndex: number): void {
|
||||
// host bindings
|
||||
elementProperty(
|
||||
elementIndex, 'title', bind(load<TodoComponentHostBinding>(directiveIndex).title));
|
||||
hostBindings: function(rf: RenderFlags, ctx: any, elementIndex: number): void {
|
||||
if (rf & RenderFlags.Update) {
|
||||
// host bindings
|
||||
elementProperty(elementIndex, 'title', bind(ctx.title));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1379,8 +1380,11 @@ describe('render3 integration test', () => {
|
|||
return hostBindingDir = new HostBindingDir();
|
||||
},
|
||||
hostVars: 1,
|
||||
hostBindings: function HostBindingDir_HostBindings(dirIndex: number, elIndex: number) {
|
||||
elementAttribute(elIndex, 'aria-label', bind(load<HostBindingDir>(dirIndex).label));
|
||||
hostBindings: function HostBindingDir_HostBindings(
|
||||
rf: RenderFlags, ctx: any, elIndex: number) {
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementAttribute(elIndex, 'aria-label', bind(ctx.label));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -229,7 +229,7 @@ ivyEnabled && describe('render3 jit', () => {
|
|||
const cmpDef = (Cmp as any).ngComponentDef as ComponentDef<Cmp>;
|
||||
|
||||
expect(cmpDef.hostBindings).toBeDefined();
|
||||
expect(cmpDef.hostBindings !.length).toBe(2);
|
||||
expect(cmpDef.hostBindings !.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should compile @Pipes without errors', () => {
|
||||
|
|
|
@ -449,7 +449,42 @@ describe('event listeners', () => {
|
|||
expect(comp.counters).toEqual([1, 1]);
|
||||
});
|
||||
|
||||
it('should support host listeners', () => {
|
||||
it('should support host listeners on components', () => {
|
||||
let events: string[] = [];
|
||||
class MyComp {
|
||||
/* @HostListener('click') */
|
||||
onClick() { events.push('click!'); }
|
||||
|
||||
static ngComponentDef = defineComponent({
|
||||
type: MyComp,
|
||||
selectors: [['comp']],
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function CompTemplate(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
text(0, 'Some text');
|
||||
}
|
||||
},
|
||||
factory: () => { return new MyComp(); },
|
||||
hostBindings: function HostListenerDir_HostBindings(
|
||||
rf: RenderFlags, ctx: any, elIndex: number) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
listener('click', function() { return ctx.onClick(); });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const fixture = new ComponentFixture(MyComp);
|
||||
const host = fixture.hostElement;
|
||||
host.click();
|
||||
expect(events).toEqual(['click!']);
|
||||
|
||||
host.click();
|
||||
expect(events).toEqual(['click!', 'click!']);
|
||||
});
|
||||
|
||||
it('should support host listeners on directives', () => {
|
||||
let events: string[] = [];
|
||||
|
||||
class HostListenerDir {
|
||||
|
@ -459,11 +494,13 @@ describe('event listeners', () => {
|
|||
static ngDirectiveDef = defineDirective({
|
||||
type: HostListenerDir,
|
||||
selectors: [['', 'hostListenerDir', '']],
|
||||
factory: function HostListenerDir_Factory() {
|
||||
const $dir$ = new HostListenerDir();
|
||||
listener('click', function() { return $dir$.onClick(); });
|
||||
return $dir$;
|
||||
},
|
||||
factory: function HostListenerDir_Factory() { return new HostListenerDir(); },
|
||||
hostBindings: function HostListenerDir_HostBindings(
|
||||
rf: RenderFlags, ctx: any, elIndex: number) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
listener('click', function() { return ctx.onClick(); });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1828,9 +1828,10 @@ describe('ViewContainerRef', () => {
|
|||
template: (rf: RenderFlags, cmp: HostBindingCmpt) => {},
|
||||
hostVars: 1,
|
||||
attributes: ['id', 'attribute'],
|
||||
hostBindings: function(dirIndex, elIndex) {
|
||||
const cmptInstance = load<HostBindingCmpt>(dirIndex);
|
||||
elementProperty(elIndex, 'title', bind(cmptInstance.title));
|
||||
hostBindings: function(rf: RenderFlags, ctx: HostBindingCmpt, elIndex: number) {
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(elIndex, 'title', bind(ctx.title));
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue