refactor(ivy): evaluate prop-based styling bindings with a new algorithm (#30469)

This is the first refactor PR designed to change how styling bindings
(i.e. `[style]` and `[class]`) behave in Ivy. Instead of having a heavy
element-by-element context be generated for each element, this new
refactor aims to use a single context for each `tNode` element that is
examined and iterated over when styling values are to be applied to the
element.

This patch brings this new functionality to prop-based bindings such as
`[style.prop]` and `[class.name]`.

PR Close #30469
This commit is contained in:
Matias Niemelä
2019-05-08 16:30:28 -07:00
committed by Jason Aden
parent 848e53efd0
commit f03475cac8
24 changed files with 1993 additions and 26 deletions

View File

@ -17,6 +17,9 @@ import {BoundPlayerFactory} from '../styling/player_factory';
import {DEFAULT_TEMPLATE_DIRECTIVE_INDEX} from '../styling/shared';
import {getCachedStylingContext, setCachedStylingContext} from '../styling/state';
import {allocateOrUpdateDirectiveIntoContext, createEmptyStylingContext, forceClassesAsString, forceStylesAsString, getStylingContextFromLView, hasClassInput, hasStyleInput} from '../styling/util';
import {classProp as newClassProp, styleProp as newStyleProp, stylingApply as newStylingApply, stylingInit as newStylingInit} from '../styling_next/instructions';
import {runtimeAllowOldStyling, runtimeIsNewStylingInUse} from '../styling_next/state';
import {getBindingNameFromIndex} from '../styling_next/util';
import {NO_CHANGE} from '../tokens';
import {renderStringify} from '../util/misc_utils';
import {getRootContext} from '../util/view_traversal_utils';
@ -73,6 +76,13 @@ export function Δstyling(
const directiveStylingIndex = getActiveDirectiveStylingIndex();
if (directiveStylingIndex) {
// this is temporary hack to get the existing styling instructions to
// play ball with the new refactored implementation.
// TODO (matsko): remove this once the old implementation is not needed.
if (runtimeIsNewStylingInUse()) {
newStylingInit();
}
// despite the binding being applied in a queue (below), the allocation
// of the directive into the context happens right away. The reason for
// this is to retain the ordering of the directives (which is important
@ -81,7 +91,7 @@ export function Δstyling(
const fns = tNode.onElementCreationFns = tNode.onElementCreationFns || [];
fns.push(() => {
initstyling(
initStyling(
tNode, classBindingNames, styleBindingNames, styleSanitizer, directiveStylingIndex);
registerHostDirective(tNode.stylingTemplate !, directiveStylingIndex);
});
@ -92,13 +102,13 @@ export function Δstyling(
// components) then they will be applied at the end of the `elementEnd`
// instruction (because directives are created first before styling is
// executed for a new element).
initstyling(
initStyling(
tNode, classBindingNames, styleBindingNames, styleSanitizer,
DEFAULT_TEMPLATE_DIRECTIVE_INDEX);
}
}
function initstyling(
function initStyling(
tNode: TNode, classBindingNames: string[] | null | undefined,
styleBindingNames: string[] | null | undefined,
styleSanitizer: StyleSanitizeFn | null | undefined, directiveStylingIndex: number): void {
@ -148,6 +158,15 @@ export function ΔstyleProp(
updatestyleProp(
stylingContext, styleIndex, valueToAdd, DEFAULT_TEMPLATE_DIRECTIVE_INDEX, forceOverride);
}
if (runtimeIsNewStylingInUse()) {
const prop = getBindingNameFromIndex(stylingContext, styleIndex, directiveStylingIndex, false);
// the reason why we cast the value as `boolean` is
// because the new styling refactor does not yet support
// sanitization or animation players.
newStyleProp(prop, value as string | number, suffix);
}
}
function resolveStylePropValue(
@ -206,6 +225,15 @@ export function ΔclassProp(
updateclassProp(
stylingContext, classIndex, input, DEFAULT_TEMPLATE_DIRECTIVE_INDEX, forceOverride);
}
if (runtimeIsNewStylingInUse()) {
const prop = getBindingNameFromIndex(stylingContext, classIndex, directiveStylingIndex, true);
// the reason why we cast the value as `boolean` is
// because the new styling refactor does not yet support
// sanitization or animation players.
newClassProp(prop, input as boolean);
}
}
@ -324,11 +352,14 @@ export function ΔstylingApply(): void {
const renderer = tNode.type === TNodeType.Element ? lView[RENDERER] : null;
const isFirstRender = (lView[FLAGS] & LViewFlags.FirstLViewPass) !== 0;
const stylingContext = getStylingContext(index, lView);
const totalPlayersQueued = renderStyling(
stylingContext, renderer, lView, isFirstRender, null, null, directiveStylingIndex);
if (totalPlayersQueued > 0) {
const rootContext = getRootContext(lView);
scheduleTick(rootContext, RootContextFlags.FlushPlayers);
if (runtimeAllowOldStyling()) {
const totalPlayersQueued = renderStyling(
stylingContext, renderer, lView, isFirstRender, null, null, directiveStylingIndex);
if (totalPlayersQueued > 0) {
const rootContext = getRootContext(lView);
scheduleTick(rootContext, RootContextFlags.FlushPlayers);
}
}
// because select(n) may not run between every instruction, the cached styling
@ -339,6 +370,10 @@ export function ΔstylingApply(): void {
// cleared because there is no code in Angular that applies more styling code after a
// styling flush has occurred. Note that this will be fixed once FW-1254 lands.
setCachedStylingContext(null);
if (runtimeIsNewStylingInUse()) {
newStylingApply();
}
}
export function getActiveDirectiveStylingIndex() {