This is a follow up for https://github.com/angular/angular/pull/34804 PR Close #35022
This commit is contained in:
parent
ee8b8f52aa
commit
c1cf46c5c3
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -185,9 +185,6 @@
|
|||
{
|
||||
"name": "classIndexOf"
|
||||
},
|
||||
{
|
||||
"name": "clearActiveHostElement"
|
||||
},
|
||||
{
|
||||
"name": "computeStaticStyling"
|
||||
},
|
||||
|
@ -572,9 +569,6 @@
|
|||
{
|
||||
"name": "selectIndexInternal"
|
||||
},
|
||||
{
|
||||
"name": "setActiveHostElement"
|
||||
},
|
||||
{
|
||||
"name": "setBindingIndex"
|
||||
},
|
||||
|
|
|
@ -164,9 +164,6 @@
|
|||
{
|
||||
"name": "callHooks"
|
||||
},
|
||||
{
|
||||
"name": "clearActiveHostElement"
|
||||
},
|
||||
{
|
||||
"name": "computeStaticStyling"
|
||||
},
|
||||
|
@ -446,9 +443,6 @@
|
|||
{
|
||||
"name": "selectIndexInternal"
|
||||
},
|
||||
{
|
||||
"name": "setActiveHostElement"
|
||||
},
|
||||
{
|
||||
"name": "setBindingIndex"
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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([]);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue