refactor(ivy): move all styling configurations into TNodeFlags (#33540)

This patch gets rid of the configuration settings present in the
`TStylingContext` array that is used within the styling algorithm
for `[style]`, `[style.prop]`, `[class]` and `[class.name]` bindings.
These configurations now all live inside of the `TNodeFlags`.

PR Close #33540
This commit is contained in:
Matias Niemelä
2019-11-01 15:53:50 -07:00
committed by atscott
parent e89c2dd8d0
commit 41560b47c4
11 changed files with 369 additions and 292 deletions

View File

@ -11,7 +11,7 @@ import {throwErrorIfNoChangesMode} from '../errors';
import {setInputsForProperty} from '../instructions/shared';
import {AttributeMarker, TAttributes, TNode, TNodeFlags, TNodeType} from '../interfaces/node';
import {RElement} from '../interfaces/renderer';
import {StylingMapArray, StylingMapArrayIndex, TStylingConfig, TStylingContext} from '../interfaces/styling';
import {StylingMapArray, StylingMapArrayIndex, TStylingContext} from '../interfaces/styling';
import {isDirectiveHost} from '../interfaces/type_checks';
import {LView, RENDERER, TVIEW, TView} from '../interfaces/view';
import {getActiveDirectiveId, getCheckNoChangesMode, getCurrentStyleSanitizer, getLView, getSelectedIndex, incrementBindingIndex, nextBindingIndex, resetCurrentStyleSanitizer, setCurrentStyleSanitizer, setElementExitFn} from '../state';
@ -95,9 +95,21 @@ export function stylePropInternal(
// still needs to be incremented because all styling binding values
// are stored inside of the lView.
const bindingIndex = nextBindingIndex();
const lView = getLView();
const tNode = getTNode(elementIndex, lView);
const firstUpdatePass = lView[TVIEW].firstUpdatePass;
const updated =
stylingProp(elementIndex, bindingIndex, prop, resolveStylePropValue(value, suffix), false);
// we check for this in the instruction code so that the context can be notified
// about prop or map bindings so that the direct apply check can decide earlier
// if it allows for context resolution to be bypassed.
if (firstUpdatePass) {
patchConfig(tNode, TNodeFlags.hasStylePropBindings);
patchHostStylingFlag(tNode, isHostStyling(), false);
}
const updated = stylingProp(
tNode, firstUpdatePass, lView, bindingIndex, prop, resolveStylePropValue(value, suffix),
false);
if (ngDevMode) {
ngDevMode.styleProp++;
if (updated) {
@ -127,8 +139,20 @@ export function ɵɵclassProp(className: string, value: boolean | null): void {
// still needs to be incremented because all styling binding values
// are stored inside of the lView.
const bindingIndex = nextBindingIndex();
const lView = getLView();
const elementIndex = getSelectedIndex();
const tNode = getTNode(elementIndex, lView);
const firstUpdatePass = lView[TVIEW].firstUpdatePass;
const updated = stylingProp(getSelectedIndex(), bindingIndex, className, value, true);
// we check for this in the instruction code so that the context can be notified
// about prop or map bindings so that the direct apply check can decide earlier
// if it allows for context resolution to be bypassed.
if (firstUpdatePass) {
patchConfig(tNode, TNodeFlags.hasClassPropBindings);
patchHostStylingFlag(tNode, isHostStyling(), true);
}
const updated = stylingProp(tNode, firstUpdatePass, lView, bindingIndex, className, value, true);
if (ngDevMode) {
ngDevMode.classProp++;
if (updated) {
@ -148,25 +172,15 @@ export function ɵɵclassProp(className: string, value: boolean | null): void {
* present together).
*/
function stylingProp(
elementIndex: number, bindingIndex: number, prop: string,
tNode: TNode, firstUpdatePass: boolean, lView: LView, bindingIndex: number, prop: string,
value: boolean | number | SafeValue | string | null | undefined | NO_CHANGE,
isClassBased: boolean): boolean {
let updated = false;
const lView = getLView();
const firstUpdatePass = lView[TVIEW].firstUpdatePass;
const tNode = getTNode(elementIndex, lView);
const native = getNativeByTNode(tNode, lView) as RElement;
const context = isClassBased ? getClassesContext(tNode) : getStylesContext(tNode);
const sanitizer = isClassBased ? null : getCurrentStyleSanitizer();
// we check for this in the instruction code so that the context can be notified
// about prop or map bindings so that the direct apply check can decide earlier
// if it allows for context resolution to be bypassed.
if (firstUpdatePass) {
patchConfig(context, TStylingConfig.HasPropBindings);
}
// [style.prop] and [class.name] bindings do not use `bind()` and will
// therefore manage accessing and updating the new value in the lView directly.
// For this reason, the checkNoChanges situation must also be handled here
@ -180,11 +194,12 @@ function stylingProp(
// Direct Apply Case: bypass context resolution and apply the
// style/class value directly to the element
if (allowDirectStyling(context, firstUpdatePass)) {
if (allowDirectStyling(tNode, isClassBased, firstUpdatePass)) {
const sanitizerToUse = isClassBased ? null : sanitizer;
const renderer = getRenderer(tNode, lView);
updated = applyStylingValueDirectly(
renderer, context, native, lView, bindingIndex, prop, value, isClassBased, sanitizerToUse);
renderer, context, tNode, native, lView, bindingIndex, prop, value, isClassBased,
sanitizerToUse);
if (sanitizerToUse) {
// it's important we remove the current style sanitizer once the
@ -199,11 +214,11 @@ function stylingProp(
const directiveIndex = getActiveDirectiveId();
if (isClassBased) {
updated = updateClassViaContext(
context, lView, native, directiveIndex, prop, bindingIndex,
context, tNode, lView, native, directiveIndex, prop, bindingIndex,
value as string | boolean | null, false, firstUpdatePass);
} else {
updated = updateStyleViaContext(
context, lView, native, directiveIndex, prop, bindingIndex,
context, tNode, lView, native, directiveIndex, prop, bindingIndex,
value as string | SafeValue | null, sanitizer, false, firstUpdatePass);
}
@ -245,15 +260,24 @@ export function ɵɵstyleMap(styles: {[styleName: string]: any} | NO_CHANGE | nu
// still needs to be incremented because all styling binding values
// are stored inside of the lView.
const bindingIndex = incrementBindingIndex(2);
const hostBindingsMode = isHostStyling();
// 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 (!isHostStyling() && hasDirectiveInput && styles !== NO_CHANGE) {
if (!hostBindingsMode && hasDirectiveInput && styles !== NO_CHANGE) {
updateDirectiveInputValue(context, lView, tNode, bindingIndex, styles, false, firstUpdatePass);
styles = NO_CHANGE;
}
// we check for this in the instruction code so that the context can be notified
// about prop or map bindings so that the direct apply check can decide earlier
// if it allows for context resolution to be bypassed.
if (firstUpdatePass) {
patchConfig(tNode, TNodeFlags.hasStyleMapBindings);
patchHostStylingFlag(tNode, isHostStyling(), false);
}
stylingMap(
context, tNode, firstUpdatePass, lView, bindingIndex, styles, false, hasDirectiveInput);
}
@ -299,15 +323,24 @@ export function classMapInternal(
// still needs to be incremented because all styling binding values
// are stored inside of the lView.
const bindingIndex = incrementBindingIndex(2);
const hostBindingsMode = isHostStyling();
// 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 (!isHostStyling() && hasDirectiveInput && classes !== NO_CHANGE) {
if (!hostBindingsMode && hasDirectiveInput && classes !== NO_CHANGE) {
updateDirectiveInputValue(context, lView, tNode, bindingIndex, classes, true, firstUpdatePass);
classes = NO_CHANGE;
}
// we check for this in the instruction code so that the context can be notified
// about prop or map bindings so that the direct apply check can decide earlier
// if it allows for context resolution to be bypassed.
if (firstUpdatePass) {
patchConfig(tNode, TNodeFlags.hasClassMapBindings);
patchHostStylingFlag(tNode, isHostStyling(), true);
}
stylingMap(
context, tNode, firstUpdatePass, lView, bindingIndex, classes, true, hasDirectiveInput);
}
@ -336,20 +369,13 @@ function stylingMap(
throwErrorIfNoChangesMode(false, oldValue, value);
}
// we check for this in the instruction code so that the context can be notified
// about prop or map bindings so that the direct apply check can decide earlier
// if it allows for context resolution to be bypassed.
if (firstUpdatePass) {
patchConfig(context, TStylingConfig.HasMapBindings);
}
// Direct Apply Case: bypass context resolution and apply the
// style/class map values directly to the element
if (allowDirectStyling(context, firstUpdatePass)) {
if (allowDirectStyling(tNode, isClassBased, firstUpdatePass)) {
const sanitizerToUse = isClassBased ? null : sanitizer;
const renderer = getRenderer(tNode, lView);
applyStylingMapDirectly(
renderer, context, native, lView, bindingIndex, value, isClassBased, sanitizerToUse,
renderer, context, tNode, native, lView, bindingIndex, value, isClassBased, sanitizerToUse,
valueHasChanged, hasDirectiveInput);
if (sanitizerToUse) {
// it's important we remove the current style sanitizer once the
@ -368,12 +394,12 @@ function stylingMap(
// value to the element.
if (isClassBased) {
updateClassViaContext(
context, lView, native, directiveIndex, null, bindingIndex, stylingMapArr,
context, tNode, lView, native, directiveIndex, null, bindingIndex, stylingMapArr,
valueHasChanged, firstUpdatePass);
} else {
updateStyleViaContext(
context, lView, native, directiveIndex, null, bindingIndex, stylingMapArr, sanitizer,
valueHasChanged, firstUpdatePass);
context, tNode, lView, native, directiveIndex, null, bindingIndex, stylingMapArr,
sanitizer, valueHasChanged, firstUpdatePass);
}
setElementExitFn(stylingApply);
@ -463,7 +489,7 @@ function stylingApply(): void {
const classesContext = isStylingContext(tNode.classes) ? tNode.classes as TStylingContext : null;
const stylesContext = isStylingContext(tNode.styles) ? tNode.styles as TStylingContext : null;
flushStyling(
renderer, lView, classesContext, stylesContext, native, directiveIndex, sanitizer,
renderer, lView, tNode, classesContext, stylesContext, native, directiveIndex, sanitizer,
tView.firstUpdatePass);
resetCurrentStyleSanitizer();
}
@ -541,7 +567,7 @@ function getContext(tNode: TNode, isClassBased: boolean): TStylingContext {
const hasDirectives = isDirectiveHost(tNode);
context = allocTStylingContext(context as StylingMapArray | null, hasDirectives);
if (ngDevMode) {
attachStylingDebugObject(context as TStylingContext, isClassBased);
attachStylingDebugObject(context as TStylingContext, tNode, isClassBased);
}
if (isClassBased) {
@ -582,3 +608,10 @@ function resolveStylePropValue(
function isHostStyling(): boolean {
return isHostStylingActive(getActiveDirectiveId());
}
function patchHostStylingFlag(tNode: TNode, hostBindingsMode: boolean, isClassBased: boolean) {
const flag = hostBindingsMode ?
isClassBased ? TNodeFlags.hasHostClassBindings : TNodeFlags.hasHostStyleBindings :
isClassBased ? TNodeFlags.hasTemplateClassBindings : TNodeFlags.hasTemplateStyleBindings;
patchConfig(tNode, flag);
}