refactor(ivy): break apart stylingMap into styleMap and classMap instructions (#30293)

This patch breaks up the existing `elementStylingMap` into
`elementClassMap` and `elementStyleMap` instructions. It also breaks
apart `hostStlyingMap` into `hostClassMap` and `hostStyleMap`
instructions. This change allows for better tree-shaking and reduces
the complexity of the styling algorithm code for `[style]` and `[class]`
bindings.

PR Close #30293
This commit is contained in:
Matias Niemelä 2019-05-06 17:44:03 -06:00 committed by Kara Erickson
parent 98a38ec98b
commit be8fbac942
18 changed files with 422 additions and 328 deletions

View File

@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be * Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Directive, DoCheck, Input, ɵRenderFlags, ɵɵdefineDirective, ɵɵelementHostStyling, ɵɵelementHostStylingApply, ɵɵelementHostStylingMap} from '@angular/core'; import {Directive, DoCheck, Input, ɵRenderFlags, ɵɵdefineDirective, ɵɵelementHostClassMap, ɵɵelementHostStyling, ɵɵelementHostStylingApply} from '@angular/core';
import {NgClassImpl, NgClassImplProvider} from './ng_class_impl'; import {NgClassImpl, NgClassImplProvider} from './ng_class_impl';
@ -38,7 +38,7 @@ export const ngClassDirectiveDef__POST_R3__ = ɵɵdefineDirective({
ɵɵelementHostStyling(); ɵɵelementHostStyling();
} }
if (rf & ɵRenderFlags.Update) { if (rf & ɵRenderFlags.Update) {
ɵɵelementHostStylingMap(ctx.getValue()); ɵɵelementHostClassMap(ctx.getValue());
ɵɵelementHostStylingApply(); ɵɵelementHostStylingApply();
} }
} }

View File

@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be * Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Directive, DoCheck, Input, ɵRenderFlags, ɵɵdefineDirective, ɵɵelementHostStyling, ɵɵelementHostStylingApply, ɵɵelementHostStylingMap} from '@angular/core'; import {Directive, DoCheck, Input, ɵRenderFlags, ɵɵdefineDirective, ɵɵelementHostStyleMap, ɵɵelementHostStyling, ɵɵelementHostStylingApply} from '@angular/core';
import {NgStyleImpl, NgStyleImplProvider} from './ng_style_impl'; import {NgStyleImpl, NgStyleImplProvider} from './ng_style_impl';
@ -38,7 +38,7 @@ export const ngStyleDirectiveDef__POST_R3__ = ɵɵdefineDirective({
ɵɵelementHostStyling(); ɵɵelementHostStyling();
} }
if (rf & ɵRenderFlags.Update) { if (rf & ɵRenderFlags.Update) {
ɵɵelementHostStylingMap(null, ctx.getValue()); ɵɵelementHostStyleMap(ctx.getValue());
ɵɵelementHostStylingApply(); ɵɵelementHostStylingApply();
} }
} }

View File

@ -389,7 +389,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd(); $r3$.ɵɵelementEnd();
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵelementStylingMap(0, null, $ctx$.myStyleExp); $r3$.ɵɵelementStyleMap(0, $ctx$.myStyleExp);
$r3$.ɵɵelementStylingApply(0); $r3$.ɵɵelementStylingApply(0);
} }
} }
@ -454,7 +454,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd(); $r3$.ɵɵelementEnd();
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵelementStylingMap(0, $r3$.ɵɵinterpolation1("foo foo-", $ctx$.fooId, "")); $r3$.ɵɵelementClassMap(0, $r3$.ɵɵinterpolation1("foo foo-", $ctx$.fooId, ""));
$r3$.ɵɵelementStylingApply(0); $r3$.ɵɵelementStylingApply(0);
} }
} }
@ -468,7 +468,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd(); $r3$.ɵɵelementEnd();
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵelementStylingMap(0, $r3$.ɵɵinterpolation2("foo foo-", $ctx$.fooId, "-", $ctx$.fooUsername, "")); $r3$.ɵɵelementClassMap(0, $r3$.ɵɵinterpolation2("foo foo-", $ctx$.fooId, "-", $ctx$.fooUsername, ""));
$r3$.ɵɵelementStylingApply(0); $r3$.ɵɵelementStylingApply(0);
} }
} }
@ -482,7 +482,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd(); $r3$.ɵɵelementEnd();
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵelementStylingMap(0, $ctx$.exp); $r3$.ɵɵelementClassMap(0, $ctx$.exp);
$r3$.ɵɵelementStylingApply(0); $r3$.ɵɵelementStylingApply(0);
} }
} }
@ -538,7 +538,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd(); $r3$.ɵɵelementEnd();
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵelementStylingMap(0, null, $ctx$.myStyleExp); $r3$.ɵɵelementStyleMap(0, $ctx$.myStyleExp);
$r3$.ɵɵelementStyleProp(0, 0, $ctx$.myWidth); $r3$.ɵɵelementStyleProp(0, 0, $ctx$.myWidth);
$r3$.ɵɵelementStyleProp(0, 1, $ctx$.myHeight); $r3$.ɵɵelementStyleProp(0, 1, $ctx$.myHeight);
$r3$.ɵɵelementStylingApply(0); $r3$.ɵɵelementStylingApply(0);
@ -704,7 +704,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd(); $r3$.ɵɵelementEnd();
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵelementStylingMap(0,$ctx$.myClassExp); $r3$.ɵɵelementClassMap(0,$ctx$.myClassExp);
$r3$.ɵɵelementStylingApply(0); $r3$.ɵɵelementStylingApply(0);
} }
} }
@ -760,7 +760,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd(); $r3$.ɵɵelementEnd();
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵelementStylingMap(0, $ctx$.myClassExp); $r3$.ɵɵelementClassMap(0, $ctx$.myClassExp);
$r3$.ɵɵelementClassProp(0, 0, $ctx$.yesToApple); $r3$.ɵɵelementClassProp(0, 0, $ctx$.yesToApple);
$r3$.ɵɵelementClassProp(0, 1, $ctx$.yesToOrange); $r3$.ɵɵelementClassProp(0, 1, $ctx$.yesToOrange);
$r3$.ɵɵelementStylingApply(0); $r3$.ɵɵelementStylingApply(0);
@ -882,7 +882,8 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd(); $r3$.ɵɵelementEnd();
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵelementStylingMap(0, $ctx$.myClassExp, $ctx$.myStyleExp); $r3$.ɵɵelementStyleMap(0, $ctx$.myStyleExp);
$r3$.ɵɵelementClassMap(0, $ctx$.myClassExp);
$r3$.ɵɵelementStylingApply(0); $r3$.ɵɵelementStylingApply(0);
} }
} }
@ -919,12 +920,13 @@ describe('compiler compliance: styling', () => {
if (rf & 1) { if (rf & 1) {
$r3$.ɵɵelementStart(0, "div"); $r3$.ɵɵelementStart(0, "div");
$r3$.ɵɵelementStyling(null, null, $r3$.ɵɵdefaultStyleSanitizer); $r3$.ɵɵelementStyling(null, null, $r3$.ɵɵdefaultStyleSanitizer);
$r3$.ɵɵpipe(1, "classPipe"); $r3$.ɵɵpipe(1, "stylePipe");
$r3$.ɵɵpipe(2, "stylePipe"); $r3$.ɵɵpipe(2, "classPipe");
$r3$.ɵɵelementEnd(); $r3$.ɵɵelementEnd();
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵelementStylingMap(0, $r3$.ɵɵpipeBind1(1, 0, $ctx$.myClassExp), $r3$.ɵɵpipeBind1(2, 2, $ctx$.myStyleExp)); $r3$.ɵɵelementStyleMap(0, $r3$.ɵɵpipeBind1(1, 0, $ctx$.myStyleExp));
$r3$.ɵɵelementClassMap(0, $r3$.ɵɵpipeBind1(2, 2, $ctx$.myClassExp));
$r3$.ɵɵelementStylingApply(0); $r3$.ɵɵelementStylingApply(0);
} }
} }
@ -980,7 +982,8 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd(); $r3$.ɵɵelementEnd();
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵelementStylingMap(0, $e2_styling$, $r3$.ɵɵpipeBind2(1, 1, $ctx$.myStyleExp, 1000)); $r3$.ɵɵelementStyleMap(0, $r3$.ɵɵpipeBind2(1, 1, $ctx$.myStyleExp, 1000));
$r3$.ɵɵelementClassMap(0, $e2_styling$);
$r3$.ɵɵelementStyleProp(0, 0, $r3$.ɵɵpipeBind2(2, 4, $ctx$.barExp, 3000)); $r3$.ɵɵelementStyleProp(0, 0, $r3$.ɵɵpipeBind2(2, 4, $ctx$.barExp, 3000));
$r3$.ɵɵelementStyleProp(0, 1, $r3$.ɵɵpipeBind2(3, 7, $ctx$.bazExp, 4000)); $r3$.ɵɵelementStyleProp(0, 1, $r3$.ɵɵpipeBind2(3, 7, $ctx$.bazExp, 4000));
$r3$.ɵɵelementClassProp(0, 0, $r3$.ɵɵpipeBind2(4, 10, $ctx$.fooExp, 2000)); $r3$.ɵɵelementClassProp(0, 0, $r3$.ɵɵpipeBind2(4, 10, $ctx$.fooExp, 2000));
@ -1042,7 +1045,8 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementHostStyling($e0_classBindings$, $e0_styleBindings$, $r3$.ɵɵdefaultStyleSanitizer); $r3$.ɵɵelementHostStyling($e0_classBindings$, $e0_styleBindings$, $r3$.ɵɵdefaultStyleSanitizer);
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵelementHostStylingMap(ctx.myClass, ctx.myStyle); $r3$.ɵɵelementHostStyleMap(ctx.myStyle);
$r3$.ɵɵelementHostClassMap(ctx.myClass);
$r3$.ɵɵelementHostStyleProp(0, ctx.myColorProp); $r3$.ɵɵelementHostStyleProp(0, ctx.myColorProp);
$r3$.ɵɵelementHostClassProp(0, ctx.myFooClass); $r3$.ɵɵelementHostClassProp(0, ctx.myFooClass);
$r3$.ɵɵelementHostStylingApply(); $r3$.ɵɵelementHostStylingApply();
@ -1102,7 +1106,8 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementHostStyling(_c0, _c1, $r3$.ɵɵdefaultStyleSanitizer); $r3$.ɵɵelementHostStyling(_c0, _c1, $r3$.ɵɵdefaultStyleSanitizer);
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵelementHostStylingMap(ctx.myClasses, ctx.myStyle); $r3$.ɵɵelementHostStyleMap(ctx.myStyle);
$r3$.ɵɵelementHostClassMap(ctx.myClasses);
$r3$.ɵɵelementHostStyleProp(0, ctx.myHeightProp, "pt"); $r3$.ɵɵelementHostStyleProp(0, ctx.myHeightProp, "pt");
$r3$.ɵɵelementHostStyleProp(1, ctx.myWidthProp); $r3$.ɵɵelementHostStyleProp(1, ctx.myWidthProp);
$r3$.ɵɵelementHostClassProp(0, ctx.myBarClass); $r3$.ɵɵelementHostClassProp(0, ctx.myBarClass);
@ -1166,7 +1171,8 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementEnd(); $r3$.ɵɵelementEnd();
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵelementStylingMap(0, ctx.myClassExp, ctx.myStyleExp); $r3$.ɵɵelementStyleMap(0, ctx.myStyleExp);
$r3$.ɵɵelementClassMap(0, ctx.myClassExp);
$r3$.ɵɵelementStyleProp(0, 0, ctx.myHeightExp, null, true); $r3$.ɵɵelementStyleProp(0, 0, ctx.myHeightExp, null, true);
$r3$.ɵɵelementClassProp(0, 0, ctx.myBarClassExp, true); $r3$.ɵɵelementClassProp(0, 0, ctx.myBarClassExp, true);
$r3$.ɵɵelementStylingApply(0); $r3$.ɵɵelementStylingApply(0);
@ -1183,7 +1189,8 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementHostStyling(_c0, _c1, $r3$.ɵɵdefaultStyleSanitizer); $r3$.ɵɵelementHostStyling(_c0, _c1, $r3$.ɵɵdefaultStyleSanitizer);
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵelementHostStylingMap(ctx.myClassExp, ctx.myStyleExp); $r3$.ɵɵelementHostStyleMap(ctx.myStyleExp);
$r3$.ɵɵelementHostClassMap(ctx.myClassExp);
$r3$.ɵɵelementHostStyleProp(0, ctx.myWidthExp, null, true); $r3$.ɵɵelementHostStyleProp(0, ctx.myWidthExp, null, true);
$r3$.ɵɵelementHostClassProp(0, ctx.myFooClassExp, true); $r3$.ɵɵelementHostClassProp(0, ctx.myFooClassExp, true);
$r3$.ɵɵelementHostStylingApply(); $r3$.ɵɵelementHostStylingApply();
@ -1251,7 +1258,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵelementHostStyling(); $r3$.ɵɵelementHostStyling();
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵelementHostStylingMap(ctx.myClassMap); $r3$.ɵɵelementHostClassMap(ctx.myClassMap);
$r3$.ɵɵelementHostStylingApply(); $r3$.ɵɵelementHostStylingApply();
} }
} }
@ -1335,7 +1342,8 @@ describe('compiler compliance: styling', () => {
if (rf & 2) { if (rf & 2) {
$r3$.ɵɵproperty("id", ctx.id, null, true); $r3$.ɵɵproperty("id", ctx.id, null, true);
$r3$.ɵɵproperty("title", ctx.title, null, true); $r3$.ɵɵproperty("title", ctx.title, null, true);
$r3$.ɵɵelementHostStylingMap(ctx.myClass, ctx.myStyle); $r3$.ɵɵelementHostStyleMap(ctx.myStyle);
$r3$.ɵɵelementHostClassMap(ctx.myClass);
$r3$.ɵɵelementHostStylingApply(); $r3$.ɵɵelementHostStylingApply();
} }
} }

View File

@ -51,7 +51,9 @@ export class Identifiers {
static elementStyling: o.ExternalReference = {name: 'ɵɵelementStyling', moduleName: CORE}; static elementStyling: o.ExternalReference = {name: 'ɵɵelementStyling', moduleName: CORE};
static elementStylingMap: o.ExternalReference = {name: 'ɵɵelementStylingMap', moduleName: CORE}; static elementStyleMap: o.ExternalReference = {name: 'ɵɵelementStyleMap', moduleName: CORE};
static elementClassMap: o.ExternalReference = {name: 'ɵɵelementClassMap', moduleName: CORE};
static elementStyleProp: o.ExternalReference = {name: 'ɵɵelementStyleProp', moduleName: CORE}; static elementStyleProp: o.ExternalReference = {name: 'ɵɵelementStyleProp', moduleName: CORE};
@ -62,8 +64,11 @@ export class Identifiers {
static elementHostStyling: o.ExternalReference = {name: 'ɵɵelementHostStyling', moduleName: CORE}; static elementHostStyling: o.ExternalReference = {name: 'ɵɵelementHostStyling', moduleName: CORE};
static elementHostStylingMap: static elementHostStyleMap:
o.ExternalReference = {name: 'ɵɵelementHostStylingMap', moduleName: CORE}; o.ExternalReference = {name: 'ɵɵelementHostStyleMap', moduleName: CORE};
static elementHostClassMap:
o.ExternalReference = {name: 'ɵɵelementHostClassMap', moduleName: CORE};
static elementHostStyleProp: static elementHostStyleProp:
o.ExternalReference = {name: 'ɵɵelementHostStyleProp', moduleName: CORE}; o.ExternalReference = {name: 'ɵɵelementHostStyleProp', moduleName: CORE};

View File

@ -61,7 +61,8 @@ interface BoundStylingEntry {
* elementStyling(...) * elementStyling(...)
* } * }
* if (updateMode) { * if (updateMode) {
* elementStylingMap(...) * elementStyleMap(...)
* elementClassMap(...)
* elementStyleProp(...) * elementStyleProp(...)
* elementClassProp(...) * elementClassProp(...)
* elementStylingApp(...) * elementStylingApp(...)
@ -339,73 +340,67 @@ export class StylingBuilder {
} }
/** /**
* Builds an instruction with all the expressions and parameters for `elementStylingMap`. * Builds an instruction with all the expressions and parameters for `elementClassMap`.
* *
* The instruction data will contain all expressions for `elementStylingMap` to function * The instruction data will contain all expressions for `elementClassMap` to function
* which include the `[style]` and `[class]` expression params (if they exist) as well as * which includes the `[class]` expression params.
* the sanitizer and directive reference expression.
*/ */
buildElementStylingMapInstruction(valueConverter: ValueConverter): Instruction|null { buildElementClassMapInstruction(valueConverter: ValueConverter): Instruction|null {
if (this._classMapInput || this._styleMapInput) { if (this._classMapInput) {
const stylingInput = this._classMapInput ! || this._styleMapInput !; return this._buildMapBasedInstruction(valueConverter, true, this._classMapInput);
let totalBindingSlotsRequired = 0;
// these values must be outside of the update block so that they can
// be evaluted (the AST visit call) during creation time so that any
// pipes can be picked up in time before the template is built
const mapBasedClassValue =
this._classMapInput ? this._classMapInput.value.visit(valueConverter) : null;
if (mapBasedClassValue instanceof Interpolation) {
totalBindingSlotsRequired += mapBasedClassValue.expressions.length;
}
const mapBasedStyleValue =
this._styleMapInput ? this._styleMapInput.value.visit(valueConverter) : null;
if (mapBasedStyleValue instanceof Interpolation) {
totalBindingSlotsRequired += mapBasedStyleValue.expressions.length;
}
const isHostBinding = this._directiveExpr;
const reference = isHostBinding ? R3.elementHostStylingMap : R3.elementStylingMap;
return {
sourceSpan: stylingInput.sourceSpan,
reference,
allocateBindingSlots: totalBindingSlotsRequired,
buildParams: (convertFn: (value: any) => o.Expression) => {
// HOST:
// min params => elementHostStylingMap(classMap)
// max params => elementHostStylingMap(classMap, styleMap)
// Template:
// min params => elementStylingMap(elmIndex, classMap)
// max params => elementStylingMap(elmIndex, classMap, styleMap)
const params: o.Expression[] = [];
if (!isHostBinding) {
params.push(this._elementIndexExpr);
}
let expectedNumberOfArgs = 0;
if (mapBasedStyleValue) {
expectedNumberOfArgs = 2;
} else if (mapBasedClassValue) {
// index and class = 2
expectedNumberOfArgs = 1;
}
addParam(
params, mapBasedClassValue, mapBasedClassValue ? convertFn(mapBasedClassValue) : null,
1, expectedNumberOfArgs);
addParam(
params, mapBasedStyleValue, mapBasedStyleValue ? convertFn(mapBasedStyleValue) : null,
2, expectedNumberOfArgs);
return params;
}
};
} }
return null; return null;
} }
/**
* Builds an instruction with all the expressions and parameters for `elementStyleMap`.
*
* The instruction data will contain all expressions for `elementStyleMap` to function
* which includes the `[style]` expression params.
*/
buildElementStyleMapInstruction(valueConverter: ValueConverter): Instruction|null {
if (this._styleMapInput) {
return this._buildMapBasedInstruction(valueConverter, false, this._styleMapInput);
}
return null;
}
private _buildMapBasedInstruction(
valueConverter: ValueConverter, isClassBased: boolean, stylingInput: BoundStylingEntry) {
let totalBindingSlotsRequired = 0;
// these values must be outside of the update block so that they can
// be evaluated (the AST visit call) during creation time so that any
// pipes can be picked up in time before the template is built
const mapValue = stylingInput.value.visit(valueConverter);
if (mapValue instanceof Interpolation) {
totalBindingSlotsRequired += mapValue.expressions.length;
}
const isHostBinding = this._directiveExpr;
let reference: o.ExternalReference;
if (isClassBased) {
reference = isHostBinding ? R3.elementHostClassMap : R3.elementClassMap;
} else {
reference = isHostBinding ? R3.elementHostStyleMap : R3.elementStyleMap;
}
return {
sourceSpan: stylingInput.sourceSpan,
reference,
allocateBindingSlots: totalBindingSlotsRequired,
buildParams: (convertFn: (value: any) => o.Expression) => {
const params: o.Expression[] = [];
if (!isHostBinding) {
params.push(this._elementIndexExpr);
}
params.push(convertFn(mapValue));
return params;
}
};
}
private _buildSingleInputs( private _buildSingleInputs(
reference: o.ExternalReference, isHostBinding: boolean, inputs: BoundStylingEntry[], reference: o.ExternalReference, isHostBinding: boolean, inputs: BoundStylingEntry[],
mapIndex: Map<string, number>, allowUnits: boolean, mapIndex: Map<string, number>, allowUnits: boolean,
@ -498,9 +493,13 @@ export class StylingBuilder {
buildUpdateLevelInstructions(valueConverter: ValueConverter) { buildUpdateLevelInstructions(valueConverter: ValueConverter) {
const instructions: Instruction[] = []; const instructions: Instruction[] = [];
if (this.hasBindings) { if (this.hasBindings) {
const mapInstruction = this.buildElementStylingMapInstruction(valueConverter); const styleMapInstruction = this.buildElementStyleMapInstruction(valueConverter);
if (mapInstruction) { if (styleMapInstruction) {
instructions.push(mapInstruction); instructions.push(styleMapInstruction);
}
const classMapInstruction = this.buildElementClassMapInstruction(valueConverter);
if (classMapInstruction) {
instructions.push(classMapInstruction);
} }
instructions.push(...this._buildStyleInputs(valueConverter)); instructions.push(...this._buildStyleInputs(valueConverter));
instructions.push(...this._buildClassInputs(valueConverter)); instructions.push(...this._buildClassInputs(valueConverter));

View File

@ -686,8 +686,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
// the code here will collect all update-level styling instructions and add them to the // the code here will collect all update-level styling instructions and add them to the
// update block of the template function AOT code. Instructions like `elementStyleProp`, // update block of the template function AOT code. Instructions like `elementStyleProp`,
// `elementStylingMap`, `elementClassProp` and `elementStylingApply` are all generated // `elementStyleMap`, `elementClassMap`, `elementClassProp` and `elementStylingApply`
// and assign in the code below. // are all generated and assigned in the code below.
stylingBuilder.buildUpdateLevelInstructions(this._valueConverter).forEach(instruction => { stylingBuilder.buildUpdateLevelInstructions(this._valueConverter).forEach(instruction => {
this._bindingSlots += instruction.allocateBindingSlots; this._bindingSlots += instruction.allocateBindingSlots;
this.processStylingInstruction(implicit, instruction, false); this.processStylingInstruction(implicit, instruction, false);

View File

@ -111,14 +111,16 @@ export {
ɵɵelementContainerStart, ɵɵelementContainerStart,
ɵɵelementContainerEnd, ɵɵelementContainerEnd,
ɵɵelementStyling, ɵɵelementStyling,
ɵɵelementStylingMap, ɵɵelementStyleMap,
ɵɵelementClassMap,
ɵɵelementStyleProp, ɵɵelementStyleProp,
ɵɵelementStylingApply, ɵɵelementStylingApply,
ɵɵelementClassProp, ɵɵelementClassProp,
ɵɵelementHostAttrs, ɵɵelementHostAttrs,
ɵɵelementHostClassMap,
ɵɵelementHostStyleMap,
ɵɵelementHostStyling, ɵɵelementHostStyling,
ɵɵelementHostStylingMap,
ɵɵelementHostStyleProp, ɵɵelementHostStyleProp,
ɵɵelementHostClassProp, ɵɵelementHostClassProp,
ɵɵelementHostStylingApply, ɵɵelementHostStylingApply,

View File

@ -34,6 +34,7 @@ export {
ɵɵelement, ɵɵelement,
ɵɵelementAttribute, ɵɵelementAttribute,
ɵɵelementClassMap,
ɵɵelementClassProp, ɵɵelementClassProp,
ɵɵelementContainerEnd, ɵɵelementContainerEnd,
@ -41,17 +42,18 @@ export {
ɵɵelementEnd, ɵɵelementEnd,
ɵɵelementHostAttrs, ɵɵelementHostAttrs,
ɵɵelementHostClassMap,
ɵɵelementHostClassProp, ɵɵelementHostClassProp,
ɵɵelementHostStyleMap,
ɵɵelementHostStyleProp, ɵɵelementHostStyleProp,
ɵɵelementHostStyling, ɵɵelementHostStyling,
ɵɵelementHostStylingApply, ɵɵelementHostStylingApply,
ɵɵelementHostStylingMap,
ɵɵelementProperty, ɵɵelementProperty,
ɵɵelementStart, ɵɵelementStart,
ɵɵelementStyleMap,
ɵɵelementStyleProp, ɵɵelementStyleProp,
ɵɵelementStyling, ɵɵelementStyling,
ɵɵelementStylingApply, ɵɵelementStylingApply,
ɵɵelementStylingMap,
ɵɵembeddedViewEnd, ɵɵembeddedViewEnd,
ɵɵembeddedViewStart, ɵɵembeddedViewStart,

View File

@ -11,7 +11,7 @@ import {TNode, TNodeType} from '../interfaces/node';
import {PlayerFactory} from '../interfaces/player'; import {PlayerFactory} from '../interfaces/player';
import {FLAGS, HEADER_OFFSET, LView, LViewFlags, RENDERER, RootContextFlags} from '../interfaces/view'; import {FLAGS, HEADER_OFFSET, LView, LViewFlags, RENDERER, RootContextFlags} from '../interfaces/view';
import {getActiveDirectiveId, getActiveDirectiveSuperClassDepth, getLView, getPreviousOrParentTNode, getSelectedIndex} from '../state'; import {getActiveDirectiveId, getActiveDirectiveSuperClassDepth, getLView, getPreviousOrParentTNode, getSelectedIndex} from '../state';
import {getInitialClassNameValue, renderStyling, updateClassProp as updateElementClassProp, updateContextWithBindings, updateStyleProp as updateElementStyleProp, updateStylingMap} from '../styling/class_and_style_bindings'; import {getInitialClassNameValue, renderStyling, updateClassMap, updateClassProp as updateElementClassProp, updateContextWithBindings, updateStyleMap, updateStyleProp as updateElementStyleProp} from '../styling/class_and_style_bindings';
import {ParamsOf, enqueueHostInstruction, registerHostDirective} from '../styling/host_instructions_queue'; import {ParamsOf, enqueueHostInstruction, registerHostDirective} from '../styling/host_instructions_queue';
import {BoundPlayerFactory} from '../styling/player_factory'; import {BoundPlayerFactory} from '../styling/player_factory';
import {DEFAULT_TEMPLATE_DIRECTIVE_INDEX} from '../styling/shared'; import {DEFAULT_TEMPLATE_DIRECTIVE_INDEX} from '../styling/shared';
@ -34,14 +34,16 @@ import {scheduleTick, setInputsForProperty} from './shared';
* *
* Template level styling instructions: * Template level styling instructions:
* - elementStyling * - elementStyling
* - elementStylingMap * - elementStyleMap
* - elementClassMap
* - elementStyleProp * - elementStyleProp
* - elementClassProp * - elementClassProp
* - elementStylingApply * - elementStylingApply
* *
* Host bindings level styling instructions: * Host bindings level styling instructions:
* - elementHostStyling * - elementHostStyling
* - elementHostStylingMap * - elementHostStyleMap
* - elementHostClassMap
* - elementHostStyleProp * - elementHostStyleProp
* - elementHostClassProp * - elementHostClassProp
* - elementHostStylingApply * - elementHostStylingApply
@ -147,7 +149,7 @@ function initElementStyling(
* *
* If the style value is falsy then it will be removed from the element * If the style value is falsy then it will be removed from the element
* (or assigned a different value depending if there are any styles placed * (or assigned a different value depending if there are any styles placed
* on the element with `elementStylingMap` or any static styles that are * on the element with `elementStyleMap` or any static styles that are
* present from when the element was created with `elementStyling`). * present from when the element was created with `elementStyling`).
* *
* Note that the styling element is updated as part of `elementStylingApply`. * Note that the styling element is updated as part of `elementStylingApply`.
@ -182,7 +184,7 @@ export function ɵɵelementStyleProp(
* *
* If the style value is falsy then it will be removed from the host element * If the style value is falsy then it will be removed from the host element
* (or assigned a different value depending if there are any styles placed * (or assigned a different value depending if there are any styles placed
* on the same element with `elementHostStylingMap` or any static styles that * on the same element with `elementHostStyleMap` or any static styles that
* are present from when the element was patched with `elementHostStyling`). * are present from when the element was patched with `elementHostStyling`).
* *
* Note that the styling applied to the host element once * Note that the styling applied to the host element once
@ -305,12 +307,50 @@ function booleanOrNull(value: any): boolean|null {
/** /**
* Update style and/or class bindings using object literals on an element. * Update style bindings using an object literal on an element.
* *
* This instruction is meant to apply styling via the `[style]="exp"` and `[class]="exp"` template * This instruction is meant to apply styling via the `[style]="exp"` template bindings.
* bindings. When styles/classes are applied to the element they will then be updated with * When styles are applied to the element they will then be updated with respect to
* respect to any styles/classes set with `elementStyleProp` or `elementClassProp`. If any * any styles/classes set via `elementStyleProp`. If any styles are set to falsy
* styles or classes are set to falsy then they will be removed from the element. * then they will be removed from the element.
*
* Note that the styling instruction will not be applied until `elementStylingApply` is called.
*
* @param index Index of the element's with which styling is associated.
* @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.
*
* @codeGenApi
*/
export function ɵɵelementStyleMap(
index: number, styles: {[styleName: string]: any} | NO_CHANGE | null): void {
const lView = getLView();
const stylingContext = getStylingContext(index, lView);
const tNode = getTNode(index, lView);
// inputs are only evaluated from a template binding into a directive, therefore,
// there should not be a situation where a directive host bindings function
// evaluates the inputs (this should only happen in the template function)
if (hasStyleInput(tNode) && styles !== NO_CHANGE) {
const initialStyles = getInitialClassNameValue(stylingContext);
const styleInputVal =
(initialStyles.length ? (initialStyles + ' ') : '') + forceStylesAsString(styles);
setInputsForProperty(lView, tNode.inputs !['style'] !, styleInputVal);
styles = NO_CHANGE;
}
updateStyleMap(stylingContext, styles);
}
/**
* Update class bindings using an object literal or class-string on an element.
*
* This instruction is meant to apply styling via the `[class]="exp"` template bindings.
* When classes are applied to the element they will then be updated with
* respect to any styles/classes set via `elementClassProp`. If any
* classes are set to falsy then they will be removed from the element.
* *
* Note that the styling instruction will not be applied until `elementStylingApply` is called. * Note that the styling instruction will not be applied until `elementStylingApply` is called.
* *
@ -318,19 +358,14 @@ function booleanOrNull(value: any): boolean|null {
* @param classes A key/value map or string of CSS classes that will be added to the * @param classes A key/value map or string of CSS classes that will be added to the
* given element. Any missing classes (that have already been applied to the element * given element. Any missing classes (that have already been applied to the element
* beforehand) will be removed (unset) from the element's list of CSS classes. * beforehand) will be removed (unset) from the element's list of CSS classes.
* @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.
* *
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵelementStylingMap( export function ɵɵelementClassMap(
index: number, classes: {[key: string]: any} | string | NO_CHANGE | null, index: number, classes: {[styleName: string]: any} | NO_CHANGE | string | null): void {
styles?: {[styleName: string]: any} | NO_CHANGE | null): void {
const lView = getLView(); const lView = getLView();
const stylingContext = getStylingContext(index, lView); const stylingContext = getStylingContext(index, lView);
const tNode = getTNode(index, lView); const tNode = getTNode(index, lView);
// inputs are only evaluated from a template binding into a directive, therefore, // inputs are only evaluated from a template binding into a directive, therefore,
// there should not be a situation where a directive host bindings function // there should not be a situation where a directive host bindings function
// evaluates the inputs (this should only happen in the template function) // evaluates the inputs (this should only happen in the template function)
@ -341,29 +376,46 @@ export function ɵɵelementStylingMap(
setInputsForProperty(lView, tNode.inputs !['class'] !, classInputVal); setInputsForProperty(lView, tNode.inputs !['class'] !, classInputVal);
classes = NO_CHANGE; classes = NO_CHANGE;
} }
updateClassMap(stylingContext, classes);
if (hasStyleInput(tNode) && styles !== NO_CHANGE) {
const initialStyles = getInitialClassNameValue(stylingContext);
const styleInputVal =
(initialStyles.length ? (initialStyles + ' ') : '') + forceStylesAsString(styles);
setInputsForProperty(lView, tNode.inputs !['style'] !, styleInputVal);
styles = NO_CHANGE;
}
updateStylingMap(stylingContext, classes, styles);
} }
/** /**
* Update style and/or class host bindings using object literals on an element within the host * Update style host bindings using object literals on an element within the host
* bindings function for a directive/component. * bindings function for a directive/component.
* *
* This instruction is meant to apply styling via the `@HostBinding('style')` and * This instruction is meant to apply styling via the `@HostBinding('style')`
* `@HostBinding('class')` bindings for a component's or directive's host element. * host bindings for a component's or directive's host element.
* When styles/classes are applied to the host element they will then be updated * When styles are applied to the host element they will then be updated
* with respect to any styles/classes set with `elementHostStyleProp` or * with respect to any other styles set with `elementHostStyleProp`. If
* `elementHostClassProp`. If any styles or classes are set to falsy then they * If any styles are set to falsy then they will be removed from the element.
* will be removed from the element. *
* Note that the styling instruction will not be applied until
* `elementHostStylingApply` is called.
*
* @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.
*
* @codeGenApi
*/
export function ɵɵelementHostStyleMap(styles: {[styleName: string]: any} | NO_CHANGE | null): void {
const directiveStylingIndex = getActiveDirectiveStylingIndex();
const hostElementIndex = getSelectedIndex();
const stylingContext = getStylingContext(hostElementIndex, getLView());
const args: ParamsOf<typeof updateStyleMap> = [stylingContext, styles, directiveStylingIndex];
enqueueHostInstruction(stylingContext, directiveStylingIndex, updateStyleMap, args);
}
/**
* Update class host bindings using object literals on an element within the host
* bindings function for a directive/component.
*
* This instruction is meant to apply styling via the `@HostBinding('class')`
* host bindings for a component's or directive's host element.
* When classes are applied to the host element they will then be updated
* with respect to any other classes set with `elementHostClassProp`. If
* any classes are set to falsy then they will be removed from the element.
* *
* Note that the styling instruction will not be applied until * Note that the styling instruction will not be applied until
* `elementHostStylingApply` is called. * `elementHostStylingApply` is called.
@ -371,30 +423,24 @@ export function ɵɵelementStylingMap(
* @param classes A key/value map or string of CSS classes that will be added to the * @param classes A key/value map or string of CSS classes that will be added to the
* given element. Any missing classes (that have already been applied to the element * given element. Any missing classes (that have already been applied to the element
* beforehand) will be removed (unset) from the element's list of CSS classes. * beforehand) will be removed (unset) from the element's list of CSS classes.
* @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.
* *
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵelementHostStylingMap( export function ɵɵelementHostClassMap(classes: {[key: string]: any} | string | NO_CHANGE | null):
classes: {[key: string]: any} | string | NO_CHANGE | null, void {
styles?: {[styleName: string]: any} | NO_CHANGE | null): void {
const directiveStylingIndex = getActiveDirectiveStylingIndex(); const directiveStylingIndex = getActiveDirectiveStylingIndex();
const hostElementIndex = getSelectedIndex(); const hostElementIndex = getSelectedIndex();
const stylingContext = getStylingContext(hostElementIndex, getLView()); const stylingContext = getStylingContext(hostElementIndex, getLView());
const args: ParamsOf<typeof updateStylingMap> = const args: ParamsOf<typeof updateClassMap> = [stylingContext, classes, directiveStylingIndex];
[stylingContext, classes, styles, directiveStylingIndex]; enqueueHostInstruction(stylingContext, directiveStylingIndex, updateClassMap, args);
enqueueHostInstruction(stylingContext, directiveStylingIndex, updateStylingMap, args);
} }
/** /**
* Apply all style and class binding values to the element. * Apply all style and class binding values to the element.
* *
* This instruction is meant to be run after `elementStylingMap`, `elementStyleProp` * This instruction is meant to be run after `elementStyleMap`, `elementClassMap`,
* or `elementClassProp` instructions have been run and will only apply styling to * `elementStyleProp` or `elementClassProp` instructions have been run and will
* the element if any styling bindings have been updated. * only apply styling to the element if any styling bindings have been updated.
* *
* @param index Index of the element's with which styling is associated. * @param index Index of the element's with which styling is associated.
* *
@ -407,10 +453,10 @@ export function ɵɵelementStylingApply(index: number): void {
/** /**
* Apply all style and class host binding values to the element. * Apply all style and class host binding values to the element.
* *
* This instruction is meant to be run after `elementHostStylingMap`, * This instruction is meant to be run after both `elementHostStyleMap`
* `elementHostStyleProp` or `elementHostClassProp` instructions have * `elementHostClassMap`, `elementHostStyleProp` or `elementHostClassProp`
* been run and will only apply styling to the host element if any * instructions have been run and will only apply styling to the host
* styling bindings have been updated. * element if any styling bindings have been updated.
* *
* @codeGenApi * @codeGenApi
*/ */

View File

@ -122,8 +122,8 @@ import {LView} from './view';
* values are and how they work. * values are and how they work.
* *
* Each time a binding property is updated (whether it be through a single * Each time a binding property is updated (whether it be through a single
* property instruction like `elementStyleProp`, `elementClassProp` or * property instruction like `elementStyleProp`, `elementClassProp`,
* `elementStylingMap`) then the values in the context will be updated as * `elementStyleMap` or `elementClassMap`) then the values in the context will be updated as
* well. * well.
* *
* If for example `[style.width]` updates to `555px` then its value will be reflected * If for example `[style.width]` updates to `555px` then its value will be reflected
@ -161,7 +161,8 @@ import {LView} from './view';
* - `elementStyling` * - `elementStyling`
* - `elementStyleProp` * - `elementStyleProp`
* - `elementClassProp` * - `elementClassProp`
* - `elementStylingMap` * - `elementStyleMap`
* - `elementClassMap`
* - `elementStylingApply` * - `elementStylingApply`
* *
* Each time a directive value is passed in, it will be converted into an index by examining the * Each time a directive value is passed in, it will be converted into an index by examining the
@ -298,13 +299,13 @@ export interface StylingContext extends
[StylingIndex.SinglePropOffsetPositions]: SinglePropOffsetValues; [StylingIndex.SinglePropOffsetPositions]: SinglePropOffsetValues;
/** /**
* The last class value that was interpreted by elementStylingMap. This is cached * The last class value that was interpreted by `elementStyleMap`. This is cached
* So that the algorithm can exit early incase the value has not changed. * So that the algorithm can exit early incase the value has not changed.
*/ */
[StylingIndex.CachedMultiClasses]: any|MapBasedOffsetValues; [StylingIndex.CachedMultiClasses]: any|MapBasedOffsetValues;
/** /**
* The last style value that was interpreted by elementStylingMap. This is cached * The last style value that was interpreted by `elementClassMap`. This is cached
* So that the algorithm can exit early incase the value has not changed. * So that the algorithm can exit early incase the value has not changed.
*/ */
[StylingIndex.CachedMultiStyles]: any|MapBasedOffsetValues; [StylingIndex.CachedMultiStyles]: any|MapBasedOffsetValues;
@ -313,7 +314,7 @@ export interface StylingContext extends
* A queue of all hostStyling instructions. * A queue of all hostStyling instructions.
* *
* This array (queue) is populated only when host-level styling instructions * This array (queue) is populated only when host-level styling instructions
* (e.g. `hostStylingMap` and `hostClassProp`) are used to apply style and * (e.g. `hostStyleMap` and `hostClassProp`) are used to apply style and
* class values via host bindings to the host element. Despite these being * class values via host bindings to the host element. Despite these being
* standard angular instructions, they are not designed to immediately apply * standard angular instructions, they are not designed to immediately apply
* their values to the styling context when executed. What happens instead is * their values to the styling context when executed. What happens instead is
@ -336,7 +337,7 @@ export interface StylingContext extends
* the styling is applied). * the styling is applied).
* *
* This queue is used when any `hostStyling` instructions are executed from the `hostBindings` * This queue is used when any `hostStyling` instructions are executed from the `hostBindings`
* function. Template-level styling functions (e.g. `elementStylingMap` and `elementClassProp`) * function. Template-level styling functions (e.g. `elementStyleMap` and `elementClassProp`)
* do not make use of this queue (they are applied to the styling context immediately). * do not make use of this queue (they are applied to the styling context immediately).
* *
* Due to the nature of how components/directives are evaluated, directives (both parent and * Due to the nature of how components/directives are evaluated, directives (both parent and
@ -781,7 +782,7 @@ export const enum DirectiveOwnerAndPlayerBuilderIndex {
/** /**
* The default directive styling index value for template-based bindings. * The default directive styling index value for template-based bindings.
* *
* All host-level bindings (e.g. `hostStyleProp` and `hostStylingMap`) are * All host-level bindings (e.g. `hostStyleProp` and `hostClassMap`) are
* assigned a directive styling index value based on the current directive * assigned a directive styling index value based on the current directive
* uniqueId and the directive super-class inheritance depth. But for template * uniqueId and the directive super-class inheritance depth. But for template
* bindings they always have the same directive styling index value. * bindings they always have the same directive styling index value.

View File

@ -106,13 +106,15 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵɵloadContentQuery': r3.ɵɵloadContentQuery, 'ɵɵloadContentQuery': r3.ɵɵloadContentQuery,
'ɵɵreference': r3.ɵɵreference, 'ɵɵreference': r3.ɵɵreference,
'ɵɵelementHostAttrs': r3.ɵɵelementHostAttrs, 'ɵɵelementHostAttrs': r3.ɵɵelementHostAttrs,
'ɵɵelementClassMap': r3.ɵɵelementClassMap,
'ɵɵelementStyling': r3.ɵɵelementStyling, 'ɵɵelementStyling': r3.ɵɵelementStyling,
'ɵɵelementStylingMap': r3.ɵɵelementStylingMap, 'ɵɵelementStyleMap': r3.ɵɵelementStyleMap,
'ɵɵelementStyleProp': r3.ɵɵelementStyleProp, 'ɵɵelementStyleProp': r3.ɵɵelementStyleProp,
'ɵɵelementStylingApply': r3.ɵɵelementStylingApply, 'ɵɵelementStylingApply': r3.ɵɵelementStylingApply,
'ɵɵelementClassProp': r3.ɵɵelementClassProp, 'ɵɵelementClassProp': r3.ɵɵelementClassProp,
'ɵɵelementHostClassMap': r3.ɵɵelementHostClassMap,
'ɵɵelementHostStyling': r3.ɵɵelementHostStyling, 'ɵɵelementHostStyling': r3.ɵɵelementHostStyling,
'ɵɵelementHostStylingMap': r3.ɵɵelementHostStylingMap, 'ɵɵelementHostStyleMap': r3.ɵɵelementHostStyleMap,
'ɵɵelementHostStyleProp': r3.ɵɵelementHostStyleProp, 'ɵɵelementHostStyleProp': r3.ɵɵelementHostStyleProp,
'ɵɵelementHostStylingApply': r3.ɵɵelementHostStylingApply, 'ɵɵelementHostStylingApply': r3.ɵɵelementHostStylingApply,
'ɵɵelementHostClassProp': r3.ɵɵelementHostClassProp, 'ɵɵelementHostClassProp': r3.ɵɵelementHostClassProp,

View File

@ -486,17 +486,16 @@ function getMatchingBindingIndex(
} }
/** /**
* Registers the provided multi styling (`[style]` and `[class]`) values to the context. * Registers the provided multi class values to the context.
* *
* This function will iterate over the provided `classesInput` and `stylesInput` map * This function will iterate over the provided `classesInput` values and
* values and insert/update or remove them from the context at exactly the right * insert/update or remove them from the context at exactly the right spot.
* spot.
* *
* This function also takes in a directive which implies that the styling values will * This function also takes in a directive which implies that the styling values will
* be evaluated for that directive with respect to any other styling that already exists * be evaluated for that directive with respect to any other styling that already exists
* on the context. When there are styles that conflict (e.g. say `ngStyle` and `[style]` * on the context. When there are styles that conflict (e.g. say `ngClass` and `[class]`
* both update the `width` property at the same time) then the styling algorithm code below * both update the `foo` className value at the same time) then the styling algorithm code below
* will decide which one wins based on the directive styling prioritization mechanism. This * will decide which one wins based on the directive styling prioritization mechanism. (This
* mechanism is better explained in render3/interfaces/styling.ts#directives). * mechanism is better explained in render3/interfaces/styling.ts#directives).
* *
* This function will not render any styling values on screen, but is rather designed to * This function will not render any styling values on screen, but is rather designed to
@ -509,100 +508,108 @@ function getMatchingBindingIndex(
* @param classesInput The key/value map of CSS class names that will be used for the update. * @param classesInput The key/value map of CSS class names that will be used for the update.
* @param stylesInput The key/value map of CSS styles that will be used for the update. * @param stylesInput The key/value map of CSS styles that will be used for the update.
*/ */
export function updateStylingMap( export function updateClassMap(
context: StylingContext, classesInput: {[key: string]: any} | string | context: StylingContext, classesInput: {[key: string]: any} | string |
BoundPlayerFactory<null|string|{[key: string]: any}>| null, BoundPlayerFactory<null|string|{[key: string]: any}>| null,
stylesInput?: {[key: string]: any} | BoundPlayerFactory<null|{[key: string]: any}>| null,
directiveIndex: number = 0): void { directiveIndex: number = 0): void {
ngDevMode && ngDevMode.stylingMap++; updateStylingMap(context, classesInput, true, directiveIndex);
}
/**
* Registers the provided multi style values to the context.
*
* This function will iterate over the provided `stylesInput` values and
* insert/update or remove them from the context at exactly the right spot.
*
* This function also takes in a directive which implies that the styling values will
* be evaluated for that directive with respect to any other styling that already exists
* on the context. When there are styles that conflict (e.g. say `ngStyle` and `[style]`
* both update the `width` property at the same time) then the styling algorithm code below
* will decide which one wins based on the directive styling prioritization mechanism. (This
* mechanism is better explained in render3/interfaces/styling.ts#directives).
*
* This function will not render any styling values on screen, but is rather designed to
* prepare the context for that. `renderStyling` must be called afterwards to render any
* styling data that was set in this function (note that `updateClassProp` and
* `updateStyleProp` are designed to be run after this function is run).
*
* @param context The styling context that will be updated with the
* newly provided style values.
* @param stylesInput The key/value map of CSS styles that will be used for the update.
*/
export function updateStyleMap(
context: StylingContext, stylesInput: {[key: string]: any} | string |
BoundPlayerFactory<null|string|{[key: string]: any}>| null,
directiveIndex: number = 0): void {
updateStylingMap(context, stylesInput, false, directiveIndex);
}
function updateStylingMap(
context: StylingContext, input: {[key: string]: any} | string |
BoundPlayerFactory<null|string|{[key: string]: any}>| null,
entryIsClassBased: boolean, directiveIndex: number = 0): void {
ngDevMode && (entryIsClassBased ? ngDevMode.classMap++ : ngDevMode.styleMap++);
ngDevMode && assertValidDirectiveIndex(context, directiveIndex); ngDevMode && assertValidDirectiveIndex(context, directiveIndex);
classesInput = classesInput || null;
stylesInput = stylesInput || null;
const ignoreAllClassUpdates = isMultiValueCacheHit(context, true, directiveIndex, classesInput);
const ignoreAllStyleUpdates = isMultiValueCacheHit(context, false, directiveIndex, stylesInput);
// early exit (this is what's done to avoid using ctx.bind() to cache the value) // early exit (this is what's done to avoid using ctx.bind() to cache the value)
if (ignoreAllClassUpdates && ignoreAllStyleUpdates) return; if (isMultiValueCacheHit(context, entryIsClassBased, directiveIndex, input)) return;
classesInput = input =
classesInput === NO_CHANGE ? readCachedMapValue(context, true, directiveIndex) : classesInput; input === NO_CHANGE ? readCachedMapValue(context, entryIsClassBased, directiveIndex) : input;
stylesInput =
stylesInput === NO_CHANGE ? readCachedMapValue(context, false, directiveIndex) : stylesInput;
const element = context[StylingIndex.ElementPosition] !as HTMLElement; const element = context[StylingIndex.ElementPosition] !as HTMLElement;
const classesPlayerBuilder = classesInput instanceof BoundPlayerFactory ? const playerBuilder = input instanceof BoundPlayerFactory ?
new ClassAndStylePlayerBuilder(classesInput as any, element, BindingType.Class) : new ClassAndStylePlayerBuilder(
null; input as any, element, entryIsClassBased ? BindingType.Class : BindingType.Style) :
const stylesPlayerBuilder = stylesInput instanceof BoundPlayerFactory ?
new ClassAndStylePlayerBuilder(stylesInput as any, element, BindingType.Style) :
null; null;
const classesValue = classesPlayerBuilder ? const rawValue =
(classesInput as BoundPlayerFactory<{[key: string]: any}|string>) !.value : playerBuilder ? (input as BoundPlayerFactory<{[key: string]: any}|string>) !.value : input;
classesInput;
const stylesValue = stylesPlayerBuilder ? stylesInput !['value'] : stylesInput;
let classNames: string[] = EMPTY_ARRAY; // the position is always the same, but whether the player builder gets set
let applyAllClasses = false; // at all (depending if its set) will be reflected in the index value below...
const playerBuilderPosition = entryIsClassBased ? PlayerIndex.ClassMapPlayerBuilderPosition :
PlayerIndex.StyleMapPlayerBuilderPosition;
let playerBuilderIndex = playerBuilder ? playerBuilderPosition : 0;
let playerBuildersAreDirty = false; let playerBuildersAreDirty = false;
if (hasPlayerBuilderChanged(context, playerBuilder, playerBuilderPosition)) {
const classesPlayerBuilderIndex = setPlayerBuilder(context, playerBuilder, playerBuilderPosition);
classesPlayerBuilder ? PlayerIndex.ClassMapPlayerBuilderPosition : 0;
if (hasPlayerBuilderChanged(
context, classesPlayerBuilder, PlayerIndex.ClassMapPlayerBuilderPosition)) {
setPlayerBuilder(context, classesPlayerBuilder, PlayerIndex.ClassMapPlayerBuilderPosition);
playerBuildersAreDirty = true;
}
const stylesPlayerBuilderIndex =
stylesPlayerBuilder ? PlayerIndex.StyleMapPlayerBuilderPosition : 0;
if (hasPlayerBuilderChanged(
context, stylesPlayerBuilder, PlayerIndex.StyleMapPlayerBuilderPosition)) {
setPlayerBuilder(context, stylesPlayerBuilder, PlayerIndex.StyleMapPlayerBuilderPosition);
playerBuildersAreDirty = true; playerBuildersAreDirty = true;
} }
// each time a string-based value pops up then it shouldn't require a deep // each time a string-based value pops up then it shouldn't require a deep
// check of what's changed. // check of what's changed.
if (!ignoreAllClassUpdates) { let startIndex: number;
if (typeof classesValue == 'string') { let endIndex: number;
classNames = classesValue.split(/\s+/); let propNames: string[];
let applyAll = false;
if (entryIsClassBased) {
if (typeof rawValue == 'string') {
propNames = rawValue.split(/\s+/);
// this boolean is used to avoid having to create a key/value map of `true` values // this boolean is used to avoid having to create a key/value map of `true` values
// since a classname string implies that all those classes are added // since a className string implies that all those classes are added
applyAllClasses = true; applyAll = true;
} else { } else {
classNames = classesValue ? Object.keys(classesValue) : EMPTY_ARRAY; propNames = rawValue ? Object.keys(rawValue) : EMPTY_ARRAY;
} }
startIndex = getMultiClassesStartIndex(context);
endIndex = context.length;
} else {
startIndex = getMultiStylesStartIndex(context);
endIndex = getMultiClassesStartIndex(context);
propNames = rawValue ? Object.keys(rawValue) : EMPTY_ARRAY;
} }
const multiStylesStartIndex = getMultiStylesStartIndex(context); const values = (rawValue || EMPTY_OBJ) as{[key: string]: any};
let multiClassesStartIndex = getMultiClassesStartIndex(context); patchStylingMapIntoContext(
let multiClassesEndIndex = context.length; context, directiveIndex, playerBuilderIndex, startIndex, endIndex, propNames,
applyAll || values, input, entryIsClassBased);
if (!ignoreAllStyleUpdates) {
const styleProps = stylesValue ? Object.keys(stylesValue) : EMPTY_ARRAY;
const styles = stylesValue || EMPTY_OBJ;
const totalNewEntries = patchStylingMapIntoContext(
context, directiveIndex, stylesPlayerBuilderIndex, multiStylesStartIndex,
multiClassesStartIndex, styleProps, styles, stylesInput, false);
if (totalNewEntries) {
multiClassesStartIndex += totalNewEntries * StylingIndex.Size;
multiClassesEndIndex += totalNewEntries * StylingIndex.Size;
}
}
if (!ignoreAllClassUpdates) {
const classes = (classesValue || EMPTY_OBJ) as{[key: string]: any};
patchStylingMapIntoContext(
context, directiveIndex, classesPlayerBuilderIndex, multiClassesStartIndex,
multiClassesEndIndex, classNames, applyAllClasses || classes, classesInput, true);
}
if (playerBuildersAreDirty) { if (playerBuildersAreDirty) {
setContextPlayersDirty(context, true); setContextPlayersDirty(context, true);
} }
ngDevMode && ngDevMode.stylingMapCacheMiss++; ngDevMode && (entryIsClassBased ? ngDevMode.classMapCacheMiss++ : ngDevMode.styleMapCacheMiss++);
} }
/** /**
@ -1918,4 +1925,4 @@ function assertValidDirectiveIndex(context: StylingContext, directiveIndex: numb
dirs[index + DirectiveRegistryValuesIndex.SinglePropValuesIndexOffset] === -1) { dirs[index + DirectiveRegistryValuesIndex.SinglePropValuesIndexOffset] === -1) {
throw new Error('The provided directive is not registered with the styling context'); throw new Error('The provided directive is not registered with the styling context');
} }
} }

View File

@ -9,7 +9,7 @@
/** /**
* The default directive styling index value for template-based bindings. * The default directive styling index value for template-based bindings.
* *
* All host-level bindings (e.g. `hostStyleProp` and `hostStylingMap`) are * All host-level bindings (e.g. `hostStyleProp` and `hostStyleMap`) are
* assigned a directive styling index value based on the current directive * assigned a directive styling index value based on the current directive
* uniqueId and the directive super-class inheritance depth. But for template * uniqueId and the directive super-class inheritance depth. But for template
* bindings they always have the same directive styling index value. * bindings they always have the same directive styling index value.

View File

@ -31,8 +31,10 @@ declare global {
rendererMoveNode: number; rendererMoveNode: number;
rendererRemoveNode: number; rendererRemoveNode: number;
rendererCreateComment: number; rendererCreateComment: number;
stylingMap: number; styleMap: number;
stylingMapCacheMiss: number; styleMapCacheMiss: number;
classMap: number;
classMapCacheMiss: number;
stylingProp: number; stylingProp: number;
stylingPropCacheMiss: number; stylingPropCacheMiss: number;
stylingApply: number; stylingApply: number;
@ -62,8 +64,10 @@ export function ngDevModeResetPerfCounters(): NgDevModePerfCounters {
rendererMoveNode: 0, rendererMoveNode: 0,
rendererRemoveNode: 0, rendererRemoveNode: 0,
rendererCreateComment: 0, rendererCreateComment: 0,
stylingMap: 0, styleMap: 0,
stylingMapCacheMiss: 0, styleMapCacheMiss: 0,
classMap: 0,
classMapCacheMiss: 0,
stylingProp: 0, stylingProp: 0,
stylingPropCacheMiss: 0, stylingPropCacheMiss: 0,
stylingApply: 0, stylingApply: 0,

View File

@ -10,7 +10,7 @@ import {NgForOfContext} from '@angular/common';
import {ɵɵpropertyInterpolate, ɵɵpropertyInterpolate1, ɵɵpropertyInterpolate2, ɵɵpropertyInterpolate3, ɵɵpropertyInterpolate4, ɵɵpropertyInterpolate5, ɵɵpropertyInterpolate6, ɵɵpropertyInterpolate7, ɵɵpropertyInterpolate8, ɵɵpropertyInterpolateV} from '@angular/core/src/render3/instructions/all'; import {ɵɵpropertyInterpolate, ɵɵpropertyInterpolate1, ɵɵpropertyInterpolate2, ɵɵpropertyInterpolate3, ɵɵpropertyInterpolate4, ɵɵpropertyInterpolate5, ɵɵpropertyInterpolate6, ɵɵpropertyInterpolate7, ɵɵpropertyInterpolate8, ɵɵpropertyInterpolateV} from '@angular/core/src/render3/instructions/all';
import {ɵɵdefineComponent} from '../../src/render3/definition'; import {ɵɵdefineComponent} from '../../src/render3/definition';
import {RenderFlags, ɵɵbind, ɵɵelement, ɵɵelementAttribute, ɵɵelementEnd, ɵɵelementProperty, ɵɵelementStart, ɵɵelementStyleProp, ɵɵelementStyling, ɵɵelementStylingApply, ɵɵelementStylingMap, ɵɵinterpolation1, ɵɵproperty, ɵɵselect, ɵɵtemplate, ɵɵtext, ɵɵtextBinding} from '../../src/render3/index'; import {RenderFlags, ɵɵbind, ɵɵelement, ɵɵelementAttribute, ɵɵelementClassMap, ɵɵelementEnd, ɵɵelementProperty, ɵɵelementStart, ɵɵelementStyleMap, ɵɵelementStyleProp, ɵɵelementStyling, ɵɵelementStylingApply, ɵɵinterpolation1, ɵɵproperty, ɵɵselect, ɵɵtemplate, ɵɵtext, ɵɵtextBinding} from '../../src/render3/index';
import {AttributeMarker} from '../../src/render3/interfaces/node'; import {AttributeMarker} from '../../src/render3/interfaces/node';
import {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl} from '../../src/sanitization/bypass'; import {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl} from '../../src/sanitization/bypass';
import {ɵɵdefaultStyleSanitizer, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl} from '../../src/sanitization/sanitization'; import {ɵɵdefaultStyleSanitizer, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl} from '../../src/sanitization/sanitization';
@ -1063,7 +1063,7 @@ describe('instructions', () => {
it('should add style', () => { it('should add style', () => {
const fixture = new TemplateFixture(createDivWithStyle, () => {}, 1); const fixture = new TemplateFixture(createDivWithStyle, () => {}, 1);
fixture.update(() => { fixture.update(() => {
ɵɵelementStylingMap(0, null, {'background-color': 'red'}); ɵɵelementStyleMap(0, {'background-color': 'red'});
ɵɵelementStylingApply(0); ɵɵelementStylingApply(0);
}); });
expect(fixture.html).toEqual('<div style="background-color: red; height: 10px;"></div>'); expect(fixture.html).toEqual('<div style="background-color: red; height: 10px;"></div>');
@ -1078,7 +1078,7 @@ describe('instructions', () => {
sanitizerInterceptor); sanitizerInterceptor);
fixture.update(() => { fixture.update(() => {
ɵɵelementStylingMap(0, null, { ɵɵelementStyleMap(0, {
'background-image': 'background-image', 'background-image': 'background-image',
'background': 'background', 'background': 'background',
'border-image': 'border-image', 'border-image': 'border-image',
@ -1107,7 +1107,7 @@ describe('instructions', () => {
it('should add class', () => { it('should add class', () => {
const fixture = new TemplateFixture(createDivWithStyling, () => {}, 1); const fixture = new TemplateFixture(createDivWithStyling, () => {}, 1);
fixture.update(() => { fixture.update(() => {
ɵɵelementStylingMap(0, 'multiple classes'); ɵɵelementClassMap(0, 'multiple classes');
ɵɵelementStylingApply(0); ɵɵelementStylingApply(0);
}); });
expect(fixture.html).toEqual('<div class="multiple classes"></div>'); expect(fixture.html).toEqual('<div class="multiple classes"></div>');

View File

@ -10,8 +10,8 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
import {RendererType2} from '../../src/render/api'; import {RendererType2} from '../../src/render/api';
import {getLContext} from '../../src/render3/context_discovery'; import {getLContext} from '../../src/render3/context_discovery';
import {AttributeMarker, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵtemplateRefExtractor} from '../../src/render3/index'; import {AttributeMarker, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵelementClassMap, ɵɵelementHostClassMap, ɵɵelementHostStyleMap, ɵɵelementStyleMap, ɵɵtemplateRefExtractor} from '../../src/render3/index';
import {ɵɵallocHostVars, ɵɵbind, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵdirectiveInject, ɵɵelement, ɵɵelementAttribute, ɵɵelementClassProp, ɵɵelementContainerEnd, ɵɵelementContainerStart, ɵɵelementEnd, ɵɵelementHostAttrs, ɵɵelementHostClassProp, ɵɵelementHostStyleProp, ɵɵelementHostStyling, ɵɵelementHostStylingApply, ɵɵelementHostStylingMap, ɵɵelementProperty, ɵɵelementStart, ɵɵelementStyleProp, ɵɵelementStyling, ɵɵelementStylingApply, ɵɵelementStylingMap, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵinterpolation1, ɵɵinterpolation2, ɵɵinterpolation3, ɵɵinterpolation4, ɵɵinterpolation5, ɵɵinterpolation6, ɵɵinterpolation7, ɵɵinterpolation8, ɵɵinterpolationV, ɵɵprojection, ɵɵprojectionDef, ɵɵreference, ɵɵtemplate, ɵɵtext, ɵɵtextBinding} from '../../src/render3/instructions/all'; import {ɵɵallocHostVars, ɵɵbind, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵdirectiveInject, ɵɵelement, ɵɵelementAttribute, ɵɵelementClassProp, ɵɵelementContainerEnd, ɵɵelementContainerStart, ɵɵelementEnd, ɵɵelementHostAttrs, ɵɵelementHostClassProp, ɵɵelementHostStyleProp, ɵɵelementHostStyling, ɵɵelementHostStylingApply, ɵɵelementProperty, ɵɵelementStart, ɵɵelementStyleProp, ɵɵelementStyling, ɵɵelementStylingApply, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵinterpolation1, ɵɵinterpolation2, ɵɵinterpolation3, ɵɵinterpolation4, ɵɵinterpolation5, ɵɵinterpolation6, ɵɵinterpolation7, ɵɵinterpolation8, ɵɵinterpolationV, ɵɵprojection, ɵɵprojectionDef, ɵɵreference, ɵɵtemplate, ɵɵtext, ɵɵtextBinding} from '../../src/render3/instructions/all';
import {MONKEY_PATCH_KEY_NAME} from '../../src/render3/interfaces/context'; import {MONKEY_PATCH_KEY_NAME} from '../../src/render3/interfaces/context';
import {RenderFlags} from '../../src/render3/interfaces/definition'; import {RenderFlags} from '../../src/render3/interfaces/definition';
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer'; import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer';
@ -1727,7 +1727,7 @@ describe('render3 integration test', () => {
ɵɵelementEnd(); ɵɵelementEnd();
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
ɵɵelementStylingMap(0, 'cucumber grape'); ɵɵelementClassMap(0, 'cucumber grape');
ɵɵelementStylingApply(0); ɵɵelementStylingApply(0);
} }
}, 1, 0, [DirWithClassDirective]); }, 1, 0, [DirWithClassDirective]);
@ -1748,7 +1748,7 @@ describe('render3 integration test', () => {
ɵɵelementEnd(); ɵɵelementEnd();
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
ɵɵelementStylingMap(0, null, {width: '200px', height: '500px'}); ɵɵelementStyleMap(0, {width: '200px', height: '500px'});
ɵɵelementStylingApply(0); ɵɵelementStylingApply(0);
} }
}, 1, 0, [DirWithStyleDirective]); }, 1, 0, [DirWithStyleDirective]);
@ -1993,7 +1993,8 @@ describe('render3 integration test', () => {
ɵɵelementHostStyling(); ɵɵelementHostStyling();
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
ɵɵelementHostStylingMap(ctx.classesExp, ctx.stylesExp); ɵɵelementHostStyleMap(ctx.stylesExp);
ɵɵelementHostClassMap(ctx.classesExp);
ɵɵelementHostStylingApply(); ɵɵelementHostStylingApply();
} }
} }
@ -2020,7 +2021,7 @@ describe('render3 integration test', () => {
ɵɵelementHostStyling(); ɵɵelementHostStyling();
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
ɵɵelementHostStylingMap(null, ctx.stylesExp); ɵɵelementHostStyleMap(ctx.stylesExp);
ɵɵelementHostStylingApply(); ɵɵelementHostStylingApply();
} }
} }
@ -2039,7 +2040,8 @@ describe('render3 integration test', () => {
ɵɵelementStyling(); ɵɵelementStyling();
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
ɵɵelementStylingMap(0, ctx.classesExp, ctx.stylesExp); ɵɵelementStyleMap(0, ctx.stylesExp);
ɵɵelementClassMap(0, ctx.classesExp);
ɵɵelementStylingApply(0); ɵɵelementStylingApply(0);
} }
}, 1, 0, [Dir1WithStyling, Dir2WithStyling]); }, 1, 0, [Dir1WithStyling, Dir2WithStyling]);
@ -2116,7 +2118,7 @@ describe('render3 integration test', () => {
ɵɵelementEnd(); ɵɵelementEnd();
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
ɵɵelementStylingMap(0, ɵɵinterpolation2('-', ctx.name, '-', ctx.age, '-')); ɵɵelementClassMap(0, ɵɵinterpolation2('-', ctx.name, '-', ctx.age, '-'));
ɵɵelementStylingApply(0); ɵɵelementStylingApply(0);
} }
}, 1, 2); }, 1, 2);

View File

@ -9,7 +9,7 @@ import {createLView, createTView} from '@angular/core/src/render3/instructions/s
import {createRootContext} from '../../../src/render3/component'; import {createRootContext} from '../../../src/render3/component';
import {getLContext} from '../../../src/render3/context_discovery'; import {getLContext} from '../../../src/render3/context_discovery';
import {ɵɵdefineComponent, ɵɵdefineDirective, ɵɵelementClassProp, ɵɵelementEnd, ɵɵelementHostClassProp, ɵɵelementHostStyleProp, ɵɵelementHostStyling, ɵɵelementHostStylingApply, ɵɵelementStart, ɵɵelementStyleProp, ɵɵelementStyling, ɵɵelementStylingApply, ɵɵelementStylingMap, ɵɵnamespaceSVG} from '../../../src/render3/index'; import {ɵɵdefineComponent, ɵɵdefineDirective, ɵɵelementClassMap, ɵɵelementClassProp, ɵɵelementEnd, ɵɵelementHostClassProp, ɵɵelementHostStyleProp, ɵɵelementHostStyling, ɵɵelementHostStylingApply, ɵɵelementStart, ɵɵelementStyleMap, ɵɵelementStyleProp, ɵɵelementStyling, ɵɵelementStylingApply, ɵɵnamespaceSVG} from '../../../src/render3/index';
import {RenderFlags} from '../../../src/render3/interfaces/definition'; import {RenderFlags} from '../../../src/render3/interfaces/definition';
import {AttributeMarker, TAttributes} from '../../../src/render3/interfaces/node'; import {AttributeMarker, TAttributes} from '../../../src/render3/interfaces/node';
import {BindingStore, BindingType, PlayState, Player, PlayerContext, PlayerFactory, PlayerHandler} from '../../../src/render3/interfaces/player'; import {BindingStore, BindingType, PlayState, Player, PlayerContext, PlayerFactory, PlayerHandler} from '../../../src/render3/interfaces/player';
@ -17,7 +17,7 @@ import {RElement, Renderer3, domRendererFactory3} from '../../../src/render3/int
import {StylingContext, StylingFlags, StylingIndex} from '../../../src/render3/interfaces/styling'; import {StylingContext, StylingFlags, StylingIndex} from '../../../src/render3/interfaces/styling';
import {CONTEXT, LView, LViewFlags, RootContext} from '../../../src/render3/interfaces/view'; import {CONTEXT, LView, LViewFlags, RootContext} from '../../../src/render3/interfaces/view';
import {addPlayer, getPlayers} from '../../../src/render3/players'; import {addPlayer, getPlayers} from '../../../src/render3/players';
import {ClassAndStylePlayerBuilder, compareLogSummaries, directiveOwnerPointers, generateConfigSummary, getDirectiveIndexFromEntry, initializeStaticContext, isContextDirty, patchContextWithStaticAttrs, renderStyling as _renderStyling, setContextDirty, updateClassProp, updateContextWithBindings, updateStyleProp, updateStylingMap} from '../../../src/render3/styling/class_and_style_bindings'; import {ClassAndStylePlayerBuilder, compareLogSummaries, directiveOwnerPointers, generateConfigSummary, getDirectiveIndexFromEntry, initializeStaticContext, isContextDirty, patchContextWithStaticAttrs, renderStyling as _renderStyling, setContextDirty, updateClassMap, updateClassProp, updateContextWithBindings, updateStyleMap, updateStyleProp} from '../../../src/render3/styling/class_and_style_bindings';
import {CorePlayerHandler} from '../../../src/render3/styling/core_player_handler'; import {CorePlayerHandler} from '../../../src/render3/styling/core_player_handler';
import {registerHostDirective} from '../../../src/render3/styling/host_instructions_queue'; import {registerHostDirective} from '../../../src/render3/styling/host_instructions_queue';
import {BoundPlayerFactory, bindPlayerFactory} from '../../../src/render3/styling/player_factory'; import {BoundPlayerFactory, bindPlayerFactory} from '../../../src/render3/styling/player_factory';
@ -32,6 +32,13 @@ describe('style and class based bindings', () => {
let element: RElement|null = null; let element: RElement|null = null;
beforeEach(() => { element = document.createElement('div') as any; }); beforeEach(() => { element = document.createElement('div') as any; });
function updateStyleAndClassMaps(
context: StylingContext, classes: {} | string | null, styles?: {} | null,
directiveIndex?: number) {
updateStyleMap(context, styles || null, directiveIndex);
updateClassMap(context, classes, directiveIndex);
}
function createMockViewData(playerHandler: PlayerHandler, context: StylingContext): LView { function createMockViewData(playerHandler: PlayerHandler, context: StylingContext): LView {
const rootContext = const rootContext =
createRootContext(requestAnimationFrame.bind(window), playerHandler || null); createRootContext(requestAnimationFrame.bind(window), playerHandler || null);
@ -139,11 +146,11 @@ describe('style and class based bindings', () => {
} }
function updateClasses(context: StylingContext, classes: string | {[key: string]: any} | null) { function updateClasses(context: StylingContext, classes: string | {[key: string]: any} | null) {
updateStylingMap(context, classes, null); updateStyleAndClassMaps(context, classes, null);
} }
function updateStyles(context: StylingContext, styles: {[key: string]: any} | null) { function updateStyles(context: StylingContext, styles: {[key: string]: any} | null) {
updateStylingMap(context, null, styles); updateStyleAndClassMaps(context, null, styles);
} }
function cleanStyle(a: number = 0, b: number = 0): number { return _clean(a, b, false, false); } function cleanStyle(a: number = 0, b: number = 0): number { return _clean(a, b, false, false); }
@ -389,7 +396,7 @@ describe('style and class based bindings', () => {
ɵɵelementEnd(); ɵɵelementEnd();
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
ɵɵelementStylingMap(0, null, ctx.myStyles); ɵɵelementStyleMap(0, ctx.myStyles);
ɵɵelementStyleProp(0, 0, ctx.myWidth); ɵɵelementStyleProp(0, 0, ctx.myWidth);
ɵɵelementStylingApply(0); ɵɵelementStylingApply(0);
} }
@ -1470,9 +1477,9 @@ describe('style and class based bindings', () => {
const ctx = allocStylingContext(element, template); const ctx = allocStylingContext(element, template);
let s1, s2, s3; let s1, s2, s3;
updateStylingMap(ctx, null, s1 = {width: '100px', height: '99px'}, dir1); updateStyleAndClassMaps(ctx, null, s1 = {width: '100px', height: '99px'}, dir1);
updateStylingMap(ctx, null, s2 = {width: '200px', opacity: '0.5'}, dir2); updateStyleAndClassMaps(ctx, null, s2 = {width: '200px', opacity: '0.5'}, dir2);
updateStylingMap(ctx, null, s3 = {width: '300px', height: '999px'}, dir3); updateStyleAndClassMaps(ctx, null, s3 = {width: '300px', height: '999px'}, dir3);
expect(ctx[StylingIndex.CachedMultiStyles]).toEqual([ expect(ctx[StylingIndex.CachedMultiStyles]).toEqual([
3, 3,
@ -1526,9 +1533,9 @@ describe('style and class based bindings', () => {
2, 2,
]); ]);
updateStylingMap(ctx, null, {opacity: '0', width: null}, dir1); updateStyleAndClassMaps(ctx, null, {opacity: '0', width: null}, dir1);
updateStylingMap(ctx, null, {width: '200px', opacity: '0.5'}, dir2); updateStyleAndClassMaps(ctx, null, {width: '200px', opacity: '0.5'}, dir2);
updateStylingMap(ctx, null, {width: '300px', height: '999px'}, dir3); updateStyleAndClassMaps(ctx, null, {width: '300px', height: '999px'}, dir3);
assertContextOnlyValues(ctx, [ assertContextOnlyValues(ctx, [
// #10 // #10
@ -1562,9 +1569,10 @@ describe('style and class based bindings', () => {
3, 3,
]); ]);
updateStylingMap(ctx, null, null, dir1); updateStyleAndClassMaps(ctx, null, null, dir1);
updateStylingMap(ctx, null, {width: '500px', opacity: '0.2'}, dir2); updateStyleAndClassMaps(ctx, null, {width: '500px', opacity: '0.2'}, dir2);
updateStylingMap(ctx, null, {width: '300px', height: '999px', color: 'red'}, dir3); updateStyleAndClassMaps(
ctx, null, {width: '300px', height: '999px', color: 'red'}, dir3);
assertContextOnlyValues(ctx, [ assertContextOnlyValues(ctx, [
// #10 // #10
@ -1619,9 +1627,9 @@ describe('style and class based bindings', () => {
const ctx = allocStylingContext(element, template); const ctx = allocStylingContext(element, template);
let c1, c2, c3; let c1, c2, c3;
updateStylingMap(ctx, c1 = {red: true, orange: true}, null, dir1); updateStyleAndClassMaps(ctx, c1 = {red: true, orange: true}, null, dir1);
updateStylingMap(ctx, c2 = 'black red', null, dir2); updateStyleAndClassMaps(ctx, c2 = 'black red', null, dir2);
updateStylingMap(ctx, c3 = 'silver green', null, dir3); updateStyleAndClassMaps(ctx, c3 = 'silver green', null, dir3);
expect(ctx[StylingIndex.CachedMultiClasses]).toEqual([ expect(ctx[StylingIndex.CachedMultiClasses]).toEqual([
5, 0, 18, null, 0, 0, 18, c1, 2, 0, 26, c2, 1, 0, 30, c3, 2 5, 0, 18, null, 0, 0, 18, c1, 2, 0, 26, c2, 1, 0, 30, c3, 2
@ -1671,9 +1679,9 @@ describe('style and class based bindings', () => {
3, 3,
]); ]);
updateStylingMap(ctx, c1 = {orange: true}, null, dir1); updateStyleAndClassMaps(ctx, c1 = {orange: true}, null, dir1);
updateStylingMap(ctx, c2 = 'black red', null, dir2); updateStyleAndClassMaps(ctx, c2 = 'black red', null, dir2);
updateStylingMap(ctx, c3 = 'green', null, dir3); updateStyleAndClassMaps(ctx, c3 = 'green', null, dir3);
assertContextOnlyValues(ctx, [ assertContextOnlyValues(ctx, [
// #10 // #10
@ -1719,9 +1727,9 @@ describe('style and class based bindings', () => {
1, 1,
]); ]);
updateStylingMap(ctx, c1 = 'green', null, dir1); updateStyleAndClassMaps(ctx, c1 = 'green', null, dir1);
updateStylingMap(ctx, c2 = null, null, dir2); updateStyleAndClassMaps(ctx, c2 = null, null, dir2);
updateStylingMap(ctx, c3 = 'red', null, dir3); updateStyleAndClassMaps(ctx, c3 = 'red', null, dir3);
assertContextOnlyValues(ctx, [ assertContextOnlyValues(ctx, [
// #10 // #10
@ -1848,7 +1856,7 @@ describe('style and class based bindings', () => {
const foreignDir = 2; const foreignDir = 2;
expect(() => { expect(() => {
updateStylingMap(ctx, 'foo', null, foreignDir); updateStyleAndClassMaps(ctx, 'foo', null, foreignDir);
}).toThrowError('The provided directive is not registered with the styling context'); }).toThrowError('The provided directive is not registered with the styling context');
}); });
}); });
@ -1862,7 +1870,7 @@ describe('style and class based bindings', () => {
let styles: any = {'font-size': ''}; let styles: any = {'font-size': ''};
updateStyleProp(stylingContext, 0, ''); updateStyleProp(stylingContext, 0, '');
updateStylingMap(stylingContext, null, styles); updateStyleAndClassMaps(stylingContext, null, styles);
getStyles(stylingContext); getStyles(stylingContext);
expect(store.getValues()).toEqual({'font-size': '', 'color': ''}); expect(store.getValues()).toEqual({'font-size': '', 'color': ''});
@ -1871,7 +1879,7 @@ describe('style and class based bindings', () => {
registerHostDirective(stylingContext, otherDirective); registerHostDirective(stylingContext, otherDirective);
updateStyleProp(stylingContext, 0, 'red'); updateStyleProp(stylingContext, 0, 'red');
updateStylingMap(stylingContext, null, styles = {'font-size': '20px'}); updateStyleAndClassMaps(stylingContext, null, styles = {'font-size': '20px'});
getStyles(stylingContext); getStyles(stylingContext);
expect(store.getValues()).toEqual({'font-size': '', 'color': ''}); expect(store.getValues()).toEqual({'font-size': '', 'color': ''});
@ -1880,7 +1888,7 @@ describe('style and class based bindings', () => {
expect(store.getValues()).toEqual({'font-size': '20px', color: 'red'}); expect(store.getValues()).toEqual({'font-size': '20px', color: 'red'});
updateStyleProp(stylingContext, 0, ''); updateStyleProp(stylingContext, 0, '');
updateStylingMap(stylingContext, null, styles = {}); updateStyleAndClassMaps(stylingContext, null, styles = {});
getStyles(stylingContext, otherDirective); getStyles(stylingContext, otherDirective);
expect(store.getValues()).toEqual({'font-size': null, color: ''}); expect(store.getValues()).toEqual({'font-size': null, color: ''});
@ -1958,10 +1966,10 @@ describe('style and class based bindings', () => {
const stylingContext = createStylingContext(null, null, null, ['baz']); const stylingContext = createStylingContext(null, null, null, ['baz']);
expect(getClasses(stylingContext)).toEqual({}); expect(getClasses(stylingContext)).toEqual({});
updateStylingMap(stylingContext, 'foo bar baz'); updateStyleAndClassMaps(stylingContext, 'foo bar baz');
expect(getClasses(stylingContext)).toEqual({'foo': true, 'bar': true, 'baz': true}); expect(getClasses(stylingContext)).toEqual({'foo': true, 'bar': true, 'baz': true});
updateStylingMap(stylingContext, 'foo car'); updateStyleAndClassMaps(stylingContext, 'foo car');
updateClassProp(stylingContext, 0, true); updateClassProp(stylingContext, 0, true);
expect(getClasses(stylingContext)) expect(getClasses(stylingContext))
.toEqual({'foo': true, 'car': true, 'bar': false, 'baz': true}); .toEqual({'foo': true, 'car': true, 'bar': false, 'baz': true});
@ -2036,7 +2044,7 @@ describe('style and class based bindings', () => {
expect(getStylesAndClasses(stylingContext)).toEqual([{}, {}]); expect(getStylesAndClasses(stylingContext)).toEqual([{}, {}]);
let cachedStyleMap: any = {width: '200px', opacity: '0.5'}; let cachedStyleMap: any = {width: '200px', opacity: '0.5'};
updateStylingMap(stylingContext, 'tall round', cachedStyleMap); updateStyleAndClassMaps(stylingContext, 'tall round', cachedStyleMap);
assertContext(stylingContext, [ assertContext(stylingContext, [
element, element,
masterConfig(26, true), // masterConfig(26, true), //
@ -2117,7 +2125,7 @@ describe('style and class based bindings', () => {
let cachedClassMap = {tall: true, wide: true}; let cachedClassMap = {tall: true, wide: true};
cachedStyleMap = {width: '500px'}; cachedStyleMap = {width: '500px'};
updateStylingMap(stylingContext, cachedClassMap, cachedStyleMap); updateStyleAndClassMaps(stylingContext, cachedClassMap, cachedStyleMap);
updateStyleProp(stylingContext, 0, '300px'); updateStyleProp(stylingContext, 0, '300px');
assertContext(stylingContext, [ assertContext(stylingContext, [
@ -2198,7 +2206,7 @@ describe('style and class based bindings', () => {
{width: '300px', opacity: null}, {width: '300px', opacity: null},
]); ]);
updateStylingMap(stylingContext, {wide: false}); updateStyleAndClassMaps(stylingContext, {wide: false});
expect(getStylesAndClasses(stylingContext)).toEqual([ expect(getStylesAndClasses(stylingContext)).toEqual([
{wide: false, tall: false, round: false}, {width: '100px', opacity: null} {wide: false, tall: false, round: false}, {width: '100px', opacity: null}
@ -2212,7 +2220,7 @@ describe('style and class based bindings', () => {
const stylesMap = {width: '200px'}; const stylesMap = {width: '200px'};
const classesMap = {foo: true}; const classesMap = {foo: true};
updateStylingMap(stylingContext, classesMap, stylesMap); updateStyleAndClassMaps(stylingContext, classesMap, stylesMap);
// apply the styles // apply the styles
getStylesAndClasses(stylingContext); getStylesAndClasses(stylingContext);
@ -2239,7 +2247,7 @@ describe('style and class based bindings', () => {
stylesMap.width = '300px'; stylesMap.width = '300px';
classesMap.foo = false; classesMap.foo = false;
updateStylingMap(stylingContext, classesMap, stylesMap); updateStyleAndClassMaps(stylingContext, classesMap, stylesMap);
// apply the styles // apply the styles
getStylesAndClasses(stylingContext); getStylesAndClasses(stylingContext);
@ -2269,7 +2277,7 @@ describe('style and class based bindings', () => {
const getClasses = trackClassesFactory(); const getClasses = trackClassesFactory();
const classes = 'apple orange banana'; const classes = 'apple orange banana';
updateStylingMap(stylingContext, classes); updateStyleAndClassMaps(stylingContext, classes);
// apply the styles // apply the styles
expect(getClasses(stylingContext)).toEqual({apple: true, orange: true, banana: true}); expect(getClasses(stylingContext)).toEqual({apple: true, orange: true, banana: true});
@ -2307,7 +2315,7 @@ describe('style and class based bindings', () => {
stylingContext[14 + StylingIndex.ValueOffset] = false; stylingContext[14 + StylingIndex.ValueOffset] = false;
stylingContext[18 + StylingIndex.ValueOffset] = false; stylingContext[18 + StylingIndex.ValueOffset] = false;
updateStylingMap(stylingContext, classes); updateStyleAndClassMaps(stylingContext, classes);
// apply the styles // apply the styles
expect(getClasses(stylingContext)).toEqual({apple: true, orange: true, banana: true}); expect(getClasses(stylingContext)).toEqual({apple: true, orange: true, banana: true});
@ -2320,7 +2328,7 @@ describe('style and class based bindings', () => {
let classes: any = {red: false}; let classes: any = {red: false};
updateClassProp(stylingContext, 0, false); updateClassProp(stylingContext, 0, false);
updateStylingMap(stylingContext, classes); updateStyleAndClassMaps(stylingContext, classes);
// apply the styles // apply the styles
getClasses(stylingContext, true); getClasses(stylingContext, true);
@ -2328,14 +2336,14 @@ describe('style and class based bindings', () => {
classes = {red: true}; classes = {red: true};
updateClassProp(stylingContext, 0, true); updateClassProp(stylingContext, 0, true);
updateStylingMap(stylingContext, classes); updateStyleAndClassMaps(stylingContext, classes);
getClasses(stylingContext); getClasses(stylingContext);
expect(store.getValues()).toEqual({red: true, blue: true}); expect(store.getValues()).toEqual({red: true, blue: true});
classes = {red: false}; classes = {red: false};
updateClassProp(stylingContext, 0, false); updateClassProp(stylingContext, 0, false);
updateStylingMap(stylingContext, classes); updateStyleAndClassMaps(stylingContext, classes);
getClasses(stylingContext); getClasses(stylingContext);
expect(store.getValues()).toEqual({red: false, blue: false}); expect(store.getValues()).toEqual({red: false, blue: false});
@ -2367,7 +2375,7 @@ describe('style and class based bindings', () => {
}, },
styles); styles);
updateStylingMap(context, classFactory, styleFactory); updateStyleAndClassMaps(context, classFactory, styleFactory);
expect(classResult).toBeFalsy(); expect(classResult).toBeFalsy();
renderStyles(context); renderStyles(context);
@ -2392,15 +2400,15 @@ describe('style and class based bindings', () => {
return new MockPlayer(); return new MockPlayer();
}; };
updateStylingMap(context, null, bindPlayerFactory(buildFn, {width: '100px'})); updateStyleAndClassMaps(context, null, bindPlayerFactory(buildFn, {width: '100px'}));
renderStyles(context); renderStyles(context);
expect(count).toEqual(1); expect(count).toEqual(1);
updateStylingMap(context, null, bindPlayerFactory(buildFn, {height: '100px'})); updateStyleAndClassMaps(context, null, bindPlayerFactory(buildFn, {height: '100px'}));
renderStyles(context); renderStyles(context);
expect(count).toEqual(2); expect(count).toEqual(2);
updateStylingMap( updateStyleAndClassMaps(
context, null, bindPlayerFactory(buildFn, {height: '200px', width: '200px'})); context, null, bindPlayerFactory(buildFn, {height: '200px', width: '200px'}));
renderStyles(context); renderStyles(context);
expect(count).toEqual(3); expect(count).toEqual(3);
@ -2415,15 +2423,16 @@ describe('style and class based bindings', () => {
return new MockPlayer(); return new MockPlayer();
}; };
updateStylingMap(context, bindPlayerFactory(buildFn, {myClass: true})); updateStyleAndClassMaps(context, bindPlayerFactory(buildFn, {myClass: true}));
renderStyles(context); renderStyles(context);
expect(count).toEqual(1); expect(count).toEqual(1);
updateStylingMap(context, bindPlayerFactory(buildFn, {otherClass: true})); updateStyleAndClassMaps(context, bindPlayerFactory(buildFn, {otherClass: true}));
renderStyles(context); renderStyles(context);
expect(count).toEqual(2); expect(count).toEqual(2);
updateStylingMap(context, bindPlayerFactory(buildFn, {myClass: false, otherClass: false})); updateStyleAndClassMaps(
context, bindPlayerFactory(buildFn, {myClass: false, otherClass: false}));
renderStyles(context); renderStyles(context);
expect(count).toEqual(3); expect(count).toEqual(3);
}); });
@ -2452,7 +2461,7 @@ describe('style and class based bindings', () => {
const classPlayerBuilder = const classPlayerBuilder =
new ClassAndStylePlayerBuilder(classFactory, element as HTMLElement, BindingType.Class); new ClassAndStylePlayerBuilder(classFactory, element as HTMLElement, BindingType.Class);
updateStylingMap(context, classFactory, styleFactory); updateStyleAndClassMaps(context, classFactory, styleFactory);
expect(context[StylingIndex.PlayerContext]).toEqual([ expect(context[StylingIndex.PlayerContext]).toEqual([
5, classPlayerBuilder, null, stylePlayerBuilder, null 5, classPlayerBuilder, null, stylePlayerBuilder, null
]); ]);
@ -2518,7 +2527,7 @@ describe('style and class based bindings', () => {
const classMapPlayerBuilder = new ClassAndStylePlayerBuilder( const classMapPlayerBuilder = new ClassAndStylePlayerBuilder(
classMapFactory, element as HTMLElement, BindingType.Class); classMapFactory, element as HTMLElement, BindingType.Class);
updateStylingMap(context, classMapFactory, styleMapFactory); updateStyleAndClassMaps(context, classMapFactory, styleMapFactory);
const widthFactory = bindPlayerFactory(styleBuildFn, '100px'); const widthFactory = bindPlayerFactory(styleBuildFn, '100px');
const barFactory = bindPlayerFactory(classBuildFn, true); const barFactory = bindPlayerFactory(classBuildFn, true);
@ -2604,7 +2613,7 @@ describe('style and class based bindings', () => {
expect(context[StylingIndex.PlayerContext]).toEqual(null); expect(context[StylingIndex.PlayerContext]).toEqual(null);
let mapFactory = bindPlayerFactory(buildFn, {width: '200px'}); let mapFactory = bindPlayerFactory(buildFn, {width: '200px'});
updateStylingMap(context, null, mapFactory); updateStyleAndClassMaps(context, null, mapFactory);
renderStyles(context, false, undefined, lView); renderStyles(context, false, undefined, lView);
expect(players.length).toEqual(1); expect(players.length).toEqual(1);
@ -2612,7 +2621,7 @@ describe('style and class based bindings', () => {
expect(p1.state).toEqual(PlayState.Pending); expect(p1.state).toEqual(PlayState.Pending);
mapFactory = bindPlayerFactory(buildFn, {width: '100px'}); mapFactory = bindPlayerFactory(buildFn, {width: '100px'});
updateStylingMap(context, null, mapFactory); updateStyleAndClassMaps(context, null, mapFactory);
renderStyles(context, false, undefined, lView); renderStyles(context, false, undefined, lView);
expect(players.length).toEqual(1); expect(players.length).toEqual(1);
@ -2684,7 +2693,7 @@ describe('style and class based bindings', () => {
const classMapWithPlayerFactory = bindPlayerFactory(buildClassFn, cachedClassMap); const classMapWithPlayerFactory = bindPlayerFactory(buildClassFn, cachedClassMap);
const styleMapPlayerBuilder = makePlayerBuilder(styleMapWithPlayerFactory, false); const styleMapPlayerBuilder = makePlayerBuilder(styleMapWithPlayerFactory, false);
const classMapPlayerBuilder = makePlayerBuilder(classMapWithPlayerFactory, true); const classMapPlayerBuilder = makePlayerBuilder(classMapWithPlayerFactory, true);
updateStylingMap(context, classMapWithPlayerFactory, styleMapWithPlayerFactory); updateStyleAndClassMaps(context, classMapWithPlayerFactory, styleMapWithPlayerFactory);
const colorWithPlayerFactory = bindPlayerFactory(buildStyleFn, 'red'); const colorWithPlayerFactory = bindPlayerFactory(buildStyleFn, 'red');
const fooWithPlayerFactory = bindPlayerFactory(buildClassFn, true); const fooWithPlayerFactory = bindPlayerFactory(buildClassFn, true);
@ -2754,7 +2763,7 @@ describe('style and class based bindings', () => {
0, 0,
]); ]);
updateStylingMap(context, cachedClassMap, cachedStyleMap); updateStyleAndClassMaps(context, cachedClassMap, cachedStyleMap);
const colorWithoutPlayerFactory = 'blue'; const colorWithoutPlayerFactory = 'blue';
const fooWithoutPlayerFactory = false; const fooWithoutPlayerFactory = false;
@ -2836,7 +2845,7 @@ describe('style and class based bindings', () => {
let styleFactory = bindPlayerFactory(buildStyleFn, {opacity: '1'}) as BoundPlayerFactory<any>; let styleFactory = bindPlayerFactory(buildStyleFn, {opacity: '1'}) as BoundPlayerFactory<any>;
let classFactory = bindPlayerFactory(buildClassFn, 'bar') as BoundPlayerFactory<any>; let classFactory = bindPlayerFactory(buildClassFn, 'bar') as BoundPlayerFactory<any>;
updateStylingMap(context, classFactory, styleFactory); updateStyleAndClassMaps(context, classFactory, styleFactory);
expect(styleCalls).toEqual(0); expect(styleCalls).toEqual(0);
expect(classCalls).toEqual(0); expect(classCalls).toEqual(0);
@ -2849,18 +2858,18 @@ describe('style and class based bindings', () => {
expect(classCalls).toEqual(1); expect(classCalls).toEqual(1);
styleFactory = bindPlayerFactory(buildStyleFn, {opacity: '0.5'}) as BoundPlayerFactory<any>; styleFactory = bindPlayerFactory(buildStyleFn, {opacity: '0.5'}) as BoundPlayerFactory<any>;
updateStylingMap(context, classFactory, styleFactory); updateStyleAndClassMaps(context, classFactory, styleFactory);
renderStyles(context, false, undefined, lView); renderStyles(context, false, undefined, lView);
expect(styleCalls).toEqual(2); expect(styleCalls).toEqual(2);
expect(classCalls).toEqual(1); expect(classCalls).toEqual(1);
classFactory = bindPlayerFactory(buildClassFn, 'foo') as BoundPlayerFactory<any>; classFactory = bindPlayerFactory(buildClassFn, 'foo') as BoundPlayerFactory<any>;
updateStylingMap(context, classFactory, styleFactory); updateStyleAndClassMaps(context, classFactory, styleFactory);
renderStyles(context, false, undefined, lView); renderStyles(context, false, undefined, lView);
expect(styleCalls).toEqual(2); expect(styleCalls).toEqual(2);
expect(classCalls).toEqual(2); expect(classCalls).toEqual(2);
updateStylingMap(context, 'foo', {opacity: '0.5'}); updateStyleAndClassMaps(context, 'foo', {opacity: '0.5'});
renderStyles(context, false, undefined, lView); renderStyles(context, false, undefined, lView);
expect(styleCalls).toEqual(2); expect(styleCalls).toEqual(2);
expect(classCalls).toEqual(2); expect(classCalls).toEqual(2);
@ -2883,7 +2892,7 @@ describe('style and class based bindings', () => {
}; };
const mapFactory = bindPlayerFactory(mapBuildFn, {color: 'black'}); const mapFactory = bindPlayerFactory(mapBuildFn, {color: 'black'});
updateStylingMap(context, null, mapFactory); updateStyleAndClassMaps(context, null, mapFactory);
updateStyleProp(context, 0, 'green'); updateStyleProp(context, 0, 'green');
renderStyles(context, false, undefined, lView); renderStyles(context, false, undefined, lView);
@ -2907,7 +2916,7 @@ describe('style and class based bindings', () => {
propPlayer = styleMapPlayer = null; propPlayer = styleMapPlayer = null;
updateStylingMap(context, null, null); updateStyleAndClassMaps(context, null, null);
renderStyles(context, false, undefined, lView); renderStyles(context, false, undefined, lView);
expect(propPlayer).toBeFalsy(); expect(propPlayer).toBeFalsy();
@ -2929,7 +2938,7 @@ describe('style and class based bindings', () => {
}; };
let factory = bindPlayerFactory<{[key: string]: any}>(buildFn, {width: '200px'}); let factory = bindPlayerFactory<{[key: string]: any}>(buildFn, {width: '200px'});
updateStylingMap(context, null, factory); updateStyleAndClassMaps(context, null, factory);
renderStyles(context, false, undefined, lView); renderStyles(context, false, undefined, lView);
expect(previousPlayer).toEqual(null); expect(previousPlayer).toEqual(null);
@ -2937,7 +2946,7 @@ describe('style and class based bindings', () => {
factory = bindPlayerFactory(buildFn, {height: '200px'}); factory = bindPlayerFactory(buildFn, {height: '200px'});
updateStylingMap(context, null, factory); updateStyleAndClassMaps(context, null, factory);
renderStyles(context, false, undefined, lView); renderStyles(context, false, undefined, lView);
expect(previousPlayer !.value).toEqual({width: '200px'}); expect(previousPlayer !.value).toEqual({width: '200px'});
@ -2959,7 +2968,7 @@ describe('style and class based bindings', () => {
}; };
let factory = bindPlayerFactory<any>(buildFn, {foo: true}); let factory = bindPlayerFactory<any>(buildFn, {foo: true});
updateStylingMap(context, null, factory); updateStyleAndClassMaps(context, null, factory);
renderStyles(context, false, undefined, lView); renderStyles(context, false, undefined, lView);
expect(currentPlayer).toBeTruthy(); expect(currentPlayer).toBeTruthy();
@ -2969,7 +2978,7 @@ describe('style and class based bindings', () => {
previousPlayer = currentPlayer = null; previousPlayer = currentPlayer = null;
factory = bindPlayerFactory(buildFn, {bar: true}); factory = bindPlayerFactory(buildFn, {bar: true});
updateStylingMap(context, null, factory); updateStyleAndClassMaps(context, null, factory);
renderStyles(context, false, undefined, lView); renderStyles(context, false, undefined, lView);
expect(currentPlayer).toBeTruthy(); expect(currentPlayer).toBeTruthy();
@ -3000,13 +3009,13 @@ describe('style and class based bindings', () => {
let factory = bindPlayerFactory<{[key: string]: any}>( let factory = bindPlayerFactory<{[key: string]: any}>(
buildFn, {width: '200px', height: '100px', opacity: '1'}); buildFn, {width: '200px', height: '100px', opacity: '1'});
updateStylingMap(context, null, factory); updateStyleAndClassMaps(context, null, factory);
renderStyles(context, false, undefined, lView); renderStyles(context, false, undefined, lView);
expect(values !).toEqual({width: '200px-safe!', height: '100px-safe!', opacity: '1'}); expect(values !).toEqual({width: '200px-safe!', height: '100px-safe!', opacity: '1'});
factory = bindPlayerFactory(buildFn, {width: 'auto'}); factory = bindPlayerFactory(buildFn, {width: 'auto'});
updateStylingMap(context, null, factory); updateStyleAndClassMaps(context, null, factory);
renderStyles(context, false, undefined, lView); renderStyles(context, false, undefined, lView);
expect(values !).toEqual({width: 'auto-safe!', height: null, opacity: null}); expect(values !).toEqual({width: 'auto-safe!', height: null, opacity: null});
@ -3035,20 +3044,21 @@ describe('style and class based bindings', () => {
const styleMapFactory = bindPlayerFactory(styleBuildFn, {opacity: '1'}); const styleMapFactory = bindPlayerFactory(styleBuildFn, {opacity: '1'});
const classMapFactory = bindPlayerFactory(classBuildFn, {map: true}); const classMapFactory = bindPlayerFactory(classBuildFn, {map: true});
updateStylingMap(context, classMapFactory, styleMapFactory); updateStyleAndClassMaps(context, classMapFactory, styleMapFactory);
updateStyleProp(context, 0, bindPlayerFactory(styleBuildFn, '100px') as any); updateStyleProp(context, 0, bindPlayerFactory(styleBuildFn, '100px') as any);
updateClassProp(context, 0, bindPlayerFactory(classBuildFn, true) as any); updateClassProp(context, 0, bindPlayerFactory(classBuildFn, true) as any);
updateClassProp(context, 1, bindPlayerFactory(classBuildFn, true) as any); updateClassProp(context, 1, bindPlayerFactory(classBuildFn, true) as any);
renderStyles(context, false, undefined, lView); renderStyles(context, false, undefined, lView);
handler.flushPlayers(); handler.flushPlayers();
expect(players.length).toEqual(5);
const [p1, p2, p3, p4, p5] = players; const [p1, p2, p3, p4, p5] = players;
expect(p1.state).toEqual(PlayState.Running); expect(p1.state).toEqual(PlayState.Running);
expect(p2.state).toEqual(PlayState.Running); expect(p2.state).toEqual(PlayState.Running);
expect(p3.state).toEqual(PlayState.Running); expect(p3.state).toEqual(PlayState.Running);
expect(p4.state).toEqual(PlayState.Running); expect(p4.state).toEqual(PlayState.Running);
updateStylingMap(context, {bar: true}, {height: '200px'}); updateStyleAndClassMaps(context, {bar: true}, {height: '200px'});
updateStyleProp(context, 0, '200px'); updateStyleProp(context, 0, '200px');
updateClassProp(context, 0, false); updateClassProp(context, 0, false);
expect(p1.state).toEqual(PlayState.Running); expect(p1.state).toEqual(PlayState.Running);
@ -3100,7 +3110,8 @@ describe('style and class based bindings', () => {
ɵɵelementEnd(); ɵɵelementEnd();
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
ɵɵelementStylingMap(0, classMapFactory, styleMapFactory); ɵɵelementStyleMap(0, styleMapFactory);
ɵɵelementClassMap(0, classMapFactory);
ɵɵelementStyleProp(0, 0, widthFactory); ɵɵelementStyleProp(0, 0, widthFactory);
ɵɵelementClassProp(0, 0, fooFactory); ɵɵelementClassProp(0, 0, fooFactory);
ɵɵelementStylingApply(0); ɵɵelementStylingApply(0);
@ -3173,7 +3184,8 @@ describe('style and class based bindings', () => {
ɵɵelementEnd(); ɵɵelementEnd();
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
ɵɵelementStylingMap(0, classMapFactory, styleMapFactory); ɵɵelementStyleMap(0, styleMapFactory);
ɵɵelementClassMap(0, classMapFactory);
ɵɵelementStyleProp(0, 0, widthFactory); ɵɵelementStyleProp(0, 0, widthFactory);
ɵɵelementClassProp(0, 0, fooFactory); ɵɵelementClassProp(0, 0, fooFactory);
ɵɵelementStylingApply(0); ɵɵelementStylingApply(0);

View File

@ -795,6 +795,10 @@ export declare function ɵɵelement(index: number, name: string, attrs?: TAttrib
export declare function ɵɵelementAttribute(index: number, name: string, value: any, sanitizer?: SanitizerFn | null, namespace?: string): void; export declare function ɵɵelementAttribute(index: number, name: string, value: any, sanitizer?: SanitizerFn | null, namespace?: string): void;
export declare function ɵɵelementClassMap(index: number, classes: {
[styleName: string]: any;
} | NO_CHANGE | string | null): void;
export declare function ɵɵelementClassProp(index: number, classIndex: number, value: boolean | PlayerFactory, forceOverride?: boolean): void; export declare function ɵɵelementClassProp(index: number, classIndex: number, value: boolean | PlayerFactory, forceOverride?: boolean): void;
export declare function ɵɵelementContainerEnd(): void; export declare function ɵɵelementContainerEnd(): void;
@ -805,36 +809,36 @@ export declare function ɵɵelementEnd(): void;
export declare function ɵɵelementHostAttrs(attrs: TAttributes): void; export declare function ɵɵelementHostAttrs(attrs: TAttributes): void;
export declare function ɵɵelementHostClassMap(classes: {
[key: string]: any;
} | string | NO_CHANGE | null): void;
export declare function ɵɵelementHostClassProp(classIndex: number, value: boolean | PlayerFactory, forceOverride?: boolean): void; export declare function ɵɵelementHostClassProp(classIndex: number, value: boolean | PlayerFactory, forceOverride?: boolean): void;
export declare function ɵɵelementHostStyleMap(styles: {
[styleName: string]: any;
} | NO_CHANGE | null): void;
export declare function ɵɵelementHostStyleProp(styleIndex: number, value: string | number | String | PlayerFactory | null, suffix?: string | null, forceOverride?: boolean): void; export declare function ɵɵelementHostStyleProp(styleIndex: number, value: string | number | String | PlayerFactory | null, suffix?: string | null, forceOverride?: boolean): void;
export declare function ɵɵelementHostStyling(classBindingNames?: string[] | null, styleBindingNames?: string[] | null, styleSanitizer?: StyleSanitizeFn | null): void; export declare function ɵɵelementHostStyling(classBindingNames?: string[] | null, styleBindingNames?: string[] | null, styleSanitizer?: StyleSanitizeFn | null): void;
export declare function ɵɵelementHostStylingApply(): void; export declare function ɵɵelementHostStylingApply(): void;
export declare function ɵɵelementHostStylingMap(classes: {
[key: string]: any;
} | string | NO_CHANGE | null, styles?: {
[styleName: string]: any;
} | NO_CHANGE | null): void;
export declare function ɵɵelementProperty<T>(index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null, nativeOnly?: boolean): void; export declare function ɵɵelementProperty<T>(index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null, nativeOnly?: boolean): void;
export declare function ɵɵelementStart(index: number, name: string, attrs?: TAttributes | null, localRefs?: string[] | null): void; export declare function ɵɵelementStart(index: number, name: string, attrs?: TAttributes | null, localRefs?: string[] | null): void;
export declare function ɵɵelementStyleMap(index: number, styles: {
[styleName: string]: any;
} | NO_CHANGE | null): void;
export declare function ɵɵelementStyleProp(index: number, styleIndex: number, value: string | number | String | PlayerFactory | null, suffix?: string | null, forceOverride?: boolean): void; export declare function ɵɵelementStyleProp(index: number, styleIndex: number, value: string | number | String | PlayerFactory | null, suffix?: string | null, forceOverride?: boolean): void;
export declare function ɵɵelementStyling(classBindingNames?: string[] | null, styleBindingNames?: string[] | null, styleSanitizer?: StyleSanitizeFn | null): void; export declare function ɵɵelementStyling(classBindingNames?: string[] | null, styleBindingNames?: string[] | null, styleSanitizer?: StyleSanitizeFn | null): void;
export declare function ɵɵelementStylingApply(index: number): void; export declare function ɵɵelementStylingApply(index: number): void;
export declare function ɵɵelementStylingMap(index: number, classes: {
[key: string]: any;
} | string | NO_CHANGE | null, styles?: {
[styleName: string]: any;
} | NO_CHANGE | null): void;
export declare function ɵɵembeddedViewEnd(): void; export declare function ɵɵembeddedViewEnd(): void;
export declare function ɵɵembeddedViewStart(viewBlockId: number, consts: number, vars: number): RenderFlags; export declare function ɵɵembeddedViewStart(viewBlockId: number, consts: number, vars: number): RenderFlags;