Revert "refactor(ivy): ensure directive host bindings use the styling algorithm (#27134)"
This reverts commit b5dbf5154e
.
This commit is contained in:
parent
b5dbf5154e
commit
4222b63639
|
@ -724,179 +724,4 @@ describe('compiler compliance: styling', () => {
|
||||||
expectEmit(result.source, template, 'Incorrect template');
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('@Component host styles/classes', () => {
|
|
||||||
it('should generate style/class instructions for a host component creation definition', () => {
|
|
||||||
const files = {
|
|
||||||
app: {
|
|
||||||
'spec.ts': `
|
|
||||||
import {Component, NgModule, HostBinding} from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'my-component',
|
|
||||||
template: '',
|
|
||||||
host: {
|
|
||||||
'style': 'width:200px; height:500px',
|
|
||||||
'class': 'foo baz'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
export class MyComponent {
|
|
||||||
@HostBinding('style')
|
|
||||||
myStyle = {width:'100px'};
|
|
||||||
|
|
||||||
@HostBinding('class')
|
|
||||||
myClass = {bar:false};
|
|
||||||
|
|
||||||
@HostBinding('style.color')
|
|
||||||
myColorProp = 'red';
|
|
||||||
|
|
||||||
@HostBinding('class.foo')
|
|
||||||
myFooClass = 'red';
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({declarations: [MyComponent]})
|
|
||||||
export class MyModule {}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const template = `
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const result = compile(files, angularFiles);
|
|
||||||
expectEmit(result.source, template, 'Incorrect template');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate style/class instructions for multiple host binding definitions', () => {
|
|
||||||
const files = {
|
|
||||||
app: {
|
|
||||||
'spec.ts': `
|
|
||||||
import {Component, NgModule, HostBinding} from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'my-component',
|
|
||||||
template: '',
|
|
||||||
host: {
|
|
||||||
'[style.height.pt]': 'myHeightProp',
|
|
||||||
'[class.bar]': 'myBarClass'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
export class MyComponent {
|
|
||||||
myHeightProp = 20;
|
|
||||||
myBarClass = true;
|
|
||||||
|
|
||||||
@HostBinding('style')
|
|
||||||
myStyle = {};
|
|
||||||
|
|
||||||
@HostBinding('style.width')
|
|
||||||
myWidthProp = '500px';
|
|
||||||
|
|
||||||
@HostBinding('class.foo')
|
|
||||||
myFooClass = true;
|
|
||||||
|
|
||||||
@HostBinding('class')
|
|
||||||
myClasses = {a:true, b:true};
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({declarations: [MyComponent]})
|
|
||||||
export class MyModule {}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const template = `
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const result = compile(files, angularFiles);
|
|
||||||
expectEmit(result.source, template, 'Incorrect template');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate styling instructions for multiple directives that contain host binding definitions',
|
|
||||||
() => {
|
|
||||||
const files = {
|
|
||||||
app: {
|
|
||||||
'spec.ts': `
|
|
||||||
import {Directive, Component, NgModule, HostBinding} from '@angular/core';
|
|
||||||
|
|
||||||
@Directive({selector: '[myWidthDir]'})
|
|
||||||
export class WidthDirective {
|
|
||||||
@HostBinding('style.width')
|
|
||||||
myWidth = 200;
|
|
||||||
|
|
||||||
@HostBinding('class.foo')
|
|
||||||
myFooClass = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Directive({selector: '[myHeightDir]'})
|
|
||||||
export class HeightDirective {
|
|
||||||
@HostBinding('style.height')
|
|
||||||
myHeight = 200;
|
|
||||||
|
|
||||||
@HostBinding('class.bar')
|
|
||||||
myBarClass = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'my-component',
|
|
||||||
template: '
|
|
||||||
<div myWidthDir myHeightDir></div>
|
|
||||||
',
|
|
||||||
})
|
|
||||||
export class MyComponent {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({declarations: [MyComponent, WidthDirective, HeightDirective]})
|
|
||||||
export class MyModule {}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const template = `
|
|
||||||
const _c0 = ["foo"];
|
|
||||||
const _c1 = ["width"];
|
|
||||||
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 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);
|
|
||||||
}
|
|
||||||
…
|
|
||||||
`;
|
|
||||||
|
|
||||||
const result = compile(files, angularFiles);
|
|
||||||
expectEmit(result.source, template, 'Incorrect template');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -481,7 +481,8 @@ describe('ngtsc behavioral tests', () => {
|
||||||
expect(jsContents)
|
expect(jsContents)
|
||||||
.toContain(`i0.ɵelementProperty(elIndex, "prop", i0.ɵbind(i0.ɵload(dirIndex).bar));`);
|
.toContain(`i0.ɵelementProperty(elIndex, "prop", i0.ɵbind(i0.ɵload(dirIndex).bar));`);
|
||||||
expect(jsContents)
|
expect(jsContents)
|
||||||
.toContain('i0.ɵelementClassProp(elIndex, 0, i0.ɵload(dirIndex).someClass, dirIndex)');
|
.toContain(
|
||||||
|
'i0.ɵelementProperty(elIndex, "class.someclass", i0.ɵbind(i0.ɵload(dirIndex).someClass))');
|
||||||
|
|
||||||
const factoryDef = `
|
const factoryDef = `
|
||||||
factory: function FooCmp_Factory(t) {
|
factory: function FooCmp_Factory(t) {
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {CompileReflector} from '../../compile_reflector';
|
||||||
import {BindingForm, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter';
|
import {BindingForm, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter';
|
||||||
import {ConstantPool, DefinitionKind} from '../../constant_pool';
|
import {ConstantPool, DefinitionKind} from '../../constant_pool';
|
||||||
import * as core from '../../core';
|
import * as core from '../../core';
|
||||||
import {AST, ParsedEvent} from '../../expression_parser/ast';
|
import {ParsedEvent} from '../../expression_parser/ast';
|
||||||
import {LifecycleHooks} from '../../lifecycle_reflector';
|
import {LifecycleHooks} from '../../lifecycle_reflector';
|
||||||
import * as o from '../../output/output_ast';
|
import * as o from '../../output/output_ast';
|
||||||
import {typeSourceSpan} from '../../parse_util';
|
import {typeSourceSpan} from '../../parse_util';
|
||||||
|
@ -27,7 +27,6 @@ import {Render3ParseResult} from '../r3_template_transform';
|
||||||
import {typeWithParameters} from '../util';
|
import {typeWithParameters} from '../util';
|
||||||
|
|
||||||
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3QueryMetadata} from './api';
|
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3QueryMetadata} from './api';
|
||||||
import {StylingBuilder, StylingInstruction} from './styling';
|
|
||||||
import {BindingScope, TemplateDefinitionBuilder, ValueConverter, renderFlagCheckIfStmt} from './template';
|
import {BindingScope, TemplateDefinitionBuilder, ValueConverter, renderFlagCheckIfStmt} from './template';
|
||||||
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, mapToExpression, temporaryAllocator} from './util';
|
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, mapToExpression, temporaryAllocator} from './util';
|
||||||
|
|
||||||
|
@ -66,48 +65,23 @@ function baseDirectiveFields(
|
||||||
// Initialize hostVars to number of bound host properties (interpolations illegal)
|
// Initialize hostVars to number of bound host properties (interpolations illegal)
|
||||||
let hostVars = Object.keys(meta.host.properties).length;
|
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 allOtherAttributes: any = {};
|
|
||||||
const attrNames = Object.getOwnPropertyNames(meta.host.attributes);
|
|
||||||
for (let i = 0; i < attrNames.length; i++) {
|
|
||||||
const attr = attrNames[i];
|
|
||||||
const value = meta.host.attributes[attr];
|
|
||||||
switch (attr) {
|
|
||||||
// style attributes are handled in the styling context
|
|
||||||
case 'style':
|
|
||||||
styleBuilder.registerStyleAttr(value);
|
|
||||||
break;
|
|
||||||
// class attributes are handled in the styling context
|
|
||||||
case 'class':
|
|
||||||
styleBuilder.registerClassAttr(value);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
allOtherAttributes[attr] = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// e.g. `attributes: ['role', 'listbox']`
|
|
||||||
definitionMap.set('attributes', createHostAttributesArray(allOtherAttributes));
|
|
||||||
|
|
||||||
// e.g. `hostBindings: (dirIndex, elIndex) => { ... }
|
// e.g. `hostBindings: (dirIndex, elIndex) => { ... }
|
||||||
definitionMap.set(
|
definitionMap.set(
|
||||||
'hostBindings',
|
'hostBindings',
|
||||||
createHostBindingsFunction(
|
createHostBindingsFunction(meta, bindingParser, constantPool, (slots: number) => {
|
||||||
meta, elVarExp, dirVarExp, styleBuilder, bindingParser, constantPool, (slots: number) => {
|
const originalSlots = hostVars;
|
||||||
const originalSlots = hostVars;
|
hostVars += slots;
|
||||||
hostVars += slots;
|
return originalSlots;
|
||||||
return originalSlots;
|
}));
|
||||||
}));
|
|
||||||
|
|
||||||
if (hostVars) {
|
if (hostVars) {
|
||||||
// e.g. `hostVars: 2
|
// e.g. `hostVars: 2
|
||||||
definitionMap.set('hostVars', o.literal(hostVars));
|
definitionMap.set('hostVars', o.literal(hostVars));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// e.g. `attributes: ['role', 'listbox']`
|
||||||
|
definitionMap.set('attributes', createHostAttributesArray(meta));
|
||||||
|
|
||||||
// e.g 'inputs: {a: 'a'}`
|
// e.g 'inputs: {a: 'a'}`
|
||||||
definitionMap.set('inputs', conditionallyCreateMapObjectLiteral(meta.inputs));
|
definitionMap.set('inputs', conditionallyCreateMapObjectLiteral(meta.inputs));
|
||||||
|
|
||||||
|
@ -492,8 +466,9 @@ function createDirectiveSelector(selector: string): o.Expression {
|
||||||
return asLiteral(core.parseSelectorToR3Selector(selector));
|
return asLiteral(core.parseSelectorToR3Selector(selector));
|
||||||
}
|
}
|
||||||
|
|
||||||
function createHostAttributesArray(attributes: any): o.Expression|null {
|
function createHostAttributesArray(meta: R3DirectiveMetadata): o.Expression|null {
|
||||||
const values: o.Expression[] = [];
|
const values: o.Expression[] = [];
|
||||||
|
const attributes = meta.host.attributes;
|
||||||
for (let key of Object.getOwnPropertyNames(attributes)) {
|
for (let key of Object.getOwnPropertyNames(attributes)) {
|
||||||
const value = attributes[key];
|
const value = attributes[key];
|
||||||
values.push(o.literal(key), o.literal(value));
|
values.push(o.literal(key), o.literal(value));
|
||||||
|
@ -636,8 +611,7 @@ function createViewQueriesFunction(
|
||||||
|
|
||||||
// Return a host binding function or null if one is not necessary.
|
// Return a host binding function or null if one is not necessary.
|
||||||
function createHostBindingsFunction(
|
function createHostBindingsFunction(
|
||||||
meta: R3DirectiveMetadata, elVarExp: o.ReadVarExpr, dirVarExp: o.ReadVarExpr,
|
meta: R3DirectiveMetadata, bindingParser: BindingParser, constantPool: ConstantPool,
|
||||||
styleBuilder: StylingBuilder, bindingParser: BindingParser, constantPool: ConstantPool,
|
|
||||||
allocatePureFunctionSlots: (slots: number) => number): o.Expression|null {
|
allocatePureFunctionSlots: (slots: number) => number): o.Expression|null {
|
||||||
const statements: o.Statement[] = [];
|
const statements: o.Statement[] = [];
|
||||||
|
|
||||||
|
@ -647,13 +621,7 @@ function createHostBindingsFunction(
|
||||||
|
|
||||||
// Calculate the host property bindings
|
// Calculate the host property bindings
|
||||||
const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
|
const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
|
||||||
const bindingContext = o.importExpr(R3.load).callFn([dirVarExp]);
|
const bindingContext = o.importExpr(R3.load).callFn([o.variable('dirIndex')]);
|
||||||
|
|
||||||
const bindingFn = (implicit: any, value: AST) => {
|
|
||||||
return convertPropertyBinding(
|
|
||||||
null, implicit, value, 'b', BindingForm.TrySimple, () => error('Unexpected interpolation'));
|
|
||||||
};
|
|
||||||
|
|
||||||
if (bindings) {
|
if (bindings) {
|
||||||
const valueConverter = new ValueConverter(
|
const valueConverter = new ValueConverter(
|
||||||
constantPool,
|
constantPool,
|
||||||
|
@ -661,43 +629,22 @@ function createHostBindingsFunction(
|
||||||
/* pipes are illegal here */ () => error('Unexpected pipe'));
|
/* pipes are illegal here */ () => error('Unexpected pipe'));
|
||||||
|
|
||||||
for (const binding of bindings) {
|
for (const binding of bindings) {
|
||||||
const name = binding.name;
|
// resolve literal arrays and literal objects
|
||||||
const stylePrefix = name.substring(0, 5).toLowerCase();
|
const value = binding.expression.visit(valueConverter);
|
||||||
if (stylePrefix === 'style') {
|
const bindingExpr = convertPropertyBinding(
|
||||||
const {propertyName, unit} = parseNamedProperty(name);
|
null, bindingContext, value, 'b', BindingForm.TrySimple,
|
||||||
styleBuilder.registerStyleInput(propertyName, binding.expression, unit, binding.sourceSpan);
|
() => error('Unexpected interpolation'));
|
||||||
} else if (stylePrefix === 'class') {
|
|
||||||
styleBuilder.registerClassInput(
|
|
||||||
parseNamedProperty(name).propertyName, binding.expression, binding.sourceSpan);
|
|
||||||
} else {
|
|
||||||
// resolve literal arrays and literal objects
|
|
||||||
const value = binding.expression.visit(valueConverter);
|
|
||||||
const bindingExpr = bindingFn(bindingContext, value);
|
|
||||||
|
|
||||||
const {bindingName, instruction} = getBindingNameAndInstruction(name);
|
const {bindingName, instruction} = getBindingNameAndInstruction(binding.name);
|
||||||
|
|
||||||
statements.push(...bindingExpr.stmts);
|
statements.push(...bindingExpr.stmts);
|
||||||
statements.push(o.importExpr(instruction)
|
statements.push(o.importExpr(instruction)
|
||||||
.callFn([
|
.callFn([
|
||||||
elVarExp,
|
o.variable('elIndex'),
|
||||||
o.literal(bindingName),
|
o.literal(bindingName),
|
||||||
o.importExpr(R3.bind).callFn([bindingExpr.currValExpr]),
|
o.importExpr(R3.bind).callFn([bindingExpr.currValExpr]),
|
||||||
])
|
])
|
||||||
.toStmt());
|
.toStmt());
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (styleBuilder.hasBindingsOrInitialValues) {
|
|
||||||
const createInstruction = styleBuilder.buildCreateLevelInstruction(null, constantPool);
|
|
||||||
if (createInstruction) {
|
|
||||||
const createStmt = createStylingStmt(createInstruction, bindingContext, bindingFn);
|
|
||||||
statements.push(createStmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
styleBuilder.buildUpdateLevelInstructions(valueConverter).forEach(instruction => {
|
|
||||||
const updateStmt = createStylingStmt(instruction, bindingContext, bindingFn);
|
|
||||||
statements.push(updateStmt);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -705,8 +652,8 @@ function createHostBindingsFunction(
|
||||||
const typeName = meta.name;
|
const typeName = meta.name;
|
||||||
return o.fn(
|
return o.fn(
|
||||||
[
|
[
|
||||||
new o.FnParam(dirVarExp.name !, o.NUMBER_TYPE),
|
new o.FnParam('dirIndex', o.NUMBER_TYPE),
|
||||||
new o.FnParam(elVarExp.name !, o.NUMBER_TYPE),
|
new o.FnParam('elIndex', o.NUMBER_TYPE),
|
||||||
],
|
],
|
||||||
statements, o.INFERRED_TYPE, null, typeName ? `${typeName}_HostBindings` : null);
|
statements, o.INFERRED_TYPE, null, typeName ? `${typeName}_HostBindings` : null);
|
||||||
}
|
}
|
||||||
|
@ -714,14 +661,6 @@ function createHostBindingsFunction(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createStylingStmt(
|
|
||||||
instruction: StylingInstruction, bindingContext: any, bindingFn: Function): o.Statement {
|
|
||||||
const params = instruction.buildParams(value => bindingFn(bindingContext, value).currValExpr);
|
|
||||||
return o.importExpr(instruction.reference, null, instruction.sourceSpan)
|
|
||||||
.callFn(params, instruction.sourceSpan)
|
|
||||||
.toStmt();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBindingNameAndInstruction(bindingName: string):
|
function getBindingNameAndInstruction(bindingName: string):
|
||||||
{bindingName: string, instruction: o.ExternalReference} {
|
{bindingName: string, instruction: o.ExternalReference} {
|
||||||
let instruction !: o.ExternalReference;
|
let instruction !: o.ExternalReference;
|
||||||
|
@ -829,19 +768,3 @@ function compileStyles(styles: string[], selector: string, hostSelector: string)
|
||||||
const shadowCss = new ShadowCss();
|
const shadowCss = new ShadowCss();
|
||||||
return styles.map(style => { return shadowCss !.shimCssText(style, selector, hostSelector); });
|
return styles.map(style => { return shadowCss !.shimCssText(style, selector, hostSelector); });
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseNamedProperty(name: string): {propertyName: string, unit: string} {
|
|
||||||
let unit = '';
|
|
||||||
let propertyName = '';
|
|
||||||
const index = name.indexOf('.');
|
|
||||||
if (index > 0) {
|
|
||||||
const unitIndex = name.lastIndexOf('.');
|
|
||||||
if (unitIndex !== index) {
|
|
||||||
unit = name.substring(unitIndex + 1, name.length);
|
|
||||||
propertyName = name.substring(index + 1, unitIndex);
|
|
||||||
} else {
|
|
||||||
propertyName = name.substring(index + 1, name.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {propertyName, unit};
|
|
||||||
}
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
import {ConstantPool} from '../../constant_pool';
|
import {ConstantPool} from '../../constant_pool';
|
||||||
import {InitialStylingFlags} from '../../core';
|
import {InitialStylingFlags} from '../../core';
|
||||||
import {AST, BindingType, ParseSpan} from '../../expression_parser/ast';
|
import {BindingType} from '../../expression_parser/ast';
|
||||||
import * as o from '../../output/output_ast';
|
import * as o from '../../output/output_ast';
|
||||||
import {ParseSourceSpan} from '../../parse_util';
|
import {ParseSourceSpan} from '../../parse_util';
|
||||||
import * as t from '../r3_ast';
|
import * as t from '../r3_ast';
|
||||||
|
@ -16,7 +16,6 @@ import {Identifiers as R3} from '../r3_identifiers';
|
||||||
import {parse as parseStyle} from './style_parser';
|
import {parse as parseStyle} from './style_parser';
|
||||||
import {ValueConverter} from './template';
|
import {ValueConverter} from './template';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A styling expression summary that is to be processed by the compiler
|
* A styling expression summary that is to be processed by the compiler
|
||||||
*/
|
*/
|
||||||
|
@ -26,17 +25,6 @@ export interface StylingInstruction {
|
||||||
buildParams(convertFn: (value: any) => o.Expression): o.Expression[];
|
buildParams(convertFn: (value: any) => o.Expression): o.Expression[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* An internal record of the input data for a styling binding
|
|
||||||
*/
|
|
||||||
interface BoundStylingEntry {
|
|
||||||
name: string;
|
|
||||||
unit: string|null;
|
|
||||||
sourceSpan: ParseSourceSpan;
|
|
||||||
value: AST;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces creation/update instructions for all styling bindings (class and style)
|
* Produces creation/update instructions for all styling bindings (class and style)
|
||||||
*
|
*
|
||||||
|
@ -65,11 +53,12 @@ interface BoundStylingEntry {
|
||||||
export class StylingBuilder {
|
export class StylingBuilder {
|
||||||
public readonly hasBindingsOrInitialValues = false;
|
public readonly hasBindingsOrInitialValues = false;
|
||||||
|
|
||||||
private _classMapInput: BoundStylingEntry|null = null;
|
private _indexLiteral: o.LiteralExpr;
|
||||||
private _styleMapInput: BoundStylingEntry|null = null;
|
private _classMapInput: t.BoundAttribute|null = null;
|
||||||
private _singleStyleInputs: BoundStylingEntry[]|null = null;
|
private _styleMapInput: t.BoundAttribute|null = null;
|
||||||
private _singleClassInputs: BoundStylingEntry[]|null = null;
|
private _singleStyleInputs: t.BoundAttribute[]|null = null;
|
||||||
private _lastStylingInput: BoundStylingEntry|null = null;
|
private _singleClassInputs: t.BoundAttribute[]|null = null;
|
||||||
|
private _lastStylingInput: t.BoundAttribute|null = null;
|
||||||
|
|
||||||
// maps are used instead of hash maps because a Map will
|
// maps are used instead of hash maps because a Map will
|
||||||
// retain the ordering of the keys
|
// retain the ordering of the keys
|
||||||
|
@ -80,69 +69,46 @@ export class StylingBuilder {
|
||||||
private _useDefaultSanitizer = false;
|
private _useDefaultSanitizer = false;
|
||||||
private _applyFnRequired = false;
|
private _applyFnRequired = false;
|
||||||
|
|
||||||
constructor(
|
constructor(elementIndex: number) { this._indexLiteral = o.literal(elementIndex); }
|
||||||
private _elementIndexExpr: o.Expression, private _directiveIndexExpr: o.Expression|null) {}
|
|
||||||
|
|
||||||
registerBoundInput(input: t.BoundAttribute): boolean {
|
registerInput(input: t.BoundAttribute): boolean {
|
||||||
// [attr.style] or [attr.class] are skipped in the code below,
|
// [attr.style] or [attr.class] are skipped in the code below,
|
||||||
// they should not be treated as styling-based bindings since
|
// they should not be treated as styling-based bindings since
|
||||||
// they are intended to be written directly to the attr and
|
// they are intended to be written directly to the attr and
|
||||||
// will therefore skip all style/class resolution that is present
|
// will therefore skip all style/class resolution that is present
|
||||||
// with style="", [style]="" and [style.prop]="", class="",
|
// with style="", [style]="" and [style.prop]="", class="",
|
||||||
// [class.prop]="". [class]="" assignments
|
// [class.prop]="". [class]="" assignments
|
||||||
|
let registered = false;
|
||||||
const name = input.name;
|
const name = input.name;
|
||||||
let binding: BoundStylingEntry|null = null;
|
|
||||||
switch (input.type) {
|
switch (input.type) {
|
||||||
case BindingType.Property:
|
case BindingType.Property:
|
||||||
if (name == 'style') {
|
if (name == 'style') {
|
||||||
binding = this.registerStyleInput(null, input.value, '', input.sourceSpan);
|
this._styleMapInput = input;
|
||||||
} else if (isClassBinding(input.name)) {
|
this._useDefaultSanitizer = true;
|
||||||
binding = this.registerClassInput(null, input.value, input.sourceSpan);
|
registered = true;
|
||||||
|
} else if (isClassBinding(input)) {
|
||||||
|
this._classMapInput = input;
|
||||||
|
registered = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BindingType.Style:
|
case BindingType.Style:
|
||||||
binding = this.registerStyleInput(input.name, input.value, input.unit, input.sourceSpan);
|
(this._singleStyleInputs = this._singleStyleInputs || []).push(input);
|
||||||
|
this._useDefaultSanitizer = this._useDefaultSanitizer || isStyleSanitizable(name);
|
||||||
|
registerIntoMap(this._stylesIndex, name);
|
||||||
|
registered = true;
|
||||||
break;
|
break;
|
||||||
case BindingType.Class:
|
case BindingType.Class:
|
||||||
binding = this.registerClassInput(input.name, input.value, input.sourceSpan);
|
(this._singleClassInputs = this._singleClassInputs || []).push(input);
|
||||||
|
registerIntoMap(this._classesIndex, name);
|
||||||
|
registered = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return binding ? true : false;
|
if (registered) {
|
||||||
}
|
this._lastStylingInput = input;
|
||||||
|
|
||||||
registerStyleInput(
|
|
||||||
propertyName: string|null, value: AST, unit: string|null,
|
|
||||||
sourceSpan: ParseSourceSpan): BoundStylingEntry {
|
|
||||||
const entry = { name: propertyName, unit, value, sourceSpan } as BoundStylingEntry;
|
|
||||||
if (propertyName) {
|
|
||||||
(this._singleStyleInputs = this._singleStyleInputs || []).push(entry);
|
|
||||||
this._useDefaultSanitizer = this._useDefaultSanitizer || isStyleSanitizable(propertyName);
|
|
||||||
registerIntoMap(this._stylesIndex, propertyName);
|
|
||||||
(this as any).hasBindingsOrInitialValues = true;
|
(this as any).hasBindingsOrInitialValues = true;
|
||||||
} else {
|
this._applyFnRequired = true;
|
||||||
this._useDefaultSanitizer = true;
|
|
||||||
this._styleMapInput = entry;
|
|
||||||
}
|
}
|
||||||
this._lastStylingInput = entry;
|
return registered;
|
||||||
(this as any).hasBindingsOrInitialValues = true;
|
|
||||||
this._applyFnRequired = true;
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
registerClassInput(className: string|null, value: AST, sourceSpan: ParseSourceSpan):
|
|
||||||
BoundStylingEntry {
|
|
||||||
const entry = { name: className, value, sourceSpan } as BoundStylingEntry;
|
|
||||||
if (className) {
|
|
||||||
(this._singleClassInputs = this._singleClassInputs || []).push(entry);
|
|
||||||
(this as any).hasBindingsOrInitialValues = true;
|
|
||||||
registerIntoMap(this._classesIndex, className);
|
|
||||||
} else {
|
|
||||||
this._classMapInput = entry;
|
|
||||||
}
|
|
||||||
this._lastStylingInput = entry;
|
|
||||||
(this as any).hasBindingsOrInitialValues = true;
|
|
||||||
this._applyFnRequired = true;
|
|
||||||
return entry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerStyleAttr(value: string) {
|
registerStyleAttr(value: string) {
|
||||||
|
@ -187,7 +153,7 @@ export class StylingBuilder {
|
||||||
return exprs.length ? o.literalArr(exprs) : null;
|
return exprs.length ? o.literalArr(exprs) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
buildCreateLevelInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
|
buildCreateLevelInstruction(sourceSpan: ParseSourceSpan, constantPool: ConstantPool):
|
||||||
StylingInstruction|null {
|
StylingInstruction|null {
|
||||||
if (this.hasBindingsOrInitialValues) {
|
if (this.hasBindingsOrInitialValues) {
|
||||||
const initialClasses = this._buildInitExpr(this._classesIndex, this._initialClassValues);
|
const initialClasses = this._buildInitExpr(this._classesIndex, this._initialClassValues);
|
||||||
|
@ -222,11 +188,8 @@ export class StylingBuilder {
|
||||||
params.push(o.NULL_EXPR);
|
params.push(o.NULL_EXPR);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useSanitizer || this._directiveIndexExpr) {
|
if (useSanitizer) {
|
||||||
params.push(useSanitizer ? o.importExpr(R3.defaultStyleSanitizer) : o.NULL_EXPR);
|
params.push(o.importExpr(R3.defaultStyleSanitizer));
|
||||||
if (this._directiveIndexExpr) {
|
|
||||||
params.push(this._directiveIndexExpr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {sourceSpan, reference: R3.elementStyling, buildParams: () => params};
|
return {sourceSpan, reference: R3.elementStyling, buildParams: () => params};
|
||||||
|
@ -250,7 +213,7 @@ export class StylingBuilder {
|
||||||
sourceSpan: stylingInput.sourceSpan,
|
sourceSpan: stylingInput.sourceSpan,
|
||||||
reference: R3.elementStylingMap,
|
reference: R3.elementStylingMap,
|
||||||
buildParams: (convertFn: (value: any) => o.Expression) => {
|
buildParams: (convertFn: (value: any) => o.Expression) => {
|
||||||
const params: o.Expression[] = [this._elementIndexExpr];
|
const params: o.Expression[] = [this._indexLiteral];
|
||||||
|
|
||||||
if (mapBasedClassValue) {
|
if (mapBasedClassValue) {
|
||||||
params.push(convertFn(mapBasedClassValue));
|
params.push(convertFn(mapBasedClassValue));
|
||||||
|
@ -260,12 +223,6 @@ export class StylingBuilder {
|
||||||
|
|
||||||
if (mapBasedStyleValue) {
|
if (mapBasedStyleValue) {
|
||||||
params.push(convertFn(mapBasedStyleValue));
|
params.push(convertFn(mapBasedStyleValue));
|
||||||
} else if (this._directiveIndexExpr) {
|
|
||||||
params.push(o.NULL_EXPR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._directiveIndexExpr) {
|
|
||||||
params.push(this._directiveIndexExpr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
|
@ -276,8 +233,8 @@ export class StylingBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _buildSingleInputs(
|
private _buildSingleInputs(
|
||||||
reference: o.ExternalReference, inputs: BoundStylingEntry[], mapIndex: Map<string, number>,
|
reference: o.ExternalReference, inputs: t.BoundAttribute[], mapIndex: Map<string, number>,
|
||||||
allowUnits: boolean, valueConverter: ValueConverter): StylingInstruction[] {
|
valueConverter: ValueConverter): StylingInstruction[] {
|
||||||
return inputs.map(input => {
|
return inputs.map(input => {
|
||||||
const bindingIndex: number = mapIndex.get(input.name) !;
|
const bindingIndex: number = mapIndex.get(input.name) !;
|
||||||
const value = input.value.visit(valueConverter);
|
const value = input.value.visit(valueConverter);
|
||||||
|
@ -285,17 +242,9 @@ export class StylingBuilder {
|
||||||
sourceSpan: input.sourceSpan,
|
sourceSpan: input.sourceSpan,
|
||||||
reference,
|
reference,
|
||||||
buildParams: (convertFn: (value: any) => o.Expression) => {
|
buildParams: (convertFn: (value: any) => o.Expression) => {
|
||||||
const params = [this._elementIndexExpr, o.literal(bindingIndex), convertFn(value)];
|
const params = [this._indexLiteral, o.literal(bindingIndex), convertFn(value)];
|
||||||
if (allowUnits) {
|
if (input.unit != null) {
|
||||||
if (input.unit) {
|
params.push(o.literal(input.unit));
|
||||||
params.push(o.literal(input.unit));
|
|
||||||
} else if (this._directiveIndexExpr) {
|
|
||||||
params.push(o.NULL_EXPR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._directiveIndexExpr) {
|
|
||||||
params.push(this._directiveIndexExpr);
|
|
||||||
}
|
}
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
@ -306,7 +255,7 @@ export class StylingBuilder {
|
||||||
private _buildClassInputs(valueConverter: ValueConverter): StylingInstruction[] {
|
private _buildClassInputs(valueConverter: ValueConverter): StylingInstruction[] {
|
||||||
if (this._singleClassInputs) {
|
if (this._singleClassInputs) {
|
||||||
return this._buildSingleInputs(
|
return this._buildSingleInputs(
|
||||||
R3.elementClassProp, this._singleClassInputs, this._classesIndex, false, valueConverter);
|
R3.elementClassProp, this._singleClassInputs, this._classesIndex, valueConverter);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -314,7 +263,7 @@ export class StylingBuilder {
|
||||||
private _buildStyleInputs(valueConverter: ValueConverter): StylingInstruction[] {
|
private _buildStyleInputs(valueConverter: ValueConverter): StylingInstruction[] {
|
||||||
if (this._singleStyleInputs) {
|
if (this._singleStyleInputs) {
|
||||||
return this._buildSingleInputs(
|
return this._buildSingleInputs(
|
||||||
R3.elementStyleProp, this._singleStyleInputs, this._stylesIndex, true, valueConverter);
|
R3.elementStyleProp, this._singleStyleInputs, this._stylesIndex, valueConverter);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -323,13 +272,7 @@ export class StylingBuilder {
|
||||||
return {
|
return {
|
||||||
sourceSpan: this._lastStylingInput ? this._lastStylingInput.sourceSpan : null,
|
sourceSpan: this._lastStylingInput ? this._lastStylingInput.sourceSpan : null,
|
||||||
reference: R3.elementStylingApply,
|
reference: R3.elementStylingApply,
|
||||||
buildParams: () => {
|
buildParams: () => [this._indexLiteral]
|
||||||
const params: o.Expression[] = [this._elementIndexExpr];
|
|
||||||
if (this._directiveIndexExpr) {
|
|
||||||
params.push(this._directiveIndexExpr);
|
|
||||||
}
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,8 +293,8 @@ export class StylingBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isClassBinding(name: string): boolean {
|
function isClassBinding(input: t.BoundAttribute): boolean {
|
||||||
return name == 'className' || name == 'class';
|
return input.name == 'className' || input.name == 'class';
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerIntoMap(map: Map<string, number>, key: string) {
|
function registerIntoMap(map: Map<string, number>, key: string) {
|
||||||
|
|
|
@ -431,7 +431,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
|
|
||||||
visitElement(element: t.Element) {
|
visitElement(element: t.Element) {
|
||||||
const elementIndex = this.allocateDataSlot();
|
const elementIndex = this.allocateDataSlot();
|
||||||
const stylingBuilder = new StylingBuilder(o.literal(elementIndex), null);
|
const stylingBuilder = new StylingBuilder(elementIndex);
|
||||||
|
|
||||||
let isNonBindableMode: boolean = false;
|
let isNonBindableMode: boolean = false;
|
||||||
const isI18nRootElement: boolean = isI18nRootNode(element.i18n);
|
const isI18nRootElement: boolean = isI18nRootNode(element.i18n);
|
||||||
|
@ -476,7 +476,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
const allOtherInputs: t.BoundAttribute[] = [];
|
const allOtherInputs: t.BoundAttribute[] = [];
|
||||||
|
|
||||||
element.inputs.forEach((input: t.BoundAttribute) => {
|
element.inputs.forEach((input: t.BoundAttribute) => {
|
||||||
if (!stylingBuilder.registerBoundInput(input)) {
|
if (!stylingBuilder.registerInput(input)) {
|
||||||
if (input.type == BindingType.Property) {
|
if (input.type == BindingType.Property) {
|
||||||
if (input.i18n) {
|
if (input.i18n) {
|
||||||
i18nAttrs.push(input);
|
i18nAttrs.push(input);
|
||||||
|
|
|
@ -1072,11 +1072,9 @@ function generatePropertyAliases(
|
||||||
* @param className Name of class to toggle. Because it is going to DOM, this is not subject to
|
* @param className Name of class to toggle. Because it is going to DOM, this is not subject to
|
||||||
* renaming as part of minification.
|
* renaming as part of minification.
|
||||||
* @param value A value indicating if a given class should be added or removed.
|
* @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.
|
|
||||||
*/
|
*/
|
||||||
export function elementClassProp(
|
export function elementClassProp(
|
||||||
index: number, stylingIndex: number, value: boolean | PlayerFactory,
|
index: number, stylingIndex: number, value: boolean | PlayerFactory): void {
|
||||||
directiveIndex?: number): void {
|
|
||||||
const val =
|
const val =
|
||||||
(value instanceof BoundPlayerFactory) ? (value as BoundPlayerFactory<boolean>) : (!!value);
|
(value instanceof BoundPlayerFactory) ? (value as BoundPlayerFactory<boolean>) : (!!value);
|
||||||
updateElementClassProp(getStylingContext(index, getViewData()), stylingIndex, val);
|
updateElementClassProp(getStylingContext(index, getViewData()), stylingIndex, val);
|
||||||
|
@ -1109,12 +1107,11 @@ export function elementClassProp(
|
||||||
* values that are passed in here will be applied to the element (if matched).
|
* 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)
|
* @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).
|
* 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.
|
|
||||||
*/
|
*/
|
||||||
export function elementStyling(
|
export function elementStyling(
|
||||||
classDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
|
classDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
|
||||||
styleDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
|
styleDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
|
||||||
styleSanitizer?: StyleSanitizeFn | null, directiveIndex?: number): void {
|
styleSanitizer?: StyleSanitizeFn | null): void {
|
||||||
const tNode = getPreviousOrParentTNode();
|
const tNode = getPreviousOrParentTNode();
|
||||||
const inputData = initializeTNodeInputs(tNode);
|
const inputData = initializeTNodeInputs(tNode);
|
||||||
|
|
||||||
|
@ -1155,9 +1152,8 @@ export function elementStyling(
|
||||||
* (Note that this is not the element index, but rather an index value allocated
|
* (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
|
* specifically for element styling--the index must be the next index after the element
|
||||||
* index.)
|
* index.)
|
||||||
* @param directiveIndex the index for the directive that is attempting to change styling.
|
|
||||||
*/
|
*/
|
||||||
export function elementStylingApply(index: number, directiveIndex?: number): void {
|
export function elementStylingApply(index: number): void {
|
||||||
const viewData = getViewData();
|
const viewData = getViewData();
|
||||||
const isFirstRender = (viewData[FLAGS] & LViewFlags.CreationMode) !== 0;
|
const isFirstRender = (viewData[FLAGS] & LViewFlags.CreationMode) !== 0;
|
||||||
const totalPlayersQueued = renderStyleAndClassBindings(
|
const totalPlayersQueued = renderStyleAndClassBindings(
|
||||||
|
@ -1228,11 +1224,10 @@ export function elementStyleProp(
|
||||||
* @param styles A key/value style map of the styles that will be applied to the given element.
|
* @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
|
* Any missing styles (that have already been applied to the element beforehand) will be
|
||||||
* removed (unset) from the element's styling.
|
* removed (unset) from the element's styling.
|
||||||
* @param directiveIndex the index for the directive that is attempting to change styling.
|
|
||||||
*/
|
*/
|
||||||
export function elementStylingMap<T>(
|
export function elementStylingMap<T>(
|
||||||
index: number, classes: {[key: string]: any} | string | NO_CHANGE | null,
|
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): void {
|
||||||
const viewData = getViewData();
|
const viewData = getViewData();
|
||||||
const tNode = getTNode(index, viewData);
|
const tNode = getTNode(index, viewData);
|
||||||
const stylingContext = getStylingContext(index, viewData);
|
const stylingContext = getStylingContext(index, viewData);
|
||||||
|
|
Loading…
Reference in New Issue