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:
Matias Niemelä
2019-04-02 16:16:00 -07:00
committed by Igor Minar
parent 5c13feebfd
commit ec56354306
19 changed files with 1404 additions and 913 deletions

View File

@ -309,6 +309,21 @@ export interface StylingContext extends
*/
[StylingIndex.CachedMultiStyles]: any|MapBasedOffsetValues;
/**
* 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
* 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
* a queue is constructed and each instruction is populated into the queue.
* Then, once the style/class values are set to flush (via `elementStylingApply` or
* `hostStylingApply`), the queue is flushed and the values are rendered onto
* the host element.
*/
[StylingIndex.HostInstructionsQueue]: HostInstructionsQueue|null;
/**
* Location of animation context (which contains the active players) for this element styling
* context.
@ -316,6 +331,66 @@ export interface StylingContext extends
[StylingIndex.PlayerContext]: PlayerContext|null;
}
/**
* A queue of all host-related styling instructions (these are buffered and evaluated just before
* 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`)
* 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
* subclass directives) may not apply their styling at the right time for the styling
* algorithm code to prioritize them. Therefore, all host-styling instructions are queued up
* (buffered) into the array below and are automatically sorted in terms of priority. The
* priority for host-styling is as follows:
*
* 1. The template (this doesn't get queued, but gets evaluated immediately)
* 2. Any directives present on the host
* 2a) first child directive styling bindings are updated
* 2b) then any parent directives
* 3. Component host bindings
*
* Angular runs change detection for each of these cases in a different order. Because of this
* the array below is populated with each of the host styling functions + their arguments.
*
* context[HostInstructionsQueue] = [
* directiveIndex,
* hostStylingFn,
* [argumentsForFn],
* ...
* anotherDirectiveIndex, <-- this has a lower priority (a higher directive index)
* anotherHostStylingFn,
* [argumentsForFn],
* ]
*
* When `renderStyling` is called (within `class_and_host_bindings.ts`) then the queue is
* drained and each of the instructions are executed. Once complete the queue is empty then
* the style/class binding code is rendered on the element (which is what happens normally
* inside of `renderStyling`).
*
* Right now each directive's hostBindings function, as well the template function, both
* call `elementStylingApply()` and `hostStylingApply()`. The fact that this is called
* multiple times for the same element (b/c of change detection) causes some issues. To avoid
* having styling code be rendered on an element multiple times, the `HostInstructionsQueue`
* reserves a slot for a reference pointing to the very last directive that was registered and
* only allows for styling to be applied once that directive is encountered (which will happen
* as the last update for that element).
*/
export interface HostInstructionsQueue extends Array<number|Function|any[]> { [0]: number; }
/**
* Used as a reference for any values contained within `HostInstructionsQueue`.
*/
export const enum HostInstructionsQueueIndex {
LastRegisteredDirectiveIndexPosition = 0,
ValuesStartPosition = 1,
DirectiveIndexOffset = 0,
InstructionFnOffset = 1,
ParamsOffset = 2,
Size = 3,
}
/**
* Used as a styling array to house static class and style values that were extracted
* by the compiler and placed in the animation context via `elementStart` and
@ -511,9 +586,7 @@ export const enum InitialStylingValuesIndex {
* index value by the size of the array entries (so if DirA is at spot 8 then its index will be 2).
*/
export interface DirectiveRegistryValues extends Array<null|{}|boolean|number|StyleSanitizeFn> {
[DirectiveRegistryValuesIndex.DirectiveValueOffset]: null;
[DirectiveRegistryValuesIndex.SinglePropValuesIndexOffset]: number;
[DirectiveRegistryValuesIndex.DirtyFlagOffset]: boolean;
[DirectiveRegistryValuesIndex.StyleSanitizerOffset]: StyleSanitizeFn|null;
}
@ -522,11 +595,9 @@ export interface DirectiveRegistryValues extends Array<null|{}|boolean|number|St
* that are housed inside of [DirectiveRegistryValues].
*/
export const enum DirectiveRegistryValuesIndex {
DirectiveValueOffset = 0,
SinglePropValuesIndexOffset = 1,
DirtyFlagOffset = 2,
StyleSanitizerOffset = 3,
Size = 4
SinglePropValuesIndexOffset = 0,
StyleSanitizerOffset = 1,
Size = 2
}
/**
@ -678,9 +749,10 @@ export const enum StylingIndex {
CachedMultiStyles = 7,
// Multi and single entries are stored in `StylingContext` as: Flag; PropertyName; PropertyValue
// Position of where the initial styles are stored in the styling context
PlayerContext = 8,
HostInstructionsQueue = 8,
PlayerContext = 9,
// Location of single (prop) value entries are stored within the context
SingleStylesStartPosition = 9,
SingleStylesStartPosition = 10,
FlagsOffset = 0,
PropertyOffset = 1,
ValueOffset = 2,
@ -705,3 +777,13 @@ export const enum DirectiveOwnerAndPlayerBuilderIndex {
BitCountSize = 16,
BitMask = 0b1111111111111111
}
/**
* The default directive styling index value for template-based bindings.
*
* All host-level bindings (e.g. `hostStyleProp` and `hostStylingMap`) 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.
*/
export const DEFAULT_TEMPLATE_DIRECTIVE_INDEX = 0;