refactor(ivy): clean of #34804 from previous merge (#35022)

This is a follow up for https://github.com/angular/angular/pull/34804

PR Close #35022
This commit is contained in:
Miško Hevery 2020-01-28 16:26:56 -08:00 committed by Andrew Kushnir
parent ee8b8f52aa
commit c1cf46c5c3
19 changed files with 249 additions and 314 deletions

View File

@ -23,7 +23,7 @@ import {PlayerHandler} from './interfaces/player';
import {RElement, Renderer3, RendererFactory3, domRendererFactory3, isProceduralRenderer} from './interfaces/renderer';
import {CONTEXT, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW, TViewType} from './interfaces/view';
import {writeDirectClass, writeDirectStyle} from './node_manipulation';
import {enterView, getPreviousOrParentTNode, leaveView, setActiveHostElement} from './state';
import {enterView, getPreviousOrParentTNode, leaveView, setSelectedIndex} from './state';
import {computeStaticStyling} from './styling/static_styling';
import {setUpAttributes} from './util/attrs_utils';
import {publishDefaultGlobalUtils} from './util/global_utils';
@ -239,7 +239,7 @@ export function createRootComponent<T>(
if (tView.firstCreatePass &&
(componentDef.hostBindings !== null || componentDef.hostAttrs !== null)) {
const elementIndex = rootTNode.index - HEADER_OFFSET;
setActiveHostElement(elementIndex);
setSelectedIndex(elementIndex);
const rootTView = rootLView[TVIEW];
addHostBindingsToExpandoInstructions(rootTView, componentDef);

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {arrayMapSet} from '../../util/array_utils';
import {keyValueArraySet} from '../../util/array_utils';
import {getLView} from '../state';
import {interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV} from './interpolation';
import {checkStylingMap, classStringParser} from './styling';
@ -37,7 +37,7 @@ import {checkStylingMap, classStringParser} from './styling';
export function ɵɵclassMapInterpolate1(prefix: string, v0: any, suffix: string): void {
const lView = getLView();
const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
checkStylingMap(arrayMapSet, classStringParser, interpolatedValue, true);
checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
}
/**
@ -67,7 +67,7 @@ export function ɵɵclassMapInterpolate2(
prefix: string, v0: any, i0: string, v1: any, suffix: string): void {
const lView = getLView();
const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
checkStylingMap(arrayMapSet, classStringParser, interpolatedValue, true);
checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
}
/**
@ -100,7 +100,7 @@ export function ɵɵclassMapInterpolate3(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, suffix: string): void {
const lView = getLView();
const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
checkStylingMap(arrayMapSet, classStringParser, interpolatedValue, true);
checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
}
/**
@ -136,7 +136,7 @@ export function ɵɵclassMapInterpolate4(
suffix: string): void {
const lView = getLView();
const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
checkStylingMap(arrayMapSet, classStringParser, interpolatedValue, true);
checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
}
/**
@ -175,7 +175,7 @@ export function ɵɵclassMapInterpolate5(
const lView = getLView();
const interpolatedValue =
interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
checkStylingMap(arrayMapSet, classStringParser, interpolatedValue, true);
checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
}
/**
@ -216,7 +216,7 @@ export function ɵɵclassMapInterpolate6(
const lView = getLView();
const interpolatedValue =
interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
checkStylingMap(arrayMapSet, classStringParser, interpolatedValue, true);
checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
}
/**
@ -259,7 +259,7 @@ export function ɵɵclassMapInterpolate7(
const lView = getLView();
const interpolatedValue =
interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
checkStylingMap(arrayMapSet, classStringParser, interpolatedValue, true);
checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
}
/**
@ -305,7 +305,7 @@ export function ɵɵclassMapInterpolate8(
const lView = getLView();
const interpolatedValue = interpolation8(
lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
checkStylingMap(arrayMapSet, classStringParser, interpolatedValue, true);
checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
}
/**
@ -334,5 +334,5 @@ export function ɵɵclassMapInterpolate8(
export function ɵɵclassMapInterpolateV(values: any[]): void {
const lView = getLView();
const interpolatedValue = interpolationV(lView, values);
checkStylingMap(arrayMapSet, classStringParser, interpolatedValue, true);
checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
}

View File

@ -8,7 +8,7 @@
import {AttributeMarker, ComponentTemplate} from '..';
import {SchemaMetadata} from '../../core';
import {ArrayMap} from '../../util/array_utils';
import {KeyValueArray} from '../../util/array_utils';
import {assertDefined} from '../../util/assert';
import {createNamedArrayType} from '../../util/named_array_type';
import {initNgDevMode} from '../../util/ng_dev_mode';
@ -176,9 +176,9 @@ class TNode implements ITNode {
public parent: TElementNode|TContainerNode|null, //
public projection: number|(ITNode|RNode[])[]|null, //
public styles: string|null, //
public residualStyles: ArrayMap<any>|undefined|null, //
public residualStyles: KeyValueArray<any>|undefined|null, //
public classes: string|null, //
public residualClasses: ArrayMap<any>|undefined|null, //
public residualClasses: KeyValueArray<any>|undefined|null, //
public classBindings: TStylingRange, //
public styleBindings: TStylingRange, //
public directives: TDirectiveDefs|null, //
@ -241,7 +241,8 @@ class TNode implements ITNode {
export const TNodeDebug = TNode;
export type TNodeDebug = TNode;
export interface DebugStyleBindings extends Array<ArrayMap<any>|DebugStyleBinding|string|null> {}
export interface DebugStyleBindings extends
Array<KeyValueArray<any>|DebugStyleBinding|string|null> {}
export interface DebugStyleBinding {
key: TStylingKey;
index: number;

View File

@ -31,12 +31,13 @@ import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, isRoo
import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, TViewType, T_HOST} from '../interfaces/view';
import {assertNodeOfPossibleTypes} from '../node_assert';
import {isNodeMatchingSelectorList} from '../node_selector_matcher';
import {clearActiveHostElement, enterView, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getPreviousOrParentTNode, getSelectedIndex, leaveView, setActiveHostElement, setBindingIndex, setBindingRootForHostBindings, setCheckNoChangesMode, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state';
import {enterView, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getPreviousOrParentTNode, getSelectedIndex, leaveView, setBindingIndex, setBindingRootForHostBindings, setCheckNoChangesMode, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state';
import {NO_CHANGE} from '../tokens';
import {isAnimationProp, mergeHostAttrs} from '../util/attrs_utils';
import {INTERPOLATION_DELIMITER, renderStringify, stringifyForError} from '../util/misc_utils';
import {getLViewParent} from '../util/view_traversal_utils';
import {getComponentLViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isCreationMode, readPatchedLView, resetPreOrderHookFlags, viewAttachedToChangeDetector} from '../util/view_utils';
import {selectIndexInternal} from './advance';
import {LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeDebug, TNodeInitialInputs, TNodeLocalNames, TViewComponents, TViewConstructor, attachLContainerDebug, attachLViewDebug, cloneToLViewFromTViewBlueprint, cloneToTViewData} from './lview_debug';
@ -49,7 +50,7 @@ import {LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeDebug, TNodeIniti
const _CLEAN_PROMISE = (() => Promise.resolve(null))();
/**
* Process the `TVIew.expandoInstructions`. (Execute the `hostBindings`.)
* Process the `TView.expandoInstructions`. (Execute the `hostBindings`.)
*
* @param tView `TView` containing the `expandoInstructions`
* @param lView `LView` associated with the `TView`
@ -62,7 +63,7 @@ export function setHostBindingsByExecutingExpandoInstructions(tView: TView, lVie
let bindingRootIndex = tView.expandoStartIndex;
let currentDirectiveIndex = -1;
let currentElementIndex = -1;
// TODO(misko): PERF It is possible to get here with `TVIew.expandoInstructions` containing no
// TODO(misko): PERF It is possible to get here with `TView.expandoInstructions` containing no
// functions to execute. This is wasteful as there is no work to be done, but we still need
// to iterate over the instructions.
// In example of this is in this test: `host_binding_spec.ts`
@ -81,7 +82,7 @@ export function setHostBindingsByExecutingExpandoInstructions(tView: TView, lVie
// TODO(misko): PERF This should be refactored to use `~instruction` as that does not
// suffer from `-0` and it is faster/more compact.
currentElementIndex = 0 - instruction;
setActiveHostElement(currentElementIndex);
setSelectedIndex(currentElementIndex);
// Injector block and providers are taken into account.
const providerCount = (expandoInstructions[++i] as number);
@ -112,7 +113,7 @@ export function setHostBindingsByExecutingExpandoInstructions(tView: TView, lVie
}
}
} finally {
clearActiveHostElement();
setSelectedIndex(-1);
}
}
@ -528,7 +529,7 @@ function executeTemplate<T>(
lView: LView, templateFn: ComponentTemplate<T>, rf: RenderFlags, context: T) {
const prevSelectedIndex = getSelectedIndex();
try {
clearActiveHostElement();
setSelectedIndex(-1);
if (rf & RenderFlags.Update && lView.length > HEADER_OFFSET) {
// When we're updating, inherently select 0 so we don't
// have to generate that instruction for most update blocks.
@ -1264,7 +1265,7 @@ function invokeDirectivesHostBindings(tView: TView, lView: LView, tNode: TNode)
const firstCreatePass = tView.firstCreatePass;
const elementIndex = tNode.index - HEADER_OFFSET;
try {
setActiveHostElement(elementIndex);
setSelectedIndex(elementIndex);
for (let i = start; i < end; i++) {
const def = tView.data[i] as DirectiveDef<any>;
const directive = lView[i];
@ -1275,7 +1276,7 @@ function invokeDirectivesHostBindings(tView: TView, lView: LView, tNode: TNode)
}
}
} finally {
clearActiveHostElement();
setSelectedIndex(-1);
}
}

View File

@ -9,7 +9,7 @@
import {SafeValue, unwrapSafeValue} from '../../sanitization/bypass';
import {stylePropNeedsSanitization, ɵɵsanitizeStyle} from '../../sanitization/sanitization';
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
import {ArrayMap, arrayMapGet, arrayMapSet} from '../../util/array_utils';
import {KeyValueArray, keyValueArrayGet, keyValueArraySet} from '../../util/array_utils';
import {assertDefined, assertEqual, assertGreaterThanOrEqual, assertLessThan, assertNotEqual, assertNotSame, throwError} from '../../util/assert';
import {EMPTY_ARRAY} from '../../util/empty';
import {concatStringsWithSpace, stringify} from '../../util/stringify';
@ -122,22 +122,22 @@ export function ɵɵclassProp(
export function ɵɵstyleMap(
styles: {[styleName: string]: any} | Map<string, string|number|null|undefined>| string |
undefined | null): void {
checkStylingMap(styleArrayMapSet, styleStringParser, styles, false);
checkStylingMap(styleKeyValueArraySet, styleStringParser, styles, false);
}
/**
* Parse text as style and add values to ArrayMap.
* Parse text as style and add values to KeyValueArray.
*
* This code is pulled out to a separate function so that it can be tree shaken away if it is not
* needed. It is only reference from `ɵɵstyleMap`.
* needed. It is only referenced from `ɵɵstyleMap`.
*
* @param arrayMap ArrayMap to add parsed values to.
* @param keyValueArray KeyValueArray to add parsed values to.
* @param text text to parse.
*/
export function styleStringParser(arrayMap: ArrayMap<any>, text: string): void {
export function styleStringParser(keyValueArray: KeyValueArray<any>, text: string): void {
for (let i = parseStyle(text); i >= 0; i = parseStyleNext(text, i)) {
styleArrayMapSet(arrayMap, getLastParsedKey(text), getLastParsedValue(text));
styleKeyValueArraySet(keyValueArray, getLastParsedKey(text), getLastParsedValue(text));
}
}
@ -163,21 +163,21 @@ export function styleStringParser(arrayMap: ArrayMap<any>, text: string): void {
export function ɵɵclassMap(
classes: {[className: string]: boolean | undefined | null} |
Map<string, boolean|undefined|null>| Set<string>| string[] | string | undefined | null): void {
checkStylingMap(arrayMapSet, classStringParser, classes, true);
checkStylingMap(keyValueArraySet, classStringParser, classes, true);
}
/**
* Parse text as class and add values to ArrayMap.
* Parse text as class and add values to KeyValueArray.
*
* This code is pulled out to a separate function so that it can be tree shaken away if it is not
* needed. It is only reference from `ɵɵclassMap`.
* needed. It is only referenced from `ɵɵclassMap`.
*
* @param arrayMap ArrayMap to add parsed values to.
* @param keyValueArray KeyValueArray to add parsed values to.
* @param text text to parse.
*/
export function classStringParser(arrayMap: ArrayMap<any>, text: string): void {
export function classStringParser(keyValueArray: KeyValueArray<any>, text: string): void {
for (let i = parseClassName(text); i >= 0; i = parseClassNameNext(text, i)) {
arrayMapSet(arrayMap, getLastParsedKey(text), true);
keyValueArraySet(keyValueArray, getLastParsedKey(text), true);
}
}
@ -199,7 +199,7 @@ export function checkStylingProperty(
// 2. one for the intermittent-value / TStylingRange
const bindingIndex = incrementBindingIndex(2);
if (tView.firstUpdatePass) {
stylingPropertyFirstUpdatePass(tView, prop, bindingIndex, isClassBased);
stylingFirstUpdatePass(tView, prop, bindingIndex, isClassBased);
}
if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) {
// This is a work around. Once PR#34480 lands the sanitizer is passed explicitly and this line
@ -219,21 +219,26 @@ export function checkStylingProperty(
}
/**
* Common code between `ɵɵclassMap` and `ɵɵstyleMap`.
*
* @param tStylingMapKey See `STYLE_MAP_STYLING_KEY` and `CLASS_MAP_STYLING_KEY`.
* @param value binding value.
* @param isClassBased `true` if `class` change (`false` if `style`)
*/
* Common code between `ɵɵclassMap` and `ɵɵstyleMap`.
*
* @param keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a
* function so that
* `style` can pass in version which does sanitization. This is done for tree shaking
* purposes.
* @param stringParser Parser used to parse `value` if `string`. (Passed in as `style` and `class`
* have different parsers.)
* @param value bound value from application
* @param isClassBased `true` if `class` change (`false` if `style`)
*/
export function checkStylingMap(
arrayMapSet: (arrayMap: ArrayMap<any>, key: string, value: any) => void,
stringParser: (styleArrayMap: ArrayMap<any>, text: string) => void, value: any|NO_CHANGE,
isClassBased: boolean): void {
keyValueArraySet: (keyValueArray: KeyValueArray<any>, key: string, value: any) => void,
stringParser: (styleKeyValueArray: KeyValueArray<any>, text: string) => void,
value: any|NO_CHANGE, isClassBased: boolean): void {
const lView = getLView();
const tView = lView[TVIEW];
const bindingIndex = incrementBindingIndex(2);
if (tView.firstUpdatePass) {
stylingPropertyFirstUpdatePass(tView, null, bindingIndex, isClassBased);
stylingFirstUpdatePass(tView, null, bindingIndex, isClassBased);
}
if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) {
// `getSelectedIndex()` should be here (rather than in instruction) so that it is guarded by the
@ -252,8 +257,8 @@ export function checkStylingMap(
// Instead VE just ignores the static completely if dynamic binding is present.
// Because of locality we have already set the static portion because we don't know if there
// is a dynamic portion until later. If we would ignore the static portion it would look like
// tha the binding has removed it. This would confuse `[ngStyle]`/`[ngClass]` to do the wrong
// thing as it would think tha the static portion was removed. For this reason we
// the binding has removed it. This would confuse `[ngStyle]`/`[ngClass]` to do the wrong
// thing as it would think that the static portion was removed. For this reason we
// concatenate it so that `[ngStyle]`/`[ngClass]` can continue to work on changed.
let staticPrefix = isClassBased ? tNode.classes : tNode.styles;
ngDevMode && isClassBased === false && staticPrefix !== null &&
@ -268,7 +273,7 @@ export function checkStylingMap(
} else {
updateStylingMap(
tView, tNode, lView, lView[RENDERER], lView[bindingIndex + 1],
lView[bindingIndex + 1] = toStylingArrayMap(arrayMapSet, stringParser, value),
lView[bindingIndex + 1] = toStylingKeyValueArray(keyValueArraySet, stringParser, value),
isClassBased, bindingIndex);
}
}
@ -290,12 +295,11 @@ function isInHostBindings(tView: TView, bindingIndex: number): boolean {
* using `insertTStylingBinding`.
*
* @param tView `TView` where the binding linked list will be stored.
* @param prop Property/key of the binding.
* @param suffix Optional suffix or Sanitization function.
* @param tStylingKey Property/key of the binding.
* @param bindingIndex Index of binding associated with the `prop`
* @param isClassBased `true` if `class` change (`false` if `style`)
*/
function stylingPropertyFirstUpdatePass(
function stylingFirstUpdatePass(
tView: TView, tStylingKey: TStylingKey, bindingIndex: number, isClassBased: boolean): void {
ngDevMode && assertFirstUpdatePass(tView);
const tData = tView.data;
@ -429,16 +433,17 @@ function setTemplateHeadTStylingKey(
tData[getTStylingRangePrev(bindings)] = tStylingKey;
}
function collectResidual(tNode: TNode, isClassBased: boolean): ArrayMap<any>|null {
let residual: ArrayMap<any>|null|undefined = undefined;
function collectResidual(tNode: TNode, isClassBased: boolean): KeyValueArray<any>|null {
let residual: KeyValueArray<any>|null|undefined = undefined;
const directives = tNode.directives;
if (directives) {
for (let i = directives[DirectiveDefs.STYLING_CURSOR] + 1; i < directives.length; i++) {
const attrs = (directives[i] as DirectiveDef<any>).hostAttrs;
residual = collectStylingFromTAttrs(residual, attrs, isClassBased) as ArrayMap<any>| null;
residual =
collectStylingFromTAttrs(residual, attrs, isClassBased) as KeyValueArray<any>| null;
}
}
return collectStylingFromTAttrs(residual, tNode.attrs, isClassBased) as ArrayMap<any>| null;
return collectStylingFromTAttrs(residual, tNode.attrs, isClassBased) as KeyValueArray<any>| null;
}
/**
@ -505,7 +510,8 @@ function collectStylingFromTAttrs(
if (!Array.isArray(stylingKey)) {
stylingKey = stylingKey === undefined ? [] : ['', stylingKey] as any;
}
arrayMapSet(stylingKey as ArrayMap<any>, item, isClassBased ? true : attrs[++i]);
keyValueArraySet(
stylingKey as KeyValueArray<any>, item, isClassBased ? true : attrs[++i]);
}
}
}
@ -525,71 +531,78 @@ export function getHostDirectiveDef(tData: TData): DirectiveDef<any>|null {
}
/**
* Convert user input to `ArrayMap`.
* Convert user input to `KeyValueArray`.
*
* This function takes user input which could be `string`, Object literal, or iterable and converts
* it into a consistent representation. The output of this is `ArrayMap` (which is an array where
* it into a consistent representation. The output of this is `KeyValueArray` (which is an array
* where
* even indexes contain keys and odd indexes contain values for those keys).
*
* The advantage of converting to `ArrayMap` is that we can perform diff in a input independent way.
* The advantage of converting to `KeyValueArray` is that we can perform diff in an input
* independent
* way.
* (ie we can compare `foo bar` to `['bar', 'baz'] and determine a set of changes which need to be
* applied)
*
* The fact that `ArrayMap` is sorted is very important because it allows us to compute the
* The fact that `KeyValueArray` is sorted is very important because it allows us to compute the
* difference in linear fashion without the need to allocate any additional data.
*
* For example if we kept this as a `Map` we would have to iterate over previous `Map` to determine
* which values need to be delete, over the new `Map` to determine additions, and we would have to
* which values need to be deleted, over the new `Map` to determine additions, and we would have to
* keep additional `Map` to keep track of duplicates or items which have not yet been visited.
*
* @param keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a
* function so that
* `style` can pass in version which does sanitization. This is done for tree shaking
* purposes.
* @param stringParser The parser is passed in so that it will be tree shakable. See
* `styleStringParser` and `classStringParser`
* @param value The value to parse/convert to `ArrayMap`
* @param value The value to parse/convert to `KeyValueArray`
*/
export function toStylingArrayMap(
arrayMapSet: (arrayMap: ArrayMap<any>, key: string, value: any) => void,
stringParser: (styleArrayMap: ArrayMap<any>, text: string) => void, value: string|string[]|
{[key: string]: any}|Map<any, any>|Set<any>|null|undefined): ArrayMap<any> {
if (value === null || value === undefined || value === '') return EMPTY_ARRAY as any;
const styleArrayMap: ArrayMap<any> = [] as any;
export function toStylingKeyValueArray(
keyValueArraySet: (keyValueArray: KeyValueArray<any>, key: string, value: any) => void,
stringParser: (styleKeyValueArray: KeyValueArray<any>, text: string) => void, value: string|
string[]|{[key: string]: any}|Map<any, any>|Set<any>|null|undefined): KeyValueArray<any> {
if (value == null /*|| value === undefined */ || value === '') return EMPTY_ARRAY as any;
const styleKeyValueArray: KeyValueArray<any> = [] as any;
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
arrayMapSet(styleArrayMap, value[i], true);
keyValueArraySet(styleKeyValueArray, value[i], true);
}
} else if (typeof value === 'object') {
if (value instanceof Map) {
value.forEach((v, k) => arrayMapSet(styleArrayMap, k, v));
value.forEach((v, k) => keyValueArraySet(styleKeyValueArray, k, v));
} else if (value instanceof Set) {
value.forEach((k) => arrayMapSet(styleArrayMap, k, true));
value.forEach((k) => keyValueArraySet(styleKeyValueArray, k, true));
} else {
for (const key in value) {
if (value.hasOwnProperty(key)) {
arrayMapSet(styleArrayMap, key, value[key]);
keyValueArraySet(styleKeyValueArray, key, value[key]);
}
}
}
} else if (typeof value === 'string') {
stringParser(styleArrayMap, value);
stringParser(styleKeyValueArray, value);
} else {
ngDevMode && throwError('Unsupported styling type ' + typeof value);
ngDevMode && throwError('Unsupported styling type ' + typeof value + ': ' + value);
}
return styleArrayMap;
return styleKeyValueArray;
}
/**
* Set a `value` for a `key` taking style sanitization into account.
*
* See: `arrayMapSet` for details
* See: `keyValueArraySet` for details
*
* @param arrayMap ArrayMap to add to.
* @param keyValueArray KeyValueArray to add to.
* @param key Style key to add. (This key will be checked if it needs sanitization)
* @param value The value to set (If key needs sanitization it will be sanitized)
*/
function styleArrayMapSet(arrayMap: ArrayMap<any>, key: string, value: any) {
function styleKeyValueArraySet(keyValueArray: KeyValueArray<any>, key: string, value: any) {
if (stylePropNeedsSanitization(key)) {
value = ɵɵsanitizeStyle(value);
}
arrayMapSet(arrayMap, key, value);
keyValueArraySet(keyValueArray, key, value);
}
/**
@ -598,34 +611,38 @@ function styleArrayMapSet(arrayMap: ArrayMap<any>, key: string, value: any) {
* Map based styling could be anything which contains more than one binding. For example `string`,
* `Map`, `Set` or object literal. Dealing with all of these types would complicate the logic so
* instead this function expects that the complex input is first converted into normalized
* `ArrayMap`. The advantage of normalization is that we get the values sorted, which makes it very
* `KeyValueArray`. The advantage of normalization is that we get the values sorted, which makes it
* very
* cheap to compute deltas between the previous and current value.
*
* @param tView Associated `TView.data` contains the linked list of binding priorities.
* @param tNode `TNode` where the binding is located.
* @param lView `LView` contains the values associated with other styling binding at this `TNode`.
* @param renderer Renderer to use if any updates.
* @param oldArrayMap Previous value represented as `ArrayMap`
* @param newArrayMap Current value represented as `ArrayMap`
* @param oldKeyValueArray Previous value represented as `KeyValueArray`
* @param newKeyValueArray Current value represented as `KeyValueArray`
* @param isClassBased `true` if `class` (`false` if `style`)
* @param bindingIndex Binding index of the binding.
*/
function updateStylingMap(
tView: TView, tNode: TNode, lView: LView, renderer: Renderer3, oldArrayMap: ArrayMap<any>,
newArrayMap: ArrayMap<any>, isClassBased: boolean, bindingIndex: number) {
if (oldArrayMap as ArrayMap<any>| NO_CHANGE === NO_CHANGE) {
// ON first execution the oldArrayMap is NO_CHANGE => treat is as empty ArrayMap.
oldArrayMap = EMPTY_ARRAY as any;
tView: TView, tNode: TNode, lView: LView, renderer: Renderer3,
oldKeyValueArray: KeyValueArray<any>, newKeyValueArray: KeyValueArray<any>,
isClassBased: boolean, bindingIndex: number) {
if (oldKeyValueArray as KeyValueArray<any>| NO_CHANGE === NO_CHANGE) {
// On first execution the oldKeyValueArray is NO_CHANGE => treat it as empty KeyValueArray.
oldKeyValueArray = EMPTY_ARRAY as any;
}
let oldIndex = 0;
let newIndex = 0;
let oldKey: string|null = 0 < oldArrayMap.length ? oldArrayMap[0] : null;
let newKey: string|null = 0 < newArrayMap.length ? newArrayMap[0] : null;
let oldKey: string|null = 0 < oldKeyValueArray.length ? oldKeyValueArray[0] : null;
let newKey: string|null = 0 < newKeyValueArray.length ? newKeyValueArray[0] : null;
while (oldKey !== null || newKey !== null) {
ngDevMode && assertLessThan(oldIndex, 999, 'Are we stuck in infinite loop?');
ngDevMode && assertLessThan(newIndex, 999, 'Are we stuck in infinite loop?');
const oldValue = oldIndex < oldArrayMap.length ? oldArrayMap[oldIndex + 1] : undefined;
const newValue = newIndex < newArrayMap.length ? newArrayMap[newIndex + 1] : undefined;
const oldValue =
oldIndex < oldKeyValueArray.length ? oldKeyValueArray[oldIndex + 1] : undefined;
const newValue =
newIndex < newKeyValueArray.length ? newKeyValueArray[newIndex + 1] : undefined;
let setKey: string|null = null;
let setValue: any = undefined;
if (oldKey === newKey) {
@ -637,11 +654,16 @@ function updateStylingMap(
setValue = newValue;
}
} else if (newKey === null || oldKey !== null && oldKey < newKey !) {
// DELETE: oldKey key is missing or we did not find the oldKey in the newValue.
// DELETE: oldKey key is missing or we did not find the oldKey in the newValue
// (because the keyValueArray is sorted and `newKey` is found later alphabetically).
// `"background" < "color"` so we need to delete `"background"` because it is not found in the
// new array.
oldIndex += 2;
setKey = oldKey;
} else {
// CREATE: newKey is less than oldKey (or no oldKey) => we have new key.
// CREATE: newKey's is earlier alphabetically than oldKey's (or no oldKey) => we have new key.
// `"color" > "background"` so we need to add `color` because it is in new array but not in
// old array.
ngDevMode && assertDefined(newKey, 'Expecting to have a valid key');
newIndex += 2;
setKey = newKey;
@ -650,8 +672,8 @@ function updateStylingMap(
if (setKey !== null) {
updateStyling(tView, tNode, lView, renderer, setKey, setValue, isClassBased, bindingIndex);
}
oldKey = oldIndex < oldArrayMap.length ? oldArrayMap[oldIndex] : null;
newKey = newIndex < newArrayMap.length ? newArrayMap[newIndex] : null;
oldKey = oldIndex < oldKeyValueArray.length ? oldKeyValueArray[oldIndex] : null;
newKey = newIndex < newKeyValueArray.length ? newKeyValueArray[newIndex] : null;
}
}
@ -668,7 +690,7 @@ function updateStylingMap(
* @param lView `LView` contains the values associated with other styling binding at this `TNode`.
* @param renderer Renderer to use if any updates.
* @param prop Either style property name or a class name.
* @param value Either style vale for `prop` or `true`/`false` if `prop` is class.
* @param value Either style value for `prop` or `true`/`false` if `prop` is class.
* @param isClassBased `true` if `class` (`false` if `style`)
* @param bindingIndex Binding index of the binding.
*/
@ -700,20 +722,22 @@ function updateStyling(
}
/**
* Search for styling value with higher priority which is overwriting current value.
* Search for styling value with higher priority which is overwriting current value, or a
* value of lower priority to which we should fall back if the value is `undefined`.
*
* When value is being applied at a location related values need to be consulted.
* When value is being applied at a location, related values need to be consulted.
* - If there is a higher priority binding, we should be using that one instead.
* For example `<div [style]="{color:exp1}" [style.color]="exp2">` change to `exp1`
* requires that we check `exp2` to see if it is set to value other than `undefined`.
* - If there is a lower priority binding and we are changing to `undefined`
* For example `<div [style]="{color:exp1}" [style.color]="exp2">` change to `exp2` to
* `undefined` requires that we check `exp` (and static values) and use that as new value.
* `undefined` requires that we check `exp1` (and static values) and use that as new value.
*
* NOTE: The styling stores two values.
* 1. The raw value which came from the application is stored at `index + 0` location. (This value
* is used for dirty checking).
* 2. The normalized value (converted to `ArrayMap` if map and sanitized) is stored at `index + 1`.
* 2. The normalized value (converted to `KeyValueArray` if map and sanitized) is stored at `index +
* 1`.
* The advantage of storing the sanitized value is that once the value is written we don't need
* to worry about sanitizing it later or keeping track of the sanitizer.
*
@ -731,32 +755,38 @@ function updateStyling(
function findStylingValue(
tData: TData, tNode: TNode | null, lView: LView, prop: string, index: number,
isClassBased: boolean): any {
// `TNode` to use for resolving static styling. Also controls search direction.
// - `TNode` search next and quit as soon as `isStylingValuePresent(value)` is true.
// If no value found consult `tNode.residualStyle`/`tNode.residualClass` for default value.
// - `null` search prev and go all the way to end. Return last value where
// `isStylingValuePresent(value)` is true.
const isPrevDirection = tNode === null;
let value: any = undefined;
while (index > 0) {
const rawKey = tData[index] as TStylingKey;
const containsStatics = Array.isArray(rawKey);
// Unwrap the key if we contain static values.
const key = containsStatics ? (rawKey as string[])[1] : rawKey;
let currentValue = key === null ? arrayMapGet(lView[index + 1], prop) :
let currentValue = key === null ? keyValueArrayGet(lView[index + 1], prop) :
key === prop ? lView[index + 1] : undefined;
if (containsStatics && !isStylingValuePresent(currentValue)) {
currentValue = arrayMapGet(rawKey as ArrayMap<any>, prop);
currentValue = keyValueArrayGet(rawKey as KeyValueArray<any>, prop);
}
if (isStylingValuePresent(currentValue)) {
value = currentValue;
if (tNode === null) {
if (isPrevDirection) {
return value;
}
}
const tRange = tData[index + 1] as TStylingRange;
index = tNode === null ? getTStylingRangePrev(tRange) : getTStylingRangeNext(tRange);
index = isPrevDirection ? getTStylingRangePrev(tRange) : getTStylingRangeNext(tRange);
}
if (tNode !== null) {
// in case where we are going in next direction AND we did not find anything, we need to
// consult residual styling
let residual = isClassBased ? tNode.residualClasses : tNode.residualStyles;
if (residual != null /** OR residual !=== undefined */) {
value = arrayMapGet(residual !, prop);
value = keyValueArrayGet(residual !, prop);
}
}
return value;
@ -776,38 +806,6 @@ function isStylingValuePresent(value: any): boolean {
return value !== undefined;
}
/**
* Lazily computes `tNode.classesMap`/`tNode.stylesMap`.
*
* This code is here because we don't want to included it in `elementStart` as it would make hello
* world bigger even if no styling would be present. Instead we initialize the values here so that
* tree shaking will only bring it in if styling is present.
*
* @param tNode `TNode` to initialize.
*/
export function initializeStylingStaticArrayMap(tNode: TNode) {
ngDevMode && assertEqual(tNode.residualClasses, undefined, 'Already initialized!');
ngDevMode && assertEqual(tNode.residualStyles, undefined, 'Already initialized!');
let styleMap: ArrayMap<any>|null = null;
let classMap: ArrayMap<any>|null = null;
const mergeAttrs = tNode.mergedAttrs || EMPTY_ARRAY as TAttributes;
let mode: AttributeMarker = AttributeMarker.ImplicitAttributes;
for (let i = 0; i < mergeAttrs.length; i++) {
let item = mergeAttrs[i];
if (typeof item === 'number') {
mode = item;
} else if (mode === AttributeMarker.Classes) {
classMap = classMap || [] as any;
arrayMapSet(classMap !, item as string, true);
} else if (mode === AttributeMarker.Styles) {
styleMap = styleMap || [] as any;
arrayMapSet(styleMap !, item as string, mergeAttrs[++i] as string);
}
}
tNode.residualClasses = classMap;
tNode.residualStyles = styleMap;
}
/**
* Sanitizes or adds suffix to the value.
*
@ -818,7 +816,7 @@ export function initializeStylingStaticArrayMap(tNode: TNode) {
function normalizeAndApplySuffixOrSanitizer(
value: any, suffixOrSanitizer: SanitizerFn | string | undefined | null): string|null|undefined|
boolean {
if (value === null || value === undefined) {
if (value == null /** || value === undefined */) {
// do nothing
} else if (typeof suffixOrSanitizer === 'function') {
// sanitize the value.

View File

@ -5,7 +5,7 @@
* 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 {ArrayMap} from '../../util/array_utils';
import {KeyValueArray} from '../../util/array_utils';
import {TStylingRange} from '../interfaces/styling';
import {DirectiveDef} from './definition';
@ -498,7 +498,7 @@ export interface TNode {
styles: string|null;
/**
* An `ArrayMap` version of residual `styles`.
* A `KeyValueArray` version of residual `styles`.
*
* When there are styling instructions than each instruction stores the static styling
* which is of lower priority than itself. This means that there may be a higher priority styling
@ -522,9 +522,9 @@ export interface TNode {
*
* - `undefined': not initialized.
* - `null`: initialized but `styles` is `null`
* - `ArrayMap`: parsed version of `styles`.
* - `KeyValueArray`: parsed version of `styles`.
*/
residualStyles: ArrayMap<any>|undefined|null;
residualStyles: KeyValueArray<any>|undefined|null;
/**
* A collection of all class static values for an element.
@ -536,15 +536,15 @@ export interface TNode {
classes: string|null;
/**
* An `ArrayMap` version of residual `classes`.
* A `KeyValueArray` version of residual `classes`.
*
* Same as `TNode.residualStyles` but for classes.
*
* - `undefined': not initialized.
* - `null`: initialized but `classes` is `null`
* - `ArrayMap`: parsed version of `S`.
* - `KeyValueArray`: parsed version of `classes`.
*/
residualClasses: ArrayMap<any>|undefined|null;
residualClasses: KeyValueArray<any>|undefined|null;
/**
* Stores the head/tail index of the class bindings.

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ArrayMap} from '../../util/array_utils';
import {KeyValueArray} from '../../util/array_utils';
import {assertNumber, assertNumberInRange} from '../../util/assert';
/**
@ -32,7 +32,7 @@ export type TStylingKeyPrimitive = string | null | false;
/**
* Store the static values for the styling binding.
*
* The `TStylingStatic` is just `ArrayMap` where key `""` (stored at location 0) contains the
* The `TStylingStatic` is just `KeyValueArray` where key `""` (stored at location 0) contains the
* `TStylingKey` (stored at location 1). In other words this wraps the `TStylingKey` such that the
* `""` contains the wrapped value.
*
@ -89,7 +89,7 @@ export type TStylingKeyPrimitive = string | null | false;
*
* This means that it is safe to add class.
*/
export interface TStylingStatic extends ArrayMap<any> {}
export interface TStylingStatic extends KeyValueArray<any> {}
/**
* This is a branded number which contains previous and next index.

View File

@ -915,14 +915,15 @@ function applyContainer(
* @param isClassBased `true` if it should be written to `class` (`false` to write to `style`)
* @param rNode The Node to write to.
* @param prop Property to write to. This would be the class/style name.
* @param value Value to wiret. If `null`/`undefined`/`false` this is consider a remove (set/add
* @param value Value to write. If `null`/`undefined`/`false` this is considered a remove (set/add
* otherwise).
*/
export function applyStyling(
renderer: Renderer3, isClassBased: boolean, rNode: RElement, prop: string, value: any) {
const isProcedural = isProceduralRenderer(renderer);
if (isClassBased) {
if (!value) { // We actually want JS falseness here
// We actually want JS true/false here because any truthy value should add the class
if (!value) {
ngDevMode && ngDevMode.rendererRemoveClass++;
if (isProcedural) {
(renderer as Renderer2).removeClass(rNode, prop);
@ -942,7 +943,7 @@ export function applyStyling(
// TODO(misko): Can't import RendererStyleFlags2.DashCase as it causes imports to be resolved in
// different order which causes failures. Using direct constant as workaround for now.
const flags = prop.indexOf('-') == -1 ? undefined : 2 /* RendererStyleFlags2.DashCase */;
if (value === null || value === undefined) {
if (value == null /** || value === undefined */) {
ngDevMode && ngDevMode.rendererRemoveStyle++;
if (isProcedural) {
(renderer as Renderer2).removeStyle(rNode, prop, flags);

View File

@ -240,21 +240,6 @@ export function getLView(): LView {
return lFrame === null ? null ! : lFrame.lView;
}
/**
* Sets the active directive host element and resets the directive id value
* (when the provided elementIndex value has changed).
*
* @param elementIndex the element index value for the host element where
* the directive/component instance lives
*/
export function setActiveHostElement(elementIndex: number) {
setSelectedIndex(elementIndex);
}
export function clearActiveHostElement() {
setSelectedIndex(-1);
}
/**
* Restores `contextViewData` to the given OpaqueViewState instance.
*
@ -562,11 +547,3 @@ export function getCurrentStyleSanitizer() {
const lFrame = instructionState.lFrame;
return lFrame === null ? null : lFrame.currentSanitizer;
}
/**
* Used for encoding both Class and Style index into `LFrame.stylingBindingChanged`.
*/
const enum BindingChanged {
CLASS_SHIFT = 16,
STYLE_MASK = 0xFFFF,
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ArrayMap, arrayMapIndexOf} from '../../util/array_utils';
import {KeyValueArray, keyValueArrayIndexOf} from '../../util/array_utils';
import {assertDataInRange, assertEqual, assertNotEqual} from '../../util/assert';
import {assertFirstUpdatePass} from '../assert';
import {TNode} from '../interfaces/node';
@ -204,10 +204,11 @@ export function insertTStylingBinding(
let tStylingKey: TStylingKeyPrimitive;
if (Array.isArray(tStylingKeyWithStatic)) {
// We are case when the `TStylingKey` contains static fields as well.
const staticArrayMap = tStylingKeyWithStatic as ArrayMap<any>;
tStylingKey = staticArrayMap[1]; // unwrap.
const staticKeyValueArray = tStylingKeyWithStatic as KeyValueArray<any>;
tStylingKey = staticKeyValueArray[1]; // unwrap.
// We need to check if our key is present in the static so that we can mark it as duplicate.
if (tStylingKey === null || arrayMapIndexOf(staticArrayMap, tStylingKey as string) > 0) {
if (tStylingKey === null ||
keyValueArrayIndexOf(staticKeyValueArray, tStylingKey as string) > 0) {
// tStylingKey is present in the statics, need to mark it as duplicate.
isKeyDuplicateOfStatic = true;
}
@ -292,7 +293,7 @@ function markDuplicateOfResidualStyling(
tNode: TNode, tStylingKey: TStylingKey, tData: TData, index: number, isClassBinding: boolean) {
const residual = isClassBinding ? tNode.residualClasses : tNode.residualStyles;
if (residual != null /* or undefined */ && typeof tStylingKey == 'string' &&
arrayMapIndexOf(residual, tStylingKey) >= 0) {
keyValueArrayIndexOf(residual, tStylingKey) >= 0) {
// We have duplicate in the residual so mark ourselves as duplicate.
tData[index + 1] = setTStylingRangeNextDuplicate(tData[index + 1] as TStylingRange);
}
@ -418,9 +419,10 @@ function isStylingMatch(tStylingKeyCursor: TStylingKey, tStylingKey: TStylingKey
) {
return true;
} else if (Array.isArray(tStylingKeyCursor) && typeof tStylingKey === 'string') {
// if we did not find a match, but `tStylingKeyCursor` is `ArrayMap` that means cursor has
// if we did not find a match, but `tStylingKeyCursor` is `KeyValueArray` that means cursor has
// statics and we need to check those as well.
return arrayMapIndexOf(tStylingKeyCursor, tStylingKey) >= 0; // see if we are matching the key
return keyValueArrayIndexOf(tStylingKeyCursor, tStylingKey) >=
0; // see if we are matching the key
}
return false;
}

View File

@ -105,8 +105,6 @@ export function arraySplice(array: any[], index: number, count: number): void {
* removed. This causes memory pressure and slows down code when most of the time we don't
* care about the deleted items array.
*
* https://jsperf.com/fast-array-splice (About 20x faster)
*
* @param array Array to splice.
* @param index Index in array where the `value` should be added.
* @param value Value to add to array.
@ -129,8 +127,6 @@ export function arrayInsert(array: any[], index: number, value: any): void {
* removed. This causes memory pressure and slows down code when most of the time we don't
* care about the deleted items array.
*
* https://jsperf.com/fast-array-splice (About 20x faster)
*
* @param array Array to splice.
* @param index Index in array where the `value` should be added.
* @param value1 Value to add to array.
@ -221,35 +217,36 @@ export function arrayIndexOfSorted(array: string[], value: string): number {
/**
* `ArrayMap` is an array where even positions contain keys and odd positions contain values.
* `KeyValueArray` is an array where even positions contain keys and odd positions contain values.
*
* `ArrayMap` provides a very efficient way of iterating over its contents. For small
* sets (~10) the cost of binary searching an `ArrayMap` has about the same performance
* `KeyValueArray` provides a very efficient way of iterating over its contents. For small
* sets (~10) the cost of binary searching an `KeyValueArray` has about the same performance
* characteristics that of a `Map` with significantly better memory footprint.
*
* If used as a `Map` the keys are stored in alphabetical order so that they can be binary searched
* for retrieval.
*
* See: `arrayMapSet`, `arrayMapGet`, `arrayMapIndexOf`, `arrayMapDelete`.
* See: `keyValueArraySet`, `keyValueArrayGet`, `keyValueArrayIndexOf`, `keyValueArrayDelete`.
*/
export interface ArrayMap<VALUE> extends Array<VALUE|string> { __brand__: 'array-map'; }
export interface KeyValueArray<VALUE> extends Array<VALUE|string> { __brand__: 'array-map'; }
/**
* Set a `value` for a `key`.
*
* @param arrayMap to modify.
* @param keyValueArray to modify.
* @param key The key to locate or create.
* @param value The value to set for a `key`.
* @returns index (always even) of where the value vas set.
*/
export function arrayMapSet<V>(arrayMap: ArrayMap<V>, key: string, value: V): number {
let index = arrayMapIndexOf(arrayMap, key);
export function keyValueArraySet<V>(
keyValueArray: KeyValueArray<V>, key: string, value: V): number {
let index = keyValueArrayIndexOf(keyValueArray, key);
if (index >= 0) {
// if we found it set it.
arrayMap[index | 1] = value;
keyValueArray[index | 1] = value;
} else {
index = ~index;
arrayInsert2(arrayMap, index, key, value);
arrayInsert2(keyValueArray, index, key, value);
}
return index;
}
@ -257,15 +254,15 @@ export function arrayMapSet<V>(arrayMap: ArrayMap<V>, key: string, value: V): nu
/**
* Retrieve a `value` for a `key` (on `undefined` if not found.)
*
* @param arrayMap to search.
* @param keyValueArray to search.
* @param key The key to locate.
* @return The `value` stored at the `key` location or `undefined if not found.
*/
export function arrayMapGet<V>(arrayMap: ArrayMap<V>, key: string): V|undefined {
const index = arrayMapIndexOf(arrayMap, key);
export function keyValueArrayGet<V>(keyValueArray: KeyValueArray<V>, key: string): V|undefined {
const index = keyValueArrayIndexOf(keyValueArray, key);
if (index >= 0) {
// if we found it retrieve it.
return arrayMap[index | 1] as V;
return keyValueArray[index | 1] as V;
}
return undefined;
}
@ -273,32 +270,32 @@ export function arrayMapGet<V>(arrayMap: ArrayMap<V>, key: string): V|undefined
/**
* Retrieve a `key` index value in the array or `-1` if not found.
*
* @param arrayMap to search.
* @param keyValueArray to search.
* @param key The key to locate.
* @returns index of where the key is (or should have been.)
* - positive (even) index if key found.
* - negative index if key not found. (`~index` (even) to get the index where it should have
* been inserted.)
*/
export function arrayMapIndexOf<V>(arrayMap: ArrayMap<V>, key: string): number {
return _arrayIndexOfSorted(arrayMap as string[], key, 1);
export function keyValueArrayIndexOf<V>(keyValueArray: KeyValueArray<V>, key: string): number {
return _arrayIndexOfSorted(keyValueArray as string[], key, 1);
}
/**
* Delete a `key` (and `value`) from the `ArrayMap`.
* Delete a `key` (and `value`) from the `KeyValueArray`.
*
* @param arrayMap to modify.
* @param keyValueArray to modify.
* @param key The key to locate or delete (if exist).
* @returns index of where the key was (or should have been.)
* - positive (even) index if key found and deleted.
* - negative index if key not found. (`~index` (even) to get the index where it should have
* been.)
*/
export function arrayMapDelete<V>(arrayMap: ArrayMap<V>, key: string): number {
const index = arrayMapIndexOf(arrayMap, key);
export function keyValueArrayDelete<V>(keyValueArray: KeyValueArray<V>, key: string): number {
const index = keyValueArrayIndexOf(keyValueArray, key);
if (index >= 0) {
// if we found it remove it.
arraySplice(arrayMap, index, 2);
arraySplice(keyValueArray, index, 2);
}
return index;
}

View File

@ -93,7 +93,7 @@ describe('host bindings', () => {
* - That executes a **different template**, that has host bindings
* - this executes `setHostBindings`
* - Inside of `setHostBindings` we are currently updating the selected index **global
* state** via `setActiveHostElement`.
* state** via `setSelectedIndex`.
* 3. We attempt to update the next property in the **original template**.
* - But the selected index has been altered, and we get errors.
*/

View File

@ -3,7 +3,7 @@
## `js_expected_symbol_test`
This folder contains tests which assert that most of the code is tree shaken away.
This is asserted by keeping gold files of all symbols which are expected to be retained.
When doing renaming it is often necessary to update the gold files, to do so use this commands:
When doing renaming it is often necessary to update the gold files, to do so use these commands:
```
yarn bazel run --config=ivy //packages/core/test/bundling/cyclic_import:symbol_test.accept

View File

@ -185,9 +185,6 @@
{
"name": "classIndexOf"
},
{
"name": "clearActiveHostElement"
},
{
"name": "computeStaticStyling"
},
@ -572,9 +569,6 @@
{
"name": "selectIndexInternal"
},
{
"name": "setActiveHostElement"
},
{
"name": "setBindingIndex"
},

View File

@ -164,9 +164,6 @@
{
"name": "callHooks"
},
{
"name": "clearActiveHostElement"
},
{
"name": "computeStaticStyling"
},
@ -446,9 +443,6 @@
{
"name": "selectIndexInternal"
},
{
"name": "setActiveHostElement"
},
{
"name": "setBindingIndex"
},

View File

@ -344,15 +344,6 @@
{
"name": "arrayInsert2"
},
{
"name": "arrayMapGet"
},
{
"name": "arrayMapIndexOf"
},
{
"name": "arrayMapSet"
},
{
"name": "assertTemplate"
},
@ -395,9 +386,6 @@
{
"name": "cleanUpView"
},
{
"name": "clearActiveHostElement"
},
{
"name": "collectNativeNodes"
},
@ -902,6 +890,15 @@
{
"name": "iterateListLike"
},
{
"name": "keyValueArrayGet"
},
{
"name": "keyValueArrayIndexOf"
},
{
"name": "keyValueArraySet"
},
{
"name": "leaveDI"
},
@ -1079,9 +1076,6 @@
{
"name": "selectIndexInternal"
},
{
"name": "setActiveHostElement"
},
{
"name": "setBindingIndex"
},
@ -1155,7 +1149,7 @@
"name": "stringifyForError"
},
{
"name": "stylingPropertyFirstUpdatePass"
"name": "stylingFirstUpdatePass"
},
{
"name": "syncViewWithBlueprint"

View File

@ -12,7 +12,7 @@ import {TNodeType} from '@angular/core/src/render3/interfaces/node';
import {LView, TView, TViewType} from '@angular/core/src/render3/interfaces/view';
import {enterView, leaveView} from '@angular/core/src/render3/state';
import {insertTStylingBinding} from '@angular/core/src/render3/styling/style_binding_list';
import {ArrayMap} from '@angular/core/src/util/array_utils';
import {KeyValueArray} from '@angular/core/src/util/array_utils';
describe('lView_debug', () => {
@ -36,14 +36,14 @@ describe('lView_debug', () => {
});
it('should decode static styling', () => {
tNode.residualStyles = ['color', 'blue'] as ArrayMap<any>;
tNode.residualClasses = ['STATIC', true] as ArrayMap<any>;
expect(tNode.styleBindings_).toEqual([['color', 'blue'] as ArrayMap<any>]);
expect(tNode.classBindings_).toEqual([['STATIC', true] as ArrayMap<any>]);
tNode.residualStyles = ['color', 'blue'] as KeyValueArray<any>;
tNode.residualClasses = ['STATIC', true] as KeyValueArray<any>;
expect(tNode.styleBindings_).toEqual([['color', 'blue'] as KeyValueArray<any>]);
expect(tNode.classBindings_).toEqual([['STATIC', true] as KeyValueArray<any>]);
});
it('should decode no-template property binding', () => {
tNode.residualClasses = ['STATIC', true] as ArrayMap<any>;
tNode.residualClasses = ['STATIC', true] as KeyValueArray<any>;
insertTStylingBinding(tView.data, tNode, 'CLASS', 2, true, true);
insertTStylingBinding(tView.data, tNode, 'color', 4, true, false);
@ -69,12 +69,12 @@ describe('lView_debug', () => {
prevIndex: 0,
nextIndex: 0,
},
['STATIC', true] as ArrayMap<any>
['STATIC', true] as KeyValueArray<any>
]);
});
it('should decode template and directive property binding', () => {
tNode.residualClasses = ['STATIC', true] as ArrayMap<any>;
tNode.residualClasses = ['STATIC', true] as KeyValueArray<any>;
insertTStylingBinding(tView.data, tNode, 'CLASS', 2, false, true);
insertTStylingBinding(tView.data, tNode, 'color', 4, false, false);
@ -100,7 +100,7 @@ describe('lView_debug', () => {
prevIndex: 0,
nextIndex: 0,
},
['STATIC', true] as ArrayMap<any>
['STATIC', true] as KeyValueArray<any>
]);
insertTStylingBinding(tView.data, tNode, null, 6, true, true);
@ -146,7 +146,7 @@ describe('lView_debug', () => {
prevIndex: 6,
nextIndex: 0,
},
['STATIC', true] as ArrayMap<any>
['STATIC', true] as KeyValueArray<any>
]);
});
});

View File

@ -8,7 +8,7 @@
import {DirectiveDef} from '@angular/core/src/render3';
import {ɵɵdefineDirective} from '@angular/core/src/render3/definition';
import {classStringParser, initializeStylingStaticArrayMap, styleStringParser, toStylingArrayMap, ɵɵclassProp, ɵɵstyleMap, ɵɵstyleProp, ɵɵstyleSanitizer} from '@angular/core/src/render3/instructions/styling';
import {classStringParser, styleStringParser, toStylingKeyValueArray, ɵɵclassProp, ɵɵstyleMap, ɵɵstyleProp, ɵɵstyleSanitizer} from '@angular/core/src/render3/instructions/styling';
import {AttributeMarker, TAttributes, TDirectiveDefs} from '@angular/core/src/render3/interfaces/node';
import {StylingRange, TStylingKey, TStylingRange, getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, setTStylingRangeNext, setTStylingRangePrev, toTStylingRange} from '@angular/core/src/render3/interfaces/styling';
import {HEADER_OFFSET, TVIEW} from '@angular/core/src/render3/interfaces/view';
@ -16,7 +16,7 @@ import {getLView, leaveView, setBindingRootForHostBindings} from '@angular/core/
import {getNativeByIndex} from '@angular/core/src/render3/util/view_utils';
import {bypassSanitizationTrustStyle} from '@angular/core/src/sanitization/bypass';
import {ɵɵsanitizeStyle} from '@angular/core/src/sanitization/sanitization';
import {arrayMapSet} from '@angular/core/src/util/array_utils';
import {keyValueArraySet} from '@angular/core/src/util/array_utils';
import {ngDevModeResetPerfCounters} from '@angular/core/src/util/ng_dev_mode';
import {getElementClasses, getElementStyles} from '@angular/core/testing/src/styling';
import {expect} from '@angular/core/testing/src/testing_internal';
@ -268,31 +268,6 @@ describe('styling', () => {
expect(div.style.getPropertyValue('background')).toContain('url("javascript:/trusted")');
});
});
describe('populateStylingStaticArrayMap', () => {
it('should initialize to null if no mergedAttrs', () => {
const tNode = getLView()[TVIEW].firstChild !;
expect(tNode.residualStyles).toEqual(undefined);
expect(tNode.residualClasses).toEqual(undefined);
initializeStylingStaticArrayMap(tNode);
expect(tNode.residualStyles).toEqual(null);
expect(tNode.residualClasses).toEqual(null);
});
it('should initialize from mergeAttrs', () => {
const tNode = getLView()[TVIEW].firstChild !;
expect(tNode.residualStyles).toEqual(undefined);
expect(tNode.residualClasses).toEqual(undefined);
tNode.mergedAttrs = [
'ignore', 'value', //
AttributeMarker.Classes, 'foo', 'bar', //
AttributeMarker.Styles, 'width', '0', 'color', 'red', //
];
initializeStylingStaticArrayMap(tNode);
expect(tNode.residualClasses).toEqual(['bar', true, 'foo', true] as any);
expect(tNode.residualStyles).toEqual(['color', 'red', 'width', '0'] as any);
});
});
});
describe('static', () => {
@ -428,53 +403,54 @@ describe('styling', () => {
describe('toStylingArray', () => {
describe('falsy', () => {
it('should return empty ArrayMap', () => {
expect(toStylingArrayMap(arrayMapSet, null !, '')).toEqual([] as any);
expect(toStylingArrayMap(arrayMapSet, null !, null)).toEqual([] as any);
expect(toStylingArrayMap(arrayMapSet, null !, undefined)).toEqual([] as any);
expect(toStylingArrayMap(arrayMapSet, null !, [])).toEqual([] as any);
expect(toStylingArrayMap(arrayMapSet, null !, {})).toEqual([] as any);
it('should return empty KeyValueArray', () => {
expect(toStylingKeyValueArray(keyValueArraySet, null !, '')).toEqual([] as any);
expect(toStylingKeyValueArray(keyValueArraySet, null !, null)).toEqual([] as any);
expect(toStylingKeyValueArray(keyValueArraySet, null !, undefined)).toEqual([] as any);
expect(toStylingKeyValueArray(keyValueArraySet, null !, [])).toEqual([] as any);
expect(toStylingKeyValueArray(keyValueArraySet, null !, {})).toEqual([] as any);
});
describe('string', () => {
it('should parse classes', () => {
expect(toStylingArrayMap(arrayMapSet, classStringParser, ' ')).toEqual([] as any);
expect(toStylingArrayMap(arrayMapSet, classStringParser, ' X A ')).toEqual([
expect(toStylingKeyValueArray(keyValueArraySet, classStringParser, ' ')).toEqual([
] as any);
expect(toStylingKeyValueArray(keyValueArraySet, classStringParser, ' X A ')).toEqual([
'A', true, 'X', true
] as any);
});
it('should parse styles', () => {
expect(toStylingArrayMap(arrayMapSet, styleStringParser, ' ')).toEqual([] as any);
expect(toStylingArrayMap(arrayMapSet, styleStringParser, 'B:b;A:a')).toEqual([
expect(toStylingKeyValueArray(keyValueArraySet, styleStringParser, ' ')).toEqual([
] as any);
expect(toStylingKeyValueArray(keyValueArraySet, styleStringParser, 'B:b;A:a')).toEqual([
'A', 'a', 'B', 'b'
] as any);
});
});
describe('array', () => {
it('should parse', () => {
expect(toStylingArrayMap(arrayMapSet, null !, ['X', 'A'])).toEqual([
expect(toStylingKeyValueArray(keyValueArraySet, null !, ['X', 'A'])).toEqual([
'A', true, 'X', true
] as any);
});
});
describe('object', () => {
it('should parse', () => {
expect(toStylingArrayMap(arrayMapSet, null !, {X: 'x', A: 'a'})).toEqual([
expect(toStylingKeyValueArray(keyValueArraySet, null !, {X: 'x', A: 'a'})).toEqual([
'A', 'a', 'X', 'x'
] as any);
});
});
describe('Map', () => {
it('should parse', () => {
expect(toStylingArrayMap(
arrayMapSet, null !, new Map<string, string>([['X', 'x'], ['A', 'a']])))
expect(toStylingKeyValueArray(
keyValueArraySet, null !, new Map<string, string>([['X', 'x'], ['A', 'a']])))
.toEqual(['A', 'a', 'X', 'x'] as any);
});
});
describe('Iterable', () => {
it('should parse', () => {
expect(toStylingArrayMap(arrayMapSet, null !, new Set<string>(['X', 'A']))).toEqual([
'A', true, 'X', true
] as any);
expect(toStylingKeyValueArray(keyValueArraySet, null !, new Set<string>(['X', 'A'])))
.toEqual(['A', true, 'X', true] as any);
});
});
});

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ArrayMap, arrayIndexOfSorted, arrayInsert, arrayInsert2, arrayInsertSorted, arrayMapDelete, arrayMapGet, arrayMapIndexOf, arrayMapSet, arrayRemoveSorted, arraySplice, flatten} from '../../src/util/array_utils';
import {KeyValueArray, arrayIndexOfSorted, arrayInsert, arrayInsert2, arrayInsertSorted, arrayRemoveSorted, arraySplice, flatten, keyValueArrayDelete, keyValueArrayGet, keyValueArrayIndexOf, keyValueArraySet} from '../../src/util/array_utils';
describe('array_utils', () => {
@ -140,38 +140,38 @@ describe('array_utils', () => {
});
});
describe('ArrayMap', () => {
describe('KeyValueArray', () => {
it('should support basic operations', () => {
const map: ArrayMap<number> = [] as any;
const map: KeyValueArray<number> = [] as any;
expect(arrayMapIndexOf(map, 'A')).toEqual(~0);
expect(keyValueArrayIndexOf(map, 'A')).toEqual(~0);
expect(arrayMapSet(map, 'B', 1)).toEqual(0);
expect(keyValueArraySet(map, 'B', 1)).toEqual(0);
expect(map).toEqual(['B', 1]);
expect(arrayMapIndexOf(map, 'B')).toEqual(0);
expect(keyValueArrayIndexOf(map, 'B')).toEqual(0);
expect(arrayMapSet(map, 'A', 0)).toEqual(0);
expect(keyValueArraySet(map, 'A', 0)).toEqual(0);
expect(map).toEqual(['A', 0, 'B', 1]);
expect(arrayMapIndexOf(map, 'B')).toEqual(2);
expect(arrayMapIndexOf(map, 'AA')).toEqual(~2);
expect(keyValueArrayIndexOf(map, 'B')).toEqual(2);
expect(keyValueArrayIndexOf(map, 'AA')).toEqual(~2);
expect(arrayMapSet(map, 'C', 2)).toEqual(4);
expect(keyValueArraySet(map, 'C', 2)).toEqual(4);
expect(map).toEqual(['A', 0, 'B', 1, 'C', 2]);
expect(arrayMapGet(map, 'A')).toEqual(0);
expect(arrayMapGet(map, 'B')).toEqual(1);
expect(arrayMapGet(map, 'C')).toEqual(2);
expect(arrayMapGet(map, 'AA')).toEqual(undefined);
expect(keyValueArrayGet(map, 'A')).toEqual(0);
expect(keyValueArrayGet(map, 'B')).toEqual(1);
expect(keyValueArrayGet(map, 'C')).toEqual(2);
expect(keyValueArrayGet(map, 'AA')).toEqual(undefined);
expect(arrayMapSet(map, 'B', -1)).toEqual(2);
expect(keyValueArraySet(map, 'B', -1)).toEqual(2);
expect(map).toEqual(['A', 0, 'B', -1, 'C', 2]);
expect(arrayMapDelete(map, 'AA')).toEqual(~2);
expect(arrayMapDelete(map, 'B')).toEqual(2);
expect(keyValueArrayDelete(map, 'AA')).toEqual(~2);
expect(keyValueArrayDelete(map, 'B')).toEqual(2);
expect(map).toEqual(['A', 0, 'C', 2]);
expect(arrayMapDelete(map, 'A')).toEqual(0);
expect(keyValueArrayDelete(map, 'A')).toEqual(0);
expect(map).toEqual(['C', 2]);
expect(arrayMapDelete(map, 'C')).toEqual(0);
expect(keyValueArrayDelete(map, 'C')).toEqual(0);
expect(map).toEqual([]);
});
});