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
* 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';
@ -38,7 +38,7 @@ export const ngClassDirectiveDef__POST_R3__ = ɵɵdefineDirective({
ɵɵelementHostStyling();
}
if (rf & ɵRenderFlags.Update) {
ɵɵelementHostStylingMap(ctx.getValue());
ɵɵelementHostClassMap(ctx.getValue());
ɵɵelementHostStylingApply();
}
}

View File

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

View File

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

View File

@ -51,7 +51,9 @@ export class Identifiers {
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};
@ -62,8 +64,11 @@ export class Identifiers {
static elementHostStyling: o.ExternalReference = {name: 'ɵɵelementHostStyling', moduleName: CORE};
static elementHostStylingMap:
o.ExternalReference = {name: 'ɵɵelementHostStylingMap', moduleName: CORE};
static elementHostStyleMap:
o.ExternalReference = {name: 'ɵɵelementHostStyleMap', moduleName: CORE};
static elementHostClassMap:
o.ExternalReference = {name: 'ɵɵelementHostClassMap', moduleName: CORE};
static elementHostStyleProp:
o.ExternalReference = {name: 'ɵɵelementHostStyleProp', moduleName: CORE};

View File

@ -61,7 +61,8 @@ interface BoundStylingEntry {
* elementStyling(...)
* }
* if (updateMode) {
* elementStylingMap(...)
* elementStyleMap(...)
* elementClassMap(...)
* elementStyleProp(...)
* elementClassProp(...)
* 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
* which include the `[style]` and `[class]` expression params (if they exist) as well as
* the sanitizer and directive reference expression.
* The instruction data will contain all expressions for `elementClassMap` to function
* which includes the `[class]` expression params.
*/
buildElementStylingMapInstruction(valueConverter: ValueConverter): Instruction|null {
if (this._classMapInput || this._styleMapInput) {
const stylingInput = this._classMapInput ! || this._styleMapInput !;
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;
}
};
buildElementClassMapInstruction(valueConverter: ValueConverter): Instruction|null {
if (this._classMapInput) {
return this._buildMapBasedInstruction(valueConverter, true, this._classMapInput);
}
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(
reference: o.ExternalReference, isHostBinding: boolean, inputs: BoundStylingEntry[],
mapIndex: Map<string, number>, allowUnits: boolean,
@ -498,9 +493,13 @@ export class StylingBuilder {
buildUpdateLevelInstructions(valueConverter: ValueConverter) {
const instructions: Instruction[] = [];
if (this.hasBindings) {
const mapInstruction = this.buildElementStylingMapInstruction(valueConverter);
if (mapInstruction) {
instructions.push(mapInstruction);
const styleMapInstruction = this.buildElementStyleMapInstruction(valueConverter);
if (styleMapInstruction) {
instructions.push(styleMapInstruction);
}
const classMapInstruction = this.buildElementClassMapInstruction(valueConverter);
if (classMapInstruction) {
instructions.push(classMapInstruction);
}
instructions.push(...this._buildStyleInputs(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
// update block of the template function AOT code. Instructions like `elementStyleProp`,
// `elementStylingMap`, `elementClassProp` and `elementStylingApply` are all generated
// and assign in the code below.
// `elementStyleMap`, `elementClassMap`, `elementClassProp` and `elementStylingApply`
// are all generated and assigned in the code below.
stylingBuilder.buildUpdateLevelInstructions(this._valueConverter).forEach(instruction => {
this._bindingSlots += instruction.allocateBindingSlots;
this.processStylingInstruction(implicit, instruction, false);

View File

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

View File

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

View File

@ -11,7 +11,7 @@ import {TNode, TNodeType} from '../interfaces/node';
import {PlayerFactory} from '../interfaces/player';
import {FLAGS, HEADER_OFFSET, LView, LViewFlags, RENDERER, RootContextFlags} from '../interfaces/view';
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 {BoundPlayerFactory} from '../styling/player_factory';
import {DEFAULT_TEMPLATE_DIRECTIVE_INDEX} from '../styling/shared';
@ -34,14 +34,16 @@ import {scheduleTick, setInputsForProperty} from './shared';
*
* Template level styling instructions:
* - elementStyling
* - elementStylingMap
* - elementStyleMap
* - elementClassMap
* - elementStyleProp
* - elementClassProp
* - elementStylingApply
*
* Host bindings level styling instructions:
* - elementHostStyling
* - elementHostStylingMap
* - elementHostStyleMap
* - elementHostClassMap
* - elementHostStyleProp
* - elementHostClassProp
* - elementHostStylingApply
@ -147,7 +149,7 @@ function initElementStyling(
*
* 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
* 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`).
*
* 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
* (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`).
*
* 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
* bindings. When styles/classes are applied to the element they will then be updated with
* respect to any styles/classes set with `elementStyleProp` or `elementClassProp`. If any
* styles or classes are set to falsy then they will be removed from the element.
* This instruction is meant to apply styling via the `[style]="exp"` template bindings.
* When styles are applied to the element they will then be updated with respect to
* any styles/classes set via `elementStyleProp`. If any styles 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.
*
* @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.
*
@ -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
* 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.
* @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 ɵɵelementStylingMap(
index: number, classes: {[key: string]: any} | string | NO_CHANGE | null,
styles?: {[styleName: string]: any} | NO_CHANGE | null): void {
export function ɵɵelementClassMap(
index: number, classes: {[styleName: string]: any} | NO_CHANGE | string | 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)
@ -341,29 +376,46 @@ export function ɵɵelementStylingMap(
setInputsForProperty(lView, tNode.inputs !['class'] !, classInputVal);
classes = NO_CHANGE;
}
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);
updateClassMap(stylingContext, classes);
}
/**
* 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.
*
* This instruction is meant to apply styling via the `@HostBinding('style')` and
* `@HostBinding('class')` bindings for a component's or directive's host element.
* When styles/classes are applied to the host element they will then be updated
* with respect to any styles/classes set with `elementHostStyleProp` or
* `elementHostClassProp`. If any styles or classes are set to falsy then they
* will be removed from the element.
* This instruction is meant to apply styling via the `@HostBinding('style')`
* host bindings for a component's or directive's host element.
* When styles are applied to the host element they will then be updated
* with respect to any other styles set with `elementHostStyleProp`. If
* If any styles are set to falsy then they 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
* `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
* 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.
* @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 ɵɵelementHostStylingMap(
classes: {[key: string]: any} | string | NO_CHANGE | null,
styles?: {[styleName: string]: any} | NO_CHANGE | null): void {
export function ɵɵelementHostClassMap(classes: {[key: string]: any} | string | NO_CHANGE | null):
void {
const directiveStylingIndex = getActiveDirectiveStylingIndex();
const hostElementIndex = getSelectedIndex();
const stylingContext = getStylingContext(hostElementIndex, getLView());
const args: ParamsOf<typeof updateStylingMap> =
[stylingContext, classes, styles, directiveStylingIndex];
enqueueHostInstruction(stylingContext, directiveStylingIndex, updateStylingMap, args);
const args: ParamsOf<typeof updateClassMap> = [stylingContext, classes, directiveStylingIndex];
enqueueHostInstruction(stylingContext, directiveStylingIndex, updateClassMap, args);
}
/**
* Apply all style and class binding values to the element.
*
* This instruction is meant to be run after `elementStylingMap`, `elementStyleProp`
* or `elementClassProp` instructions have been run and will only apply styling to
* the element if any styling bindings have been updated.
* This instruction is meant to be run after `elementStyleMap`, `elementClassMap`,
* `elementStyleProp` or `elementClassProp` instructions have been run and will
* 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.
*
@ -407,10 +453,10 @@ export function ɵɵelementStylingApply(index: number): void {
/**
* Apply all style and class host binding values to the element.
*
* This instruction is meant to be run after `elementHostStylingMap`,
* `elementHostStyleProp` or `elementHostClassProp` instructions have
* been run and will only apply styling to the host element if any
* styling bindings have been updated.
* This instruction is meant to be run after both `elementHostStyleMap`
* `elementHostClassMap`, `elementHostStyleProp` or `elementHostClassProp`
* instructions have been run and will only apply styling to the host
* element if any styling bindings have been updated.
*
* @codeGenApi
*/

View File

@ -122,8 +122,8 @@ import {LView} from './view';
* values are and how they work.
*
* Each time a binding property is updated (whether it be through a single
* property instruction like `elementStyleProp`, `elementClassProp` or
* `elementStylingMap`) then the values in the context will be updated as
* property instruction like `elementStyleProp`, `elementClassProp`,
* `elementStyleMap` or `elementClassMap`) then the values in the context will be updated as
* well.
*
* If for example `[style.width]` updates to `555px` then its value will be reflected
@ -161,7 +161,8 @@ import {LView} from './view';
* - `elementStyling`
* - `elementStyleProp`
* - `elementClassProp`
* - `elementStylingMap`
* - `elementStyleMap`
* - `elementClassMap`
* - `elementStylingApply`
*
* 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;
/**
* 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.
*/
[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.
*/
[StylingIndex.CachedMultiStyles]: any|MapBasedOffsetValues;
@ -313,7 +314,7 @@ export interface StylingContext extends
* A queue of all hostStyling 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
* standard angular instructions, they are not designed to immediately apply
* their values to the styling context when executed. What happens instead is
@ -336,7 +337,7 @@ export interface StylingContext extends
* the styling is applied).
*
* 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).
*
* 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.
*
* 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
* uniqueId and the directive super-class inheritance depth. But for template
* 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,
'ɵɵreference': r3.ɵɵreference,
'ɵɵelementHostAttrs': r3.ɵɵelementHostAttrs,
'ɵɵelementClassMap': r3.ɵɵelementClassMap,
'ɵɵelementStyling': r3.ɵɵelementStyling,
'ɵɵelementStylingMap': r3.ɵɵelementStylingMap,
'ɵɵelementStyleMap': r3.ɵɵelementStyleMap,
'ɵɵelementStyleProp': r3.ɵɵelementStyleProp,
'ɵɵelementStylingApply': r3.ɵɵelementStylingApply,
'ɵɵelementClassProp': r3.ɵɵelementClassProp,
'ɵɵelementHostClassMap': r3.ɵɵelementHostClassMap,
'ɵɵelementHostStyling': r3.ɵɵelementHostStyling,
'ɵɵelementHostStylingMap': r3.ɵɵelementHostStylingMap,
'ɵɵelementHostStyleMap': r3.ɵɵelementHostStyleMap,
'ɵɵelementHostStyleProp': r3.ɵɵelementHostStyleProp,
'ɵɵelementHostStylingApply': r3.ɵɵelementHostStylingApply,
'ɵɵ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
* values and insert/update or remove them from the context at exactly the right
* spot.
* This function will iterate over the provided `classesInput` 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
* on the context. When there are styles that conflict (e.g. say `ngClass` and `[class]`
* 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
* 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
@ -509,100 +508,108 @@ function getMatchingBindingIndex(
* @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.
*/
export function updateStylingMap(
export function updateClassMap(
context: StylingContext, classesInput: {[key: string]: any} | string |
BoundPlayerFactory<null|string|{[key: string]: any}>| null,
stylesInput?: {[key: string]: any} | BoundPlayerFactory<null|{[key: string]: any}>| null,
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);
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)
if (ignoreAllClassUpdates && ignoreAllStyleUpdates) return;
if (isMultiValueCacheHit(context, entryIsClassBased, directiveIndex, input)) return;
classesInput =
classesInput === NO_CHANGE ? readCachedMapValue(context, true, directiveIndex) : classesInput;
stylesInput =
stylesInput === NO_CHANGE ? readCachedMapValue(context, false, directiveIndex) : stylesInput;
input =
input === NO_CHANGE ? readCachedMapValue(context, entryIsClassBased, directiveIndex) : input;
const element = context[StylingIndex.ElementPosition] !as HTMLElement;
const classesPlayerBuilder = classesInput instanceof BoundPlayerFactory ?
new ClassAndStylePlayerBuilder(classesInput as any, element, BindingType.Class) :
null;
const stylesPlayerBuilder = stylesInput instanceof BoundPlayerFactory ?
new ClassAndStylePlayerBuilder(stylesInput as any, element, BindingType.Style) :
const playerBuilder = input instanceof BoundPlayerFactory ?
new ClassAndStylePlayerBuilder(
input as any, element, entryIsClassBased ? BindingType.Class : BindingType.Style) :
null;
const classesValue = classesPlayerBuilder ?
(classesInput as BoundPlayerFactory<{[key: string]: any}|string>) !.value :
classesInput;
const stylesValue = stylesPlayerBuilder ? stylesInput !['value'] : stylesInput;
const rawValue =
playerBuilder ? (input as BoundPlayerFactory<{[key: string]: any}|string>) !.value : input;
let classNames: string[] = EMPTY_ARRAY;
let applyAllClasses = false;
// the position is always the same, but whether the player builder gets set
// 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;
const classesPlayerBuilderIndex =
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);
if (hasPlayerBuilderChanged(context, playerBuilder, playerBuilderPosition)) {
setPlayerBuilder(context, playerBuilder, playerBuilderPosition);
playerBuildersAreDirty = true;
}
// each time a string-based value pops up then it shouldn't require a deep
// check of what's changed.
if (!ignoreAllClassUpdates) {
if (typeof classesValue == 'string') {
classNames = classesValue.split(/\s+/);
let startIndex: number;
let endIndex: number;
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
// since a classname string implies that all those classes are added
applyAllClasses = true;
// since a className string implies that all those classes are added
applyAll = true;
} 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);
let multiClassesStartIndex = getMultiClassesStartIndex(context);
let multiClassesEndIndex = context.length;
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);
}
const values = (rawValue || EMPTY_OBJ) as{[key: string]: any};
patchStylingMapIntoContext(
context, directiveIndex, playerBuilderIndex, startIndex, endIndex, propNames,
applyAll || values, input, entryIsClassBased);
if (playerBuildersAreDirty) {
setContextPlayersDirty(context, true);
}
ngDevMode && ngDevMode.stylingMapCacheMiss++;
ngDevMode && (entryIsClassBased ? ngDevMode.classMapCacheMiss++ : ngDevMode.styleMapCacheMiss++);
}
/**

View File

@ -9,7 +9,7 @@
/**
* 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
* uniqueId and the directive super-class inheritance depth. But for template
* bindings they always have the same directive styling index value.

View File

@ -31,8 +31,10 @@ declare global {
rendererMoveNode: number;
rendererRemoveNode: number;
rendererCreateComment: number;
stylingMap: number;
stylingMapCacheMiss: number;
styleMap: number;
styleMapCacheMiss: number;
classMap: number;
classMapCacheMiss: number;
stylingProp: number;
stylingPropCacheMiss: number;
stylingApply: number;
@ -62,8 +64,10 @@ export function ngDevModeResetPerfCounters(): NgDevModePerfCounters {
rendererMoveNode: 0,
rendererRemoveNode: 0,
rendererCreateComment: 0,
stylingMap: 0,
stylingMapCacheMiss: 0,
styleMap: 0,
styleMapCacheMiss: 0,
classMap: 0,
classMapCacheMiss: 0,
stylingProp: 0,
stylingPropCacheMiss: 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 {ɵɵ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 {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl} from '../../src/sanitization/bypass';
import {ɵɵdefaultStyleSanitizer, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl} from '../../src/sanitization/sanitization';
@ -1063,7 +1063,7 @@ describe('instructions', () => {
it('should add style', () => {
const fixture = new TemplateFixture(createDivWithStyle, () => {}, 1);
fixture.update(() => {
ɵɵelementStylingMap(0, null, {'background-color': 'red'});
ɵɵelementStyleMap(0, {'background-color': 'red'});
ɵɵelementStylingApply(0);
});
expect(fixture.html).toEqual('<div style="background-color: red; height: 10px;"></div>');
@ -1078,7 +1078,7 @@ describe('instructions', () => {
sanitizerInterceptor);
fixture.update(() => {
ɵɵelementStylingMap(0, null, {
ɵɵelementStyleMap(0, {
'background-image': 'background-image',
'background': 'background',
'border-image': 'border-image',
@ -1107,7 +1107,7 @@ describe('instructions', () => {
it('should add class', () => {
const fixture = new TemplateFixture(createDivWithStyling, () => {}, 1);
fixture.update(() => {
ɵɵelementStylingMap(0, 'multiple classes');
ɵɵelementClassMap(0, 'multiple classes');
ɵɵelementStylingApply(0);
});
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 {getLContext} from '../../src/render3/context_discovery';
import {AttributeMarker, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵ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 {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, ɵɵ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 {RenderFlags} from '../../src/render3/interfaces/definition';
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer';
@ -1727,7 +1727,7 @@ describe('render3 integration test', () => {
ɵɵelementEnd();
}
if (rf & RenderFlags.Update) {
ɵɵelementStylingMap(0, 'cucumber grape');
ɵɵelementClassMap(0, 'cucumber grape');
ɵɵelementStylingApply(0);
}
}, 1, 0, [DirWithClassDirective]);
@ -1748,7 +1748,7 @@ describe('render3 integration test', () => {
ɵɵelementEnd();
}
if (rf & RenderFlags.Update) {
ɵɵelementStylingMap(0, null, {width: '200px', height: '500px'});
ɵɵelementStyleMap(0, {width: '200px', height: '500px'});
ɵɵelementStylingApply(0);
}
}, 1, 0, [DirWithStyleDirective]);
@ -1993,7 +1993,8 @@ describe('render3 integration test', () => {
ɵɵelementHostStyling();
}
if (rf & RenderFlags.Update) {
ɵɵelementHostStylingMap(ctx.classesExp, ctx.stylesExp);
ɵɵelementHostStyleMap(ctx.stylesExp);
ɵɵelementHostClassMap(ctx.classesExp);
ɵɵelementHostStylingApply();
}
}
@ -2020,7 +2021,7 @@ describe('render3 integration test', () => {
ɵɵelementHostStyling();
}
if (rf & RenderFlags.Update) {
ɵɵelementHostStylingMap(null, ctx.stylesExp);
ɵɵelementHostStyleMap(ctx.stylesExp);
ɵɵelementHostStylingApply();
}
}
@ -2039,7 +2040,8 @@ describe('render3 integration test', () => {
ɵɵelementStyling();
}
if (rf & RenderFlags.Update) {
ɵɵelementStylingMap(0, ctx.classesExp, ctx.stylesExp);
ɵɵelementStyleMap(0, ctx.stylesExp);
ɵɵelementClassMap(0, ctx.classesExp);
ɵɵelementStylingApply(0);
}
}, 1, 0, [Dir1WithStyling, Dir2WithStyling]);
@ -2116,7 +2118,7 @@ describe('render3 integration test', () => {
ɵɵelementEnd();
}
if (rf & RenderFlags.Update) {
ɵɵelementStylingMap(0, ɵɵinterpolation2('-', ctx.name, '-', ctx.age, '-'));
ɵɵelementClassMap(0, ɵɵinterpolation2('-', ctx.name, '-', ctx.age, '-'));
ɵɵelementStylingApply(0);
}
}, 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 {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 {AttributeMarker, TAttributes} from '../../../src/render3/interfaces/node';
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 {CONTEXT, LView, LViewFlags, RootContext} from '../../../src/render3/interfaces/view';
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 {registerHostDirective} from '../../../src/render3/styling/host_instructions_queue';
import {BoundPlayerFactory, bindPlayerFactory} from '../../../src/render3/styling/player_factory';
@ -32,6 +32,13 @@ describe('style and class based bindings', () => {
let element: RElement|null = null;
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 {
const rootContext =
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) {
updateStylingMap(context, classes, null);
updateStyleAndClassMaps(context, classes, 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); }
@ -389,7 +396,7 @@ describe('style and class based bindings', () => {
ɵɵelementEnd();
}
if (rf & RenderFlags.Update) {
ɵɵelementStylingMap(0, null, ctx.myStyles);
ɵɵelementStyleMap(0, ctx.myStyles);
ɵɵelementStyleProp(0, 0, ctx.myWidth);
ɵɵelementStylingApply(0);
}
@ -1470,9 +1477,9 @@ describe('style and class based bindings', () => {
const ctx = allocStylingContext(element, template);
let s1, s2, s3;
updateStylingMap(ctx, null, s1 = {width: '100px', height: '99px'}, dir1);
updateStylingMap(ctx, null, s2 = {width: '200px', opacity: '0.5'}, dir2);
updateStylingMap(ctx, null, s3 = {width: '300px', height: '999px'}, dir3);
updateStyleAndClassMaps(ctx, null, s1 = {width: '100px', height: '99px'}, dir1);
updateStyleAndClassMaps(ctx, null, s2 = {width: '200px', opacity: '0.5'}, dir2);
updateStyleAndClassMaps(ctx, null, s3 = {width: '300px', height: '999px'}, dir3);
expect(ctx[StylingIndex.CachedMultiStyles]).toEqual([
3,
@ -1526,9 +1533,9 @@ describe('style and class based bindings', () => {
2,
]);
updateStylingMap(ctx, null, {opacity: '0', width: null}, dir1);
updateStylingMap(ctx, null, {width: '200px', opacity: '0.5'}, dir2);
updateStylingMap(ctx, null, {width: '300px', height: '999px'}, dir3);
updateStyleAndClassMaps(ctx, null, {opacity: '0', width: null}, dir1);
updateStyleAndClassMaps(ctx, null, {width: '200px', opacity: '0.5'}, dir2);
updateStyleAndClassMaps(ctx, null, {width: '300px', height: '999px'}, dir3);
assertContextOnlyValues(ctx, [
// #10
@ -1562,9 +1569,10 @@ describe('style and class based bindings', () => {
3,
]);
updateStylingMap(ctx, null, null, dir1);
updateStylingMap(ctx, null, {width: '500px', opacity: '0.2'}, dir2);
updateStylingMap(ctx, null, {width: '300px', height: '999px', color: 'red'}, dir3);
updateStyleAndClassMaps(ctx, null, null, dir1);
updateStyleAndClassMaps(ctx, null, {width: '500px', opacity: '0.2'}, dir2);
updateStyleAndClassMaps(
ctx, null, {width: '300px', height: '999px', color: 'red'}, dir3);
assertContextOnlyValues(ctx, [
// #10
@ -1619,9 +1627,9 @@ describe('style and class based bindings', () => {
const ctx = allocStylingContext(element, template);
let c1, c2, c3;
updateStylingMap(ctx, c1 = {red: true, orange: true}, null, dir1);
updateStylingMap(ctx, c2 = 'black red', null, dir2);
updateStylingMap(ctx, c3 = 'silver green', null, dir3);
updateStyleAndClassMaps(ctx, c1 = {red: true, orange: true}, null, dir1);
updateStyleAndClassMaps(ctx, c2 = 'black red', null, dir2);
updateStyleAndClassMaps(ctx, c3 = 'silver green', null, dir3);
expect(ctx[StylingIndex.CachedMultiClasses]).toEqual([
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,
]);
updateStylingMap(ctx, c1 = {orange: true}, null, dir1);
updateStylingMap(ctx, c2 = 'black red', null, dir2);
updateStylingMap(ctx, c3 = 'green', null, dir3);
updateStyleAndClassMaps(ctx, c1 = {orange: true}, null, dir1);
updateStyleAndClassMaps(ctx, c2 = 'black red', null, dir2);
updateStyleAndClassMaps(ctx, c3 = 'green', null, dir3);
assertContextOnlyValues(ctx, [
// #10
@ -1719,9 +1727,9 @@ describe('style and class based bindings', () => {
1,
]);
updateStylingMap(ctx, c1 = 'green', null, dir1);
updateStylingMap(ctx, c2 = null, null, dir2);
updateStylingMap(ctx, c3 = 'red', null, dir3);
updateStyleAndClassMaps(ctx, c1 = 'green', null, dir1);
updateStyleAndClassMaps(ctx, c2 = null, null, dir2);
updateStyleAndClassMaps(ctx, c3 = 'red', null, dir3);
assertContextOnlyValues(ctx, [
// #10
@ -1848,7 +1856,7 @@ describe('style and class based bindings', () => {
const foreignDir = 2;
expect(() => {
updateStylingMap(ctx, 'foo', null, foreignDir);
updateStyleAndClassMaps(ctx, 'foo', null, foreignDir);
}).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': ''};
updateStyleProp(stylingContext, 0, '');
updateStylingMap(stylingContext, null, styles);
updateStyleAndClassMaps(stylingContext, null, styles);
getStyles(stylingContext);
expect(store.getValues()).toEqual({'font-size': '', 'color': ''});
@ -1871,7 +1879,7 @@ describe('style and class based bindings', () => {
registerHostDirective(stylingContext, otherDirective);
updateStyleProp(stylingContext, 0, 'red');
updateStylingMap(stylingContext, null, styles = {'font-size': '20px'});
updateStyleAndClassMaps(stylingContext, null, styles = {'font-size': '20px'});
getStyles(stylingContext);
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'});
updateStyleProp(stylingContext, 0, '');
updateStylingMap(stylingContext, null, styles = {});
updateStyleAndClassMaps(stylingContext, null, styles = {});
getStyles(stylingContext, otherDirective);
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']);
expect(getClasses(stylingContext)).toEqual({});
updateStylingMap(stylingContext, 'foo bar baz');
updateStyleAndClassMaps(stylingContext, 'foo bar baz');
expect(getClasses(stylingContext)).toEqual({'foo': true, 'bar': true, 'baz': true});
updateStylingMap(stylingContext, 'foo car');
updateStyleAndClassMaps(stylingContext, 'foo car');
updateClassProp(stylingContext, 0, true);
expect(getClasses(stylingContext))
.toEqual({'foo': true, 'car': true, 'bar': false, 'baz': true});
@ -2036,7 +2044,7 @@ describe('style and class based bindings', () => {
expect(getStylesAndClasses(stylingContext)).toEqual([{}, {}]);
let cachedStyleMap: any = {width: '200px', opacity: '0.5'};
updateStylingMap(stylingContext, 'tall round', cachedStyleMap);
updateStyleAndClassMaps(stylingContext, 'tall round', cachedStyleMap);
assertContext(stylingContext, [
element,
masterConfig(26, true), //
@ -2117,7 +2125,7 @@ describe('style and class based bindings', () => {
let cachedClassMap = {tall: true, wide: true};
cachedStyleMap = {width: '500px'};
updateStylingMap(stylingContext, cachedClassMap, cachedStyleMap);
updateStyleAndClassMaps(stylingContext, cachedClassMap, cachedStyleMap);
updateStyleProp(stylingContext, 0, '300px');
assertContext(stylingContext, [
@ -2198,7 +2206,7 @@ describe('style and class based bindings', () => {
{width: '300px', opacity: null},
]);
updateStylingMap(stylingContext, {wide: false});
updateStyleAndClassMaps(stylingContext, {wide: false});
expect(getStylesAndClasses(stylingContext)).toEqual([
{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 classesMap = {foo: true};
updateStylingMap(stylingContext, classesMap, stylesMap);
updateStyleAndClassMaps(stylingContext, classesMap, stylesMap);
// apply the styles
getStylesAndClasses(stylingContext);
@ -2239,7 +2247,7 @@ describe('style and class based bindings', () => {
stylesMap.width = '300px';
classesMap.foo = false;
updateStylingMap(stylingContext, classesMap, stylesMap);
updateStyleAndClassMaps(stylingContext, classesMap, stylesMap);
// apply the styles
getStylesAndClasses(stylingContext);
@ -2269,7 +2277,7 @@ describe('style and class based bindings', () => {
const getClasses = trackClassesFactory();
const classes = 'apple orange banana';
updateStylingMap(stylingContext, classes);
updateStyleAndClassMaps(stylingContext, classes);
// apply the styles
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[18 + StylingIndex.ValueOffset] = false;
updateStylingMap(stylingContext, classes);
updateStyleAndClassMaps(stylingContext, classes);
// apply the styles
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};
updateClassProp(stylingContext, 0, false);
updateStylingMap(stylingContext, classes);
updateStyleAndClassMaps(stylingContext, classes);
// apply the styles
getClasses(stylingContext, true);
@ -2328,14 +2336,14 @@ describe('style and class based bindings', () => {
classes = {red: true};
updateClassProp(stylingContext, 0, true);
updateStylingMap(stylingContext, classes);
updateStyleAndClassMaps(stylingContext, classes);
getClasses(stylingContext);
expect(store.getValues()).toEqual({red: true, blue: true});
classes = {red: false};
updateClassProp(stylingContext, 0, false);
updateStylingMap(stylingContext, classes);
updateStyleAndClassMaps(stylingContext, classes);
getClasses(stylingContext);
expect(store.getValues()).toEqual({red: false, blue: false});
@ -2367,7 +2375,7 @@ describe('style and class based bindings', () => {
},
styles);
updateStylingMap(context, classFactory, styleFactory);
updateStyleAndClassMaps(context, classFactory, styleFactory);
expect(classResult).toBeFalsy();
renderStyles(context);
@ -2392,15 +2400,15 @@ describe('style and class based bindings', () => {
return new MockPlayer();
};
updateStylingMap(context, null, bindPlayerFactory(buildFn, {width: '100px'}));
updateStyleAndClassMaps(context, null, bindPlayerFactory(buildFn, {width: '100px'}));
renderStyles(context);
expect(count).toEqual(1);
updateStylingMap(context, null, bindPlayerFactory(buildFn, {height: '100px'}));
updateStyleAndClassMaps(context, null, bindPlayerFactory(buildFn, {height: '100px'}));
renderStyles(context);
expect(count).toEqual(2);
updateStylingMap(
updateStyleAndClassMaps(
context, null, bindPlayerFactory(buildFn, {height: '200px', width: '200px'}));
renderStyles(context);
expect(count).toEqual(3);
@ -2415,15 +2423,16 @@ describe('style and class based bindings', () => {
return new MockPlayer();
};
updateStylingMap(context, bindPlayerFactory(buildFn, {myClass: true}));
updateStyleAndClassMaps(context, bindPlayerFactory(buildFn, {myClass: true}));
renderStyles(context);
expect(count).toEqual(1);
updateStylingMap(context, bindPlayerFactory(buildFn, {otherClass: true}));
updateStyleAndClassMaps(context, bindPlayerFactory(buildFn, {otherClass: true}));
renderStyles(context);
expect(count).toEqual(2);
updateStylingMap(context, bindPlayerFactory(buildFn, {myClass: false, otherClass: false}));
updateStyleAndClassMaps(
context, bindPlayerFactory(buildFn, {myClass: false, otherClass: false}));
renderStyles(context);
expect(count).toEqual(3);
});
@ -2452,7 +2461,7 @@ describe('style and class based bindings', () => {
const classPlayerBuilder =
new ClassAndStylePlayerBuilder(classFactory, element as HTMLElement, BindingType.Class);
updateStylingMap(context, classFactory, styleFactory);
updateStyleAndClassMaps(context, classFactory, styleFactory);
expect(context[StylingIndex.PlayerContext]).toEqual([
5, classPlayerBuilder, null, stylePlayerBuilder, null
]);
@ -2518,7 +2527,7 @@ describe('style and class based bindings', () => {
const classMapPlayerBuilder = new ClassAndStylePlayerBuilder(
classMapFactory, element as HTMLElement, BindingType.Class);
updateStylingMap(context, classMapFactory, styleMapFactory);
updateStyleAndClassMaps(context, classMapFactory, styleMapFactory);
const widthFactory = bindPlayerFactory(styleBuildFn, '100px');
const barFactory = bindPlayerFactory(classBuildFn, true);
@ -2604,7 +2613,7 @@ describe('style and class based bindings', () => {
expect(context[StylingIndex.PlayerContext]).toEqual(null);
let mapFactory = bindPlayerFactory(buildFn, {width: '200px'});
updateStylingMap(context, null, mapFactory);
updateStyleAndClassMaps(context, null, mapFactory);
renderStyles(context, false, undefined, lView);
expect(players.length).toEqual(1);
@ -2612,7 +2621,7 @@ describe('style and class based bindings', () => {
expect(p1.state).toEqual(PlayState.Pending);
mapFactory = bindPlayerFactory(buildFn, {width: '100px'});
updateStylingMap(context, null, mapFactory);
updateStyleAndClassMaps(context, null, mapFactory);
renderStyles(context, false, undefined, lView);
expect(players.length).toEqual(1);
@ -2684,7 +2693,7 @@ describe('style and class based bindings', () => {
const classMapWithPlayerFactory = bindPlayerFactory(buildClassFn, cachedClassMap);
const styleMapPlayerBuilder = makePlayerBuilder(styleMapWithPlayerFactory, false);
const classMapPlayerBuilder = makePlayerBuilder(classMapWithPlayerFactory, true);
updateStylingMap(context, classMapWithPlayerFactory, styleMapWithPlayerFactory);
updateStyleAndClassMaps(context, classMapWithPlayerFactory, styleMapWithPlayerFactory);
const colorWithPlayerFactory = bindPlayerFactory(buildStyleFn, 'red');
const fooWithPlayerFactory = bindPlayerFactory(buildClassFn, true);
@ -2754,7 +2763,7 @@ describe('style and class based bindings', () => {
0,
]);
updateStylingMap(context, cachedClassMap, cachedStyleMap);
updateStyleAndClassMaps(context, cachedClassMap, cachedStyleMap);
const colorWithoutPlayerFactory = 'blue';
const fooWithoutPlayerFactory = false;
@ -2836,7 +2845,7 @@ describe('style and class based bindings', () => {
let styleFactory = bindPlayerFactory(buildStyleFn, {opacity: '1'}) as BoundPlayerFactory<any>;
let classFactory = bindPlayerFactory(buildClassFn, 'bar') as BoundPlayerFactory<any>;
updateStylingMap(context, classFactory, styleFactory);
updateStyleAndClassMaps(context, classFactory, styleFactory);
expect(styleCalls).toEqual(0);
expect(classCalls).toEqual(0);
@ -2849,18 +2858,18 @@ describe('style and class based bindings', () => {
expect(classCalls).toEqual(1);
styleFactory = bindPlayerFactory(buildStyleFn, {opacity: '0.5'}) as BoundPlayerFactory<any>;
updateStylingMap(context, classFactory, styleFactory);
updateStyleAndClassMaps(context, classFactory, styleFactory);
renderStyles(context, false, undefined, lView);
expect(styleCalls).toEqual(2);
expect(classCalls).toEqual(1);
classFactory = bindPlayerFactory(buildClassFn, 'foo') as BoundPlayerFactory<any>;
updateStylingMap(context, classFactory, styleFactory);
updateStyleAndClassMaps(context, classFactory, styleFactory);
renderStyles(context, false, undefined, lView);
expect(styleCalls).toEqual(2);
expect(classCalls).toEqual(2);
updateStylingMap(context, 'foo', {opacity: '0.5'});
updateStyleAndClassMaps(context, 'foo', {opacity: '0.5'});
renderStyles(context, false, undefined, lView);
expect(styleCalls).toEqual(2);
expect(classCalls).toEqual(2);
@ -2883,7 +2892,7 @@ describe('style and class based bindings', () => {
};
const mapFactory = bindPlayerFactory(mapBuildFn, {color: 'black'});
updateStylingMap(context, null, mapFactory);
updateStyleAndClassMaps(context, null, mapFactory);
updateStyleProp(context, 0, 'green');
renderStyles(context, false, undefined, lView);
@ -2907,7 +2916,7 @@ describe('style and class based bindings', () => {
propPlayer = styleMapPlayer = null;
updateStylingMap(context, null, null);
updateStyleAndClassMaps(context, null, null);
renderStyles(context, false, undefined, lView);
expect(propPlayer).toBeFalsy();
@ -2929,7 +2938,7 @@ describe('style and class based bindings', () => {
};
let factory = bindPlayerFactory<{[key: string]: any}>(buildFn, {width: '200px'});
updateStylingMap(context, null, factory);
updateStyleAndClassMaps(context, null, factory);
renderStyles(context, false, undefined, lView);
expect(previousPlayer).toEqual(null);
@ -2937,7 +2946,7 @@ describe('style and class based bindings', () => {
factory = bindPlayerFactory(buildFn, {height: '200px'});
updateStylingMap(context, null, factory);
updateStyleAndClassMaps(context, null, factory);
renderStyles(context, false, undefined, lView);
expect(previousPlayer !.value).toEqual({width: '200px'});
@ -2959,7 +2968,7 @@ describe('style and class based bindings', () => {
};
let factory = bindPlayerFactory<any>(buildFn, {foo: true});
updateStylingMap(context, null, factory);
updateStyleAndClassMaps(context, null, factory);
renderStyles(context, false, undefined, lView);
expect(currentPlayer).toBeTruthy();
@ -2969,7 +2978,7 @@ describe('style and class based bindings', () => {
previousPlayer = currentPlayer = null;
factory = bindPlayerFactory(buildFn, {bar: true});
updateStylingMap(context, null, factory);
updateStyleAndClassMaps(context, null, factory);
renderStyles(context, false, undefined, lView);
expect(currentPlayer).toBeTruthy();
@ -3000,13 +3009,13 @@ describe('style and class based bindings', () => {
let factory = bindPlayerFactory<{[key: string]: any}>(
buildFn, {width: '200px', height: '100px', opacity: '1'});
updateStylingMap(context, null, factory);
updateStyleAndClassMaps(context, null, factory);
renderStyles(context, false, undefined, lView);
expect(values !).toEqual({width: '200px-safe!', height: '100px-safe!', opacity: '1'});
factory = bindPlayerFactory(buildFn, {width: 'auto'});
updateStylingMap(context, null, factory);
updateStyleAndClassMaps(context, null, factory);
renderStyles(context, false, undefined, lView);
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 classMapFactory = bindPlayerFactory(classBuildFn, {map: true});
updateStylingMap(context, classMapFactory, styleMapFactory);
updateStyleAndClassMaps(context, classMapFactory, styleMapFactory);
updateStyleProp(context, 0, bindPlayerFactory(styleBuildFn, '100px') as any);
updateClassProp(context, 0, bindPlayerFactory(classBuildFn, true) as any);
updateClassProp(context, 1, bindPlayerFactory(classBuildFn, true) as any);
renderStyles(context, false, undefined, lView);
handler.flushPlayers();
expect(players.length).toEqual(5);
const [p1, p2, p3, p4, p5] = players;
expect(p1.state).toEqual(PlayState.Running);
expect(p2.state).toEqual(PlayState.Running);
expect(p3.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');
updateClassProp(context, 0, false);
expect(p1.state).toEqual(PlayState.Running);
@ -3100,7 +3110,8 @@ describe('style and class based bindings', () => {
ɵɵelementEnd();
}
if (rf & RenderFlags.Update) {
ɵɵelementStylingMap(0, classMapFactory, styleMapFactory);
ɵɵelementStyleMap(0, styleMapFactory);
ɵɵelementClassMap(0, classMapFactory);
ɵɵelementStyleProp(0, 0, widthFactory);
ɵɵelementClassProp(0, 0, fooFactory);
ɵɵelementStylingApply(0);
@ -3173,7 +3184,8 @@ describe('style and class based bindings', () => {
ɵɵelementEnd();
}
if (rf & RenderFlags.Update) {
ɵɵelementStylingMap(0, classMapFactory, styleMapFactory);
ɵɵelementStyleMap(0, styleMapFactory);
ɵɵelementClassMap(0, classMapFactory);
ɵɵelementStyleProp(0, 0, widthFactory);
ɵɵelementClassProp(0, 0, fooFactory);
ɵɵ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 ɵɵ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 ɵɵelementContainerEnd(): void;
@ -805,36 +809,36 @@ export declare function ɵɵelementEnd(): 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 ɵɵ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 ɵɵelementHostStyling(classBindingNames?: string[] | null, styleBindingNames?: string[] | null, styleSanitizer?: StyleSanitizeFn | null): 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 ɵɵ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 ɵɵelementStyling(classBindingNames?: string[] | null, styleBindingNames?: string[] | null, styleSanitizer?: StyleSanitizeFn | null): 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 ɵɵembeddedViewStart(viewBlockId: number, consts: number, vars: number): RenderFlags;