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:

committed by
Kara Erickson

parent
98a38ec98b
commit
be8fbac942
@ -111,14 +111,16 @@ export {
|
||||
ɵɵelementContainerStart,
|
||||
ɵɵelementContainerEnd,
|
||||
ɵɵelementStyling,
|
||||
ɵɵelementStylingMap,
|
||||
ɵɵelementStyleMap,
|
||||
ɵɵelementClassMap,
|
||||
ɵɵelementStyleProp,
|
||||
ɵɵelementStylingApply,
|
||||
ɵɵelementClassProp,
|
||||
|
||||
ɵɵelementHostAttrs,
|
||||
ɵɵelementHostClassMap,
|
||||
ɵɵelementHostStyleMap,
|
||||
ɵɵelementHostStyling,
|
||||
ɵɵelementHostStylingMap,
|
||||
ɵɵelementHostStyleProp,
|
||||
ɵɵelementHostClassProp,
|
||||
ɵɵelementHostStylingApply,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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++);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1918,4 +1925,4 @@ function assertValidDirectiveIndex(context: StylingContext, directiveIndex: numb
|
||||
dirs[index + DirectiveRegistryValuesIndex.SinglePropValuesIndexOffset] === -1) {
|
||||
throw new Error('The provided directive is not registered with the styling context');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
Reference in New Issue
Block a user