Prior to this fix, whenever a style or class binding is present, the binding application process would require an instance of `TStylingContext` to be built regardless of whether or not any binding resolution is needed (just so that it knows whether or not there are any collisions). This check is, however, unnecessary because if (and only if) there are directives present on the element then are collisions possible. This patch removes the need for style/class bindings to register themselves on to a `TStylingContext` if there are no directives and present on an element. This means that all map and prop-based style/class bindings are applied as soon as bindings are updated on an element. PR Close #32919
140 lines
4.7 KiB
TypeScript
140 lines
4.7 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
*
|
|
* 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 {RElement} from '../interfaces/renderer';
|
|
import {StylingMapArray} from '../interfaces/styling';
|
|
import {TEMPLATE_DIRECTIVE_INDEX} from '../util/styling_utils';
|
|
|
|
/**
|
|
* --------
|
|
*
|
|
* This file contains all state-based logic for styling in Angular.
|
|
*
|
|
* Styling in Angular is evaluated with a series of styling-specific
|
|
* template instructions which are called one after another each time
|
|
* change detection occurs in Angular.
|
|
*
|
|
* Styling makes use of various temporary, state-based variables between
|
|
* instructions so that it can better cache and optimize its values.
|
|
* These values are usually populated and cleared when an element is
|
|
* exited in change detection (once all the instructions are run for
|
|
* that element).
|
|
*
|
|
* To learn more about the algorithm see `TStylingContext`.
|
|
*
|
|
* --------
|
|
*/
|
|
|
|
/**
|
|
* Used as a state reference for update values between style/class binding instructions.
|
|
*
|
|
* In addition to storing the element and bit-mask related values, the state also
|
|
* stores the `sourceIndex` value. The `sourceIndex` value is an incremented value
|
|
* that identifies what "source" (i.e. the template, a specific directive by index or
|
|
* component) is currently applying its styling bindings to the element.
|
|
*/
|
|
export interface StylingState {
|
|
/** The element that is currently being processed */
|
|
element: RElement|null;
|
|
|
|
/** The directive index that is currently active (`0` === template) */
|
|
directiveIndex: number;
|
|
|
|
/** The source (column) index that is currently active (`0` === template) */
|
|
sourceIndex: number;
|
|
|
|
/** The classes update bit mask value that is processed during each class binding */
|
|
classesBitMask: number;
|
|
|
|
/** The classes update bit index value that is processed during each class binding */
|
|
classesIndex: number;
|
|
|
|
/** The styles update bit mask value that is processed during each style binding */
|
|
stylesBitMask: number;
|
|
|
|
/** The styles update bit index value that is processed during each style binding */
|
|
stylesIndex: number;
|
|
|
|
/**
|
|
* The last class map that was applied (i.e. `[class]="x"`).
|
|
*
|
|
* Note that this property is only populated when direct class values are applied
|
|
* (i.e. context resolution is not used).
|
|
*
|
|
* See `allowDirectStyling` for more info.
|
|
*/
|
|
lastDirectClassMap: StylingMapArray|null;
|
|
|
|
/**
|
|
* The last style map that was applied (i.e. `[style]="x"`)
|
|
*
|
|
* Note that this property is only populated when direct style values are applied
|
|
* (i.e. context resolution is not used).
|
|
*
|
|
* See `allowDirectStyling` for more info.
|
|
*/
|
|
lastDirectStyleMap: StylingMapArray|null;
|
|
}
|
|
|
|
// these values will get filled in the very first time this is accessed...
|
|
const _state: StylingState = {
|
|
element: null,
|
|
directiveIndex: -1,
|
|
sourceIndex: -1,
|
|
classesBitMask: -1,
|
|
classesIndex: -1,
|
|
stylesBitMask: -1,
|
|
stylesIndex: -1,
|
|
lastDirectClassMap: null,
|
|
lastDirectStyleMap: null,
|
|
};
|
|
|
|
const BIT_MASK_START_VALUE = 0;
|
|
|
|
// the `0` start value is reserved for [map]-based entries
|
|
const INDEX_START_VALUE = 1;
|
|
|
|
/**
|
|
* Returns (or instantiates) the styling state for the given element.
|
|
*
|
|
* Styling state is accessed and processed each time a style or class binding
|
|
* is evaluated.
|
|
*
|
|
* If and when the provided `element` doesn't match the current element in the
|
|
* state then this means that styling was recently cleared or the element has
|
|
* changed in change detection. In both cases the styling state is fully reset.
|
|
*
|
|
* If and when the provided `directiveIndex` doesn't match the current directive
|
|
* index in the state then this means that a new source has introduced itself into
|
|
* the styling code (or, in other words, another directive or component has started
|
|
* to apply its styling host bindings to the element).
|
|
*/
|
|
export function getStylingState(element: RElement, directiveIndex: number): StylingState {
|
|
if (_state.element !== element) {
|
|
_state.element = element;
|
|
_state.directiveIndex = directiveIndex;
|
|
_state.sourceIndex = directiveIndex === TEMPLATE_DIRECTIVE_INDEX ? 0 : 1;
|
|
_state.classesBitMask = BIT_MASK_START_VALUE;
|
|
_state.classesIndex = INDEX_START_VALUE;
|
|
_state.stylesBitMask = BIT_MASK_START_VALUE;
|
|
_state.stylesIndex = INDEX_START_VALUE;
|
|
_state.lastDirectClassMap = null;
|
|
_state.lastDirectStyleMap = null;
|
|
} else if (_state.directiveIndex !== directiveIndex) {
|
|
_state.directiveIndex = directiveIndex;
|
|
_state.sourceIndex++;
|
|
}
|
|
return _state;
|
|
}
|
|
|
|
/**
|
|
* Clears the styling state so that it can be used by another element's styling code.
|
|
*/
|
|
export function resetStylingState() {
|
|
_state.element = null;
|
|
}
|