fix(ivy): ensure parent/sub-class components evaluate styling correctly (#29602)
The new styling algorithm in angular is designed to evaluate host bindings stylinh priority in order of directive evaluation order. This, however, does not work with respect to parent/sub-class directives because sub-class host bindings are run after the parent host bindings but still have priority. This patch ensures that the host styling bindings for parent and sub-class components/directives are executed with respect to the styling algorithm prioritization. Jira Issue: FW-1132 PR Close #29602
This commit is contained in:

committed by
Igor Minar

parent
5c13feebfd
commit
ec56354306
@ -11,20 +11,23 @@ import {assertHasParent} from '../assert';
|
||||
import {attachPatchData} from '../context_discovery';
|
||||
import {registerPostOrderHooks} from '../hooks';
|
||||
import {TAttributes, TNodeFlags, TNodeType} from '../interfaces/node';
|
||||
import {RElement, Renderer3, isProceduralRenderer} from '../interfaces/renderer';
|
||||
import {RElement, isProceduralRenderer} from '../interfaces/renderer';
|
||||
import {SanitizerFn} from '../interfaces/sanitization';
|
||||
import {BINDING_INDEX, QUERIES, RENDERER, TVIEW} from '../interfaces/view';
|
||||
import {assertNodeType} from '../node_assert';
|
||||
import {appendChild} from '../node_manipulation';
|
||||
import {applyOnCreateInstructions} from '../node_util';
|
||||
import {decreaseElementDepthCount, getActiveHostContext, getElementDepthCount, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, increaseElementDepthCount, setIsParent, setPreviousOrParentTNode} from '../state';
|
||||
import {decreaseElementDepthCount, getActiveDirectiveId, getElementDepthCount, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, getSelectedIndex, increaseElementDepthCount, setIsParent, setPreviousOrParentTNode} from '../state';
|
||||
import {getInitialClassNameValue, getInitialStyleStringValue, initializeStaticContext, patchContextWithStaticAttrs, renderInitialClasses, renderInitialStyles} from '../styling/class_and_style_bindings';
|
||||
import {getStylingContext, hasClassInput, hasStyleInput} from '../styling/util';
|
||||
import {NO_CHANGE} from '../tokens';
|
||||
import {attrsStylingIndexOf, setUpAttributes} from '../util/attrs_utils';
|
||||
import {renderStringify} from '../util/misc_utils';
|
||||
import {getNativeByIndex, getNativeByTNode, getTNode} from '../util/view_utils';
|
||||
|
||||
import {createDirectivesAndLocals, createNodeAtIndex, elementCreate, executeContentQueries, initializeTNodeInputs, setInputsForProperty, setNodeStylingTemplate} from './shared';
|
||||
import {getActiveDirectiveStylingIndex} from './styling_instructions';
|
||||
|
||||
|
||||
/**
|
||||
* Create DOM element. The instruction must later be followed by `elementEnd()` call.
|
||||
@ -256,17 +259,26 @@ export function elementAttribute(
|
||||
* @publicApi
|
||||
*/
|
||||
export function elementHostAttrs(attrs: TAttributes) {
|
||||
const tNode = getPreviousOrParentTNode();
|
||||
const hostElementIndex = getSelectedIndex();
|
||||
const lView = getLView();
|
||||
const native = getNativeByTNode(tNode, lView) as RElement;
|
||||
const lastAttrIndex = setUpAttributes(native, attrs);
|
||||
const stylingAttrsStartIndex = attrsStylingIndexOf(attrs, lastAttrIndex);
|
||||
if (stylingAttrsStartIndex >= 0) {
|
||||
const directive = getActiveHostContext();
|
||||
if (tNode.stylingTemplate) {
|
||||
patchContextWithStaticAttrs(tNode.stylingTemplate, attrs, stylingAttrsStartIndex, directive);
|
||||
} else {
|
||||
tNode.stylingTemplate = initializeStaticContext(attrs, stylingAttrsStartIndex, directive);
|
||||
const tNode = getTNode(hostElementIndex, lView);
|
||||
|
||||
// non-element nodes (e.g. `<ng-container>`) are not rendered as actual
|
||||
// element nodes and adding styles/classes on to them will cause runtime
|
||||
// errors...
|
||||
if (tNode.type === TNodeType.Element) {
|
||||
const native = getNativeByTNode(tNode, lView) as RElement;
|
||||
const lastAttrIndex = setUpAttributes(native, attrs);
|
||||
const stylingAttrsStartIndex = attrsStylingIndexOf(attrs, lastAttrIndex);
|
||||
if (stylingAttrsStartIndex >= 0) {
|
||||
const directiveStylingIndex = getActiveDirectiveStylingIndex();
|
||||
if (tNode.stylingTemplate) {
|
||||
patchContextWithStaticAttrs(
|
||||
tNode.stylingTemplate, attrs, stylingAttrsStartIndex, directiveStylingIndex);
|
||||
} else {
|
||||
tNode.stylingTemplate =
|
||||
initializeStaticContext(attrs, stylingAttrsStartIndex, directiveStylingIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import {TAttributes, TNodeType} from '../interfaces/node';
|
||||
import {BINDING_INDEX, QUERIES, RENDERER, TVIEW} from '../interfaces/view';
|
||||
import {assertNodeType} from '../node_assert';
|
||||
import {appendChild} from '../node_manipulation';
|
||||
import {applyOnCreateInstructions} from '../node_util';
|
||||
import {getIsParent, getLView, getPreviousOrParentTNode, setIsParent, setPreviousOrParentTNode} from '../state';
|
||||
import {createDirectivesAndLocals, createNodeAtIndex, executeContentQueries, setNodeStylingTemplate} from './shared';
|
||||
|
||||
@ -83,5 +84,9 @@ export function elementContainerEnd(): void {
|
||||
lView[QUERIES] = currentQueries.parent;
|
||||
}
|
||||
|
||||
// this is required for all host-level styling-related instructions to run
|
||||
// in the correct order
|
||||
previousOrParentTNode.onElementCreationFns && applyOnCreateInstructions(previousOrParentTNode);
|
||||
|
||||
registerPostOrderHooks(tView, previousOrParentTNode);
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import {StylingContext} from '../interfaces/styling';
|
||||
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TVIEW, TView, T_HOST} from '../interfaces/view';
|
||||
import {assertNodeOfPossibleTypes, assertNodeType} from '../node_assert';
|
||||
import {isNodeMatchingSelectorList} from '../node_selector_matcher';
|
||||
import {enterView, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, isCreationMode, leaveView, namespaceHTML, resetComponentState, setActiveHost, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setIsParent, setPreviousOrParentTNode, setSelectedIndex} from '../state';
|
||||
import {enterView, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, incrementActiveDirectiveId, isCreationMode, leaveView, namespaceHTML, resetComponentState, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setIsParent, setPreviousOrParentTNode, setSelectedIndex} from '../state';
|
||||
import {initializeStaticContext as initializeStaticStylingContext} from '../styling/class_and_style_bindings';
|
||||
import {NO_CHANGE} from '../tokens';
|
||||
import {attrsStylingIndexOf} from '../util/attrs_utils';
|
||||
@ -35,6 +35,7 @@ import {INTERPOLATION_DELIMITER, renderStringify} from '../util/misc_utils';
|
||||
import {getLViewParent, getRootContext} from '../util/view_traversal_utils';
|
||||
import {getComponentViewByIndex, getNativeByTNode, isComponentDef, isContentQueryHost, isRootView, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector} from '../util/view_utils';
|
||||
|
||||
|
||||
/**
|
||||
* A permanent marker promise which signifies that the current CD tree is
|
||||
* clean.
|
||||
@ -107,6 +108,8 @@ export function setHostBindings(tView: TView, viewData: LView): void {
|
||||
// Negative numbers mean that we are starting new EXPANDO block and need to update
|
||||
// the current element and directive index.
|
||||
currentElementIndex = -instruction;
|
||||
setActiveHostElement(currentElementIndex);
|
||||
|
||||
// Injector block and providers are taken into account.
|
||||
const providerCount = (tView.expandoInstructions[++i] as number);
|
||||
bindingRootIndex += INJECTOR_BLOOM_PARENT_SIZE + providerCount;
|
||||
@ -124,14 +127,20 @@ export function setHostBindings(tView: TView, viewData: LView): void {
|
||||
if (instruction !== null) {
|
||||
viewData[BINDING_INDEX] = bindingRootIndex;
|
||||
const hostCtx = unwrapRNode(viewData[currentDirectiveIndex]);
|
||||
setActiveHost(hostCtx, currentElementIndex);
|
||||
instruction(RenderFlags.Update, hostCtx, currentElementIndex);
|
||||
setActiveHost(null);
|
||||
|
||||
// Each directive gets a uniqueId value that is the same for both
|
||||
// create and update calls when the hostBindings function is called. The
|
||||
// directive uniqueId is not set anywhere--it is just incremented between
|
||||
// each hostBindings call and is useful for helping instruction code
|
||||
// uniquely determine which directive is currently active when executed.
|
||||
incrementActiveDirectiveId();
|
||||
}
|
||||
currentDirectiveIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
setActiveHostElement(null);
|
||||
}
|
||||
|
||||
/** Refreshes content queries for all directives in the given view. */
|
||||
@ -897,15 +906,27 @@ function invokeDirectivesHostBindings(tView: TView, viewData: LView, tNode: TNod
|
||||
const end = tNode.directiveEnd;
|
||||
const expando = tView.expandoInstructions !;
|
||||
const firstTemplatePass = tView.firstTemplatePass;
|
||||
const elementIndex = tNode.index - HEADER_OFFSET;
|
||||
setActiveHostElement(elementIndex);
|
||||
|
||||
for (let i = start; i < end; i++) {
|
||||
const def = tView.data[i] as DirectiveDef<any>;
|
||||
const directive = viewData[i];
|
||||
if (def.hostBindings) {
|
||||
invokeHostBindingsInCreationMode(def, expando, directive, tNode, firstTemplatePass);
|
||||
|
||||
// Each directive gets a uniqueId value that is the same for both
|
||||
// create and update calls when the hostBindings function is called. The
|
||||
// directive uniqueId is not set anywhere--it is just incremented between
|
||||
// each hostBindings call and is useful for helping instruction code
|
||||
// uniquely determine which directive is currently active when executed.
|
||||
incrementActiveDirectiveId();
|
||||
} else if (firstTemplatePass) {
|
||||
expando.push(null);
|
||||
}
|
||||
}
|
||||
|
||||
setActiveHostElement(null);
|
||||
}
|
||||
|
||||
export function invokeHostBindingsInCreationMode(
|
||||
@ -914,9 +935,7 @@ export function invokeHostBindingsInCreationMode(
|
||||
const previousExpandoLength = expando.length;
|
||||
setCurrentDirectiveDef(def);
|
||||
const elementIndex = tNode.index - HEADER_OFFSET;
|
||||
setActiveHost(directive, elementIndex);
|
||||
def.hostBindings !(RenderFlags.Create, directive, elementIndex);
|
||||
setActiveHost(null);
|
||||
setCurrentDirectiveDef(null);
|
||||
// `hostBindings` function may or may not contain `allocHostVars` call
|
||||
// (e.g. it may not if it only contains host listeners), so we need to check whether
|
||||
|
@ -6,19 +6,24 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
||||
import {TNode} from '../interfaces/node';
|
||||
import {TNode, TNodeType} from '../interfaces/node';
|
||||
import {PlayerFactory} from '../interfaces/player';
|
||||
import {FLAGS, HEADER_OFFSET, LViewFlags, RENDERER, RootContextFlags} from '../interfaces/view';
|
||||
import {getActiveHostContext, getActiveHostElementIndex, getLView, getPreviousOrParentTNode} from '../state';
|
||||
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 {ParamsOf, enqueueHostInstruction, registerHostDirective} from '../styling/host_instructions_queue';
|
||||
import {BoundPlayerFactory} from '../styling/player_factory';
|
||||
import {allocateDirectiveIntoContext, createEmptyStylingContext, forceClassesAsString, forceStylesAsString, getStylingContext, hasClassInput, hasStyleInput} from '../styling/util';
|
||||
import {DEFAULT_TEMPLATE_DIRECTIVE_INDEX} from '../styling/shared';
|
||||
import {allocateOrUpdateDirectiveIntoContext, createEmptyStylingContext, forceClassesAsString, forceStylesAsString, getStylingContext, hasClassInput, hasStyleInput} from '../styling/util';
|
||||
import {NO_CHANGE} from '../tokens';
|
||||
import {renderStringify} from '../util/misc_utils';
|
||||
import {getRootContext} from '../util/view_traversal_utils';
|
||||
import {getTNode} from '../util/view_utils';
|
||||
|
||||
import {scheduleTick, setInputsForProperty} from './shared';
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* The contents of this file include the instructions for all styling-related
|
||||
* operations in Angular.
|
||||
@ -40,7 +45,6 @@ import {scheduleTick, setInputsForProperty} from './shared';
|
||||
* - elementHostStylingApply
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Allocates style and class binding properties on the element during creation mode.
|
||||
*
|
||||
@ -74,7 +78,9 @@ export function elementStyling(
|
||||
// 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).
|
||||
initElementStyling(tNode, classBindingNames, styleBindingNames, styleSanitizer, null);
|
||||
initElementStyling(
|
||||
tNode, classBindingNames, styleBindingNames, styleSanitizer,
|
||||
DEFAULT_TEMPLATE_DIRECTIVE_INDEX);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,25 +114,28 @@ export function elementHostStyling(
|
||||
tNode.stylingTemplate = createEmptyStylingContext();
|
||||
}
|
||||
|
||||
const directive = getActiveHostContext();
|
||||
const directiveStylingIndex = getActiveDirectiveStylingIndex();
|
||||
|
||||
// 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
|
||||
// for the prioritization of bindings).
|
||||
allocateDirectiveIntoContext(tNode.stylingTemplate, directive);
|
||||
allocateOrUpdateDirectiveIntoContext(tNode.stylingTemplate, directiveStylingIndex);
|
||||
|
||||
const fns = tNode.onElementCreationFns = tNode.onElementCreationFns || [];
|
||||
fns.push(
|
||||
() => initElementStyling(
|
||||
tNode, classBindingNames, styleBindingNames, styleSanitizer, directive));
|
||||
fns.push(() => {
|
||||
initElementStyling(
|
||||
tNode, classBindingNames, styleBindingNames, styleSanitizer, directiveStylingIndex);
|
||||
registerHostDirective(tNode.stylingTemplate !, directiveStylingIndex);
|
||||
});
|
||||
}
|
||||
|
||||
function initElementStyling(
|
||||
tNode: TNode, classBindingNames?: string[] | null, styleBindingNames?: string[] | null,
|
||||
styleSanitizer?: StyleSanitizeFn | null, directive?: {} | null): void {
|
||||
tNode: TNode, classBindingNames: string[] | null | undefined,
|
||||
styleBindingNames: string[] | null | undefined,
|
||||
styleSanitizer: StyleSanitizeFn | null | undefined, directiveStylingIndex: number): void {
|
||||
updateContextWithBindings(
|
||||
tNode.stylingTemplate !, directive || null, classBindingNames, styleBindingNames,
|
||||
tNode.stylingTemplate !, directiveStylingIndex, classBindingNames, styleBindingNames,
|
||||
styleSanitizer);
|
||||
}
|
||||
|
||||
@ -160,7 +169,10 @@ function initElementStyling(
|
||||
export function elementStyleProp(
|
||||
index: number, styleIndex: number, value: string | number | String | PlayerFactory | null,
|
||||
suffix?: string | null, forceOverride?: boolean): void {
|
||||
elementStylePropInternal(null, index, styleIndex, value, suffix, forceOverride);
|
||||
const valueToAdd = resolveStylePropValue(value, suffix);
|
||||
updateElementStyleProp(
|
||||
getStylingContext(index + HEADER_OFFSET, getLView()), styleIndex, valueToAdd,
|
||||
DEFAULT_TEMPLATE_DIRECTIVE_INDEX, forceOverride);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -191,15 +203,20 @@ export function elementStyleProp(
|
||||
export function elementHostStyleProp(
|
||||
styleIndex: number, value: string | number | String | PlayerFactory | null,
|
||||
suffix?: string | null, forceOverride?: boolean): void {
|
||||
elementStylePropInternal(
|
||||
getActiveHostContext() !, getActiveHostElementIndex() !, styleIndex, value, suffix,
|
||||
forceOverride);
|
||||
const directiveStylingIndex = getActiveDirectiveStylingIndex();
|
||||
const hostElementIndex = getSelectedIndex();
|
||||
|
||||
const lView = getLView();
|
||||
const stylingContext = getStylingContext(hostElementIndex + HEADER_OFFSET, lView);
|
||||
|
||||
const valueToAdd = resolveStylePropValue(value, suffix);
|
||||
const args: ParamsOf<typeof updateElementStyleProp> =
|
||||
[stylingContext, styleIndex, valueToAdd, directiveStylingIndex, forceOverride];
|
||||
enqueueHostInstruction(stylingContext, directiveStylingIndex, updateElementStyleProp, args);
|
||||
}
|
||||
|
||||
function elementStylePropInternal(
|
||||
directive: {} | null, index: number, styleIndex: number,
|
||||
value: string | number | String | PlayerFactory | null, suffix?: string | null,
|
||||
forceOverride?: boolean): void {
|
||||
function resolveStylePropValue(
|
||||
value: string | number | String | PlayerFactory | null, suffix: string | null | undefined) {
|
||||
let valueToAdd: string|null = null;
|
||||
if (value !== null) {
|
||||
if (suffix) {
|
||||
@ -214,9 +231,7 @@ function elementStylePropInternal(
|
||||
valueToAdd = value as any as string;
|
||||
}
|
||||
}
|
||||
updateElementStyleProp(
|
||||
getStylingContext(index + HEADER_OFFSET, getLView()), styleIndex, valueToAdd, directive,
|
||||
forceOverride);
|
||||
return valueToAdd;
|
||||
}
|
||||
|
||||
|
||||
@ -241,7 +256,12 @@ function elementStylePropInternal(
|
||||
export function elementClassProp(
|
||||
index: number, classIndex: number, value: boolean | PlayerFactory,
|
||||
forceOverride?: boolean): void {
|
||||
elementClassPropInternal(null, index, classIndex, value, forceOverride);
|
||||
const input = (value instanceof BoundPlayerFactory) ?
|
||||
(value as BoundPlayerFactory<boolean|null>) :
|
||||
booleanOrNull(value);
|
||||
updateElementClassProp(
|
||||
getStylingContext(index + HEADER_OFFSET, getLView()), classIndex, input,
|
||||
DEFAULT_TEMPLATE_DIRECTIVE_INDEX, forceOverride);
|
||||
}
|
||||
|
||||
|
||||
@ -265,19 +285,19 @@ export function elementClassProp(
|
||||
*/
|
||||
export function elementHostClassProp(
|
||||
classIndex: number, value: boolean | PlayerFactory, forceOverride?: boolean): void {
|
||||
elementClassPropInternal(
|
||||
getActiveHostContext() !, getActiveHostElementIndex() !, classIndex, value, forceOverride);
|
||||
}
|
||||
const directiveStylingIndex = getActiveDirectiveStylingIndex();
|
||||
const hostElementIndex = getSelectedIndex();
|
||||
|
||||
const lView = getLView();
|
||||
const stylingContext = getStylingContext(hostElementIndex + HEADER_OFFSET, lView);
|
||||
|
||||
function elementClassPropInternal(
|
||||
directive: {} | null, index: number, classIndex: number, value: boolean | PlayerFactory,
|
||||
forceOverride?: boolean): void {
|
||||
const input = (value instanceof BoundPlayerFactory) ?
|
||||
(value as BoundPlayerFactory<boolean|null>) :
|
||||
booleanOrNull(value);
|
||||
updateElementClassProp(
|
||||
getStylingContext(index + HEADER_OFFSET, getLView()), classIndex, input, directive,
|
||||
forceOverride);
|
||||
|
||||
const args: ParamsOf<typeof updateElementClassProp> =
|
||||
[stylingContext, classIndex, input, directiveStylingIndex, forceOverride];
|
||||
enqueueHostInstruction(stylingContext, directiveStylingIndex, updateElementClassProp, args);
|
||||
}
|
||||
|
||||
function booleanOrNull(value: any): boolean|null {
|
||||
@ -309,7 +329,30 @@ function booleanOrNull(value: any): boolean|null {
|
||||
export function elementStylingMap(
|
||||
index: number, classes: {[key: string]: any} | string | NO_CHANGE | null,
|
||||
styles?: {[styleName: string]: any} | NO_CHANGE | null): void {
|
||||
elementStylingMapInternal(null, index, classes, styles);
|
||||
const lView = getLView();
|
||||
const tNode = getTNode(index, lView);
|
||||
const stylingContext = getStylingContext(index + HEADER_OFFSET, 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 (hasClassInput(tNode) && classes !== NO_CHANGE) {
|
||||
const initialClasses = getInitialClassNameValue(stylingContext);
|
||||
const classInputVal =
|
||||
(initialClasses.length ? (initialClasses + ' ') : '') + forceClassesAsString(classes);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@ -339,39 +382,15 @@ export function elementStylingMap(
|
||||
export function elementHostStylingMap(
|
||||
classes: {[key: string]: any} | string | NO_CHANGE | null,
|
||||
styles?: {[styleName: string]: any} | NO_CHANGE | null): void {
|
||||
elementStylingMapInternal(
|
||||
getActiveHostContext() !, getActiveHostElementIndex() !, classes, styles);
|
||||
}
|
||||
const directiveStylingIndex = getActiveDirectiveStylingIndex();
|
||||
const hostElementIndex = getSelectedIndex();
|
||||
|
||||
function elementStylingMapInternal(
|
||||
directive: {} | null, index: number, classes: {[key: string]: any} | string | NO_CHANGE | null,
|
||||
styles?: {[styleName: string]: any} | NO_CHANGE | null): void {
|
||||
const lView = getLView();
|
||||
const tNode = getTNode(index, lView);
|
||||
const stylingContext = getStylingContext(index + HEADER_OFFSET, lView);
|
||||
const stylingContext = getStylingContext(hostElementIndex + HEADER_OFFSET, 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 (!directive) {
|
||||
if (hasClassInput(tNode) && classes !== NO_CHANGE) {
|
||||
const initialClasses = getInitialClassNameValue(stylingContext);
|
||||
const classInputVal =
|
||||
(initialClasses.length ? (initialClasses + ' ') : '') + forceClassesAsString(classes);
|
||||
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, directive);
|
||||
const args: ParamsOf<typeof updateStylingMap> =
|
||||
[stylingContext, classes, styles, directiveStylingIndex];
|
||||
enqueueHostInstruction(stylingContext, directiveStylingIndex, updateStylingMap, args);
|
||||
}
|
||||
|
||||
|
||||
@ -387,7 +406,7 @@ function elementStylingMapInternal(
|
||||
* @publicApi
|
||||
*/
|
||||
export function elementStylingApply(index: number): void {
|
||||
elementStylingApplyInternal(null, index);
|
||||
elementStylingApplyInternal(DEFAULT_TEMPLATE_DIRECTIVE_INDEX, index);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -401,17 +420,33 @@ export function elementStylingApply(index: number): void {
|
||||
* @publicApi
|
||||
*/
|
||||
export function elementHostStylingApply(): void {
|
||||
elementStylingApplyInternal(getActiveHostContext() !, getActiveHostElementIndex() !);
|
||||
elementStylingApplyInternal(getActiveDirectiveStylingIndex(), getSelectedIndex());
|
||||
}
|
||||
|
||||
export function elementStylingApplyInternal(directive: {} | null, index: number): void {
|
||||
export function elementStylingApplyInternal(directiveStylingIndex: number, index: number): void {
|
||||
const lView = getLView();
|
||||
const tNode = getTNode(index, lView);
|
||||
|
||||
// if a non-element value is being processed then we can't render values
|
||||
// on the element at all therefore by setting the renderer to null then
|
||||
// the styling apply code knows not to actually apply the values...
|
||||
const renderer = tNode.type === TNodeType.Element ? lView[RENDERER] : null;
|
||||
const isFirstRender = (lView[FLAGS] & LViewFlags.FirstLViewPass) !== 0;
|
||||
const stylingContext = getStylingContext(index + HEADER_OFFSET, lView);
|
||||
const totalPlayersQueued = renderStyling(
|
||||
getStylingContext(index + HEADER_OFFSET, lView), lView[RENDERER], lView, isFirstRender, null,
|
||||
null, directive);
|
||||
stylingContext, renderer, lView, isFirstRender, null, null, directiveStylingIndex);
|
||||
if (totalPlayersQueued > 0) {
|
||||
const rootContext = getRootContext(lView);
|
||||
scheduleTick(rootContext, RootContextFlags.FlushPlayers);
|
||||
}
|
||||
}
|
||||
|
||||
export function getActiveDirectiveStylingIndex() {
|
||||
// whenever a directive's hostBindings function is called a uniqueId value
|
||||
// is assigned. Normally this is enough to help distinguish one directive
|
||||
// from another for the styling context, but there are situations where a
|
||||
// sub-class directive could inherit and assign styling in concert with a
|
||||
// parent directive. To help the styling code distinguish between a parent
|
||||
// sub-classed directive the inheritance depth is taken into account as well.
|
||||
return getActiveDirectiveId() + getActiveDirectiveSuperClassDepth();
|
||||
}
|
Reference in New Issue
Block a user