feat(ivy): add ΔpropertyInterpolate instructions (#29576)
- Adds the instructions - Adds tests for all instructions - Adds TODO to remove all tests when we are able to test this with TestBed after the compiler is updated PR Close #29576
This commit is contained in:
parent
b71cd7b4ee
commit
a80637e9a1
File diff suppressed because it is too large
Load Diff
|
@ -5,21 +5,14 @@
|
|||
* 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 {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA} from '../../metadata/schema';
|
||||
import {validateAgainstEventProperties} from '../../sanitization/sanitization';
|
||||
import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect';
|
||||
import {assertLView} from '../assert';
|
||||
import {bindingUpdated} from '../bindings';
|
||||
import {PropertyAliasValue, PropertyAliases, TNode, TNodeType} from '../interfaces/node';
|
||||
import {RComment, RElement, Renderer3, isProceduralRenderer} from '../interfaces/renderer';
|
||||
import {SanitizerFn} from '../interfaces/sanitization';
|
||||
import {BINDING_INDEX, FLAGS, HEADER_OFFSET, LView, LViewFlags, RENDERER, TData, TVIEW} from '../interfaces/view';
|
||||
import {BINDING_INDEX} from '../interfaces/view';
|
||||
import {getLView, getSelectedIndex} from '../state';
|
||||
import {ANIMATION_PROP_PREFIX, isAnimationProp} from '../styling/util';
|
||||
import {NO_CHANGE} from '../tokens';
|
||||
import {INTERPOLATION_DELIMITER} from '../util/misc_utils';
|
||||
import {getComponentViewByIndex, getNativeByIndex, getTNode, isComponent} from '../util/view_utils';
|
||||
import {TsickleIssue1009, initializeTNodeInputs, loadComponentRenderer, setInputsForProperty, storeBindingMetadata} from './shared';
|
||||
|
||||
import {TsickleIssue1009, elementPropertyInternal, loadComponentRenderer, storeBindingMetadata} from './shared';
|
||||
|
||||
|
||||
/**
|
||||
* Update a property on a selected element.
|
||||
|
@ -116,167 +109,3 @@ export function ΔcomponentHostSyntheticProperty<T>(
|
|||
nativeOnly?: boolean) {
|
||||
elementPropertyInternal(index, propName, value, sanitizer, nativeOnly, loadComponentRenderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping between attributes names that don't correspond to their element property names.
|
||||
*/
|
||||
const ATTR_TO_PROP: {[name: string]: string} = {
|
||||
'class': 'className',
|
||||
'for': 'htmlFor',
|
||||
'formaction': 'formAction',
|
||||
'innerHtml': 'innerHTML',
|
||||
'readonly': 'readOnly',
|
||||
'tabindex': 'tabIndex',
|
||||
};
|
||||
|
||||
function elementPropertyInternal<T>(
|
||||
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null,
|
||||
nativeOnly?: boolean,
|
||||
loadRendererFn?: ((tNode: TNode, lView: LView) => Renderer3) | null): void {
|
||||
if (value === NO_CHANGE) return;
|
||||
const lView = getLView();
|
||||
const element = getNativeByIndex(index, lView) as RElement | RComment;
|
||||
const tNode = getTNode(index, lView);
|
||||
let inputData: PropertyAliases|null|undefined;
|
||||
let dataValue: PropertyAliasValue|undefined;
|
||||
if (!nativeOnly && (inputData = initializeTNodeInputs(tNode)) &&
|
||||
(dataValue = inputData[propName])) {
|
||||
setInputsForProperty(lView, dataValue, value);
|
||||
if (isComponent(tNode)) markDirtyIfOnPush(lView, index + HEADER_OFFSET);
|
||||
if (ngDevMode) {
|
||||
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.Container) {
|
||||
setNgReflectProperties(lView, element, tNode.type, dataValue, value);
|
||||
}
|
||||
}
|
||||
} else if (tNode.type === TNodeType.Element) {
|
||||
propName = ATTR_TO_PROP[propName] || propName;
|
||||
|
||||
if (ngDevMode) {
|
||||
validateAgainstEventProperties(propName);
|
||||
validateAgainstUnknownProperties(lView, element, propName, tNode);
|
||||
ngDevMode.rendererSetProperty++;
|
||||
}
|
||||
|
||||
savePropertyDebugData(tNode, lView, propName, lView[TVIEW].data, nativeOnly);
|
||||
|
||||
const renderer = loadRendererFn ? loadRendererFn(tNode, lView) : lView[RENDERER];
|
||||
// It is assumed that the sanitizer is only added when the compiler determines that the property
|
||||
// is risky, so sanitization can be done without further checks.
|
||||
value = sanitizer != null ? (sanitizer(value, tNode.tagName || '', propName) as any) : value;
|
||||
if (isProceduralRenderer(renderer)) {
|
||||
renderer.setProperty(element as RElement, propName, value);
|
||||
} else if (!isAnimationProp(propName)) {
|
||||
(element as RElement).setProperty ? (element as any).setProperty(propName, value) :
|
||||
(element as any)[propName] = value;
|
||||
}
|
||||
} else if (tNode.type === TNodeType.Container) {
|
||||
// If the node is a container and the property didn't
|
||||
// match any of the inputs or schemas we should throw.
|
||||
if (ngDevMode && !matchingSchemas(lView, tNode.tagName)) {
|
||||
throw createUnknownPropertyError(propName, tNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** If node is an OnPush component, marks its LView dirty. */
|
||||
function markDirtyIfOnPush(lView: LView, viewIndex: number): void {
|
||||
ngDevMode && assertLView(lView);
|
||||
const childComponentLView = getComponentViewByIndex(viewIndex, lView);
|
||||
if (!(childComponentLView[FLAGS] & LViewFlags.CheckAlways)) {
|
||||
childComponentLView[FLAGS] |= LViewFlags.Dirty;
|
||||
}
|
||||
}
|
||||
|
||||
function setNgReflectProperties(
|
||||
lView: LView, element: RElement | RComment, type: TNodeType, inputs: PropertyAliasValue,
|
||||
value: any) {
|
||||
for (let i = 0; i < inputs.length; i += 3) {
|
||||
const renderer = lView[RENDERER];
|
||||
const attrName = normalizeDebugBindingName(inputs[i + 2] as string);
|
||||
const debugValue = normalizeDebugBindingValue(value);
|
||||
if (type === TNodeType.Element) {
|
||||
isProceduralRenderer(renderer) ?
|
||||
renderer.setAttribute((element as RElement), attrName, debugValue) :
|
||||
(element as RElement).setAttribute(attrName, debugValue);
|
||||
} else if (value !== undefined) {
|
||||
const value = `bindings=${JSON.stringify({[attrName]: debugValue}, null, 2)}`;
|
||||
if (isProceduralRenderer(renderer)) {
|
||||
renderer.setValue((element as RComment), value);
|
||||
} else {
|
||||
(element as RComment).textContent = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateAgainstUnknownProperties(
|
||||
hostView: LView, element: RElement | RComment, propName: string, tNode: TNode) {
|
||||
// If the tag matches any of the schemas we shouldn't throw.
|
||||
if (matchingSchemas(hostView, tNode.tagName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If prop is not a known property of the HTML element...
|
||||
if (!(propName in element) &&
|
||||
// and we are in a browser context... (web worker nodes should be skipped)
|
||||
typeof Node === 'function' && element instanceof Node &&
|
||||
// and isn't a synthetic animation property...
|
||||
propName[0] !== ANIMATION_PROP_PREFIX) {
|
||||
// ... it is probably a user error and we should throw.
|
||||
throw createUnknownPropertyError(propName, tNode);
|
||||
}
|
||||
}
|
||||
|
||||
function matchingSchemas(hostView: LView, tagName: string | null): boolean {
|
||||
const schemas = hostView[TVIEW].schemas;
|
||||
|
||||
if (schemas !== null) {
|
||||
for (let i = 0; i < schemas.length; i++) {
|
||||
const schema = schemas[i];
|
||||
if (schema === NO_ERRORS_SCHEMA ||
|
||||
schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores debugging data for this property binding on first template pass.
|
||||
* This enables features like DebugElement.properties.
|
||||
*/
|
||||
function savePropertyDebugData(
|
||||
tNode: TNode, lView: LView, propName: string, tData: TData,
|
||||
nativeOnly: boolean | undefined): void {
|
||||
const lastBindingIndex = lView[BINDING_INDEX] - 1;
|
||||
|
||||
// Bind/interpolation functions save binding metadata in the last binding index,
|
||||
// but leave the property name blank. If the interpolation delimiter is at the 0
|
||||
// index, we know that this is our first pass and the property name still needs to
|
||||
// be set.
|
||||
const bindingMetadata = tData[lastBindingIndex] as string;
|
||||
if (bindingMetadata[0] == INTERPOLATION_DELIMITER) {
|
||||
tData[lastBindingIndex] = propName + bindingMetadata;
|
||||
|
||||
// We don't want to store indices for host bindings because they are stored in a
|
||||
// different part of LView (the expando section).
|
||||
if (!nativeOnly) {
|
||||
if (tNode.propertyMetadataStartIndex == -1) {
|
||||
tNode.propertyMetadataStartIndex = lastBindingIndex;
|
||||
}
|
||||
tNode.propertyMetadataEndIndex = lastBindingIndex + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an error that should be thrown when encountering an unknown property on an element.
|
||||
* @param propName Name of the invalid property.
|
||||
* @param tNode Node on which we encountered the error.
|
||||
*/
|
||||
function createUnknownPropertyError(propName: string, tNode: TNode): Error {
|
||||
return new Error(
|
||||
`Template error: Can't bind to '${propName}' since it isn't a known property of '${tNode.tagName}'.`);
|
||||
}
|
||||
|
|
|
@ -8,10 +8,12 @@
|
|||
import {assertEqual, assertLessThan} from '../../util/assert';
|
||||
import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4} from '../bindings';
|
||||
import {BINDING_INDEX, TVIEW} from '../interfaces/view';
|
||||
import {getLView} from '../state';
|
||||
import {getLView, getSelectedIndex} from '../state';
|
||||
import {NO_CHANGE} from '../tokens';
|
||||
import {renderStringify} from '../util/misc_utils';
|
||||
import {storeBindingMetadata} from './shared';
|
||||
|
||||
import {TsickleIssue1009, elementPropertyInternal, storeBindingMetadata} from './shared';
|
||||
|
||||
|
||||
/**
|
||||
* Create interpolation bindings with a variable number of expressions.
|
||||
|
@ -283,3 +285,423 @@ export function Δinterpolation8(
|
|||
renderStringify(v6) + i6 + renderStringify(v7) + suffix :
|
||||
NO_CHANGE;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
/// NEW INSTRUCTIONS
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* Shared reference to a string, used in `ΔpropertyInterpolate`.
|
||||
*/
|
||||
const EMPTY_STRING = '';
|
||||
|
||||
/**
|
||||
*
|
||||
* Update an interpolated property on an element with a lone bound value
|
||||
*
|
||||
* Used when the value passed to a property has 1 interpolated value in it, an no additional text
|
||||
* surrounds that interpolated value:
|
||||
*
|
||||
* ```html
|
||||
* <div title="{{v0}}"></div>
|
||||
* ```
|
||||
*
|
||||
* Its compiled representation is::
|
||||
*
|
||||
* ```ts
|
||||
* ΔpropertyInterpolate('title', v0);
|
||||
* ```
|
||||
*
|
||||
* If the property name also exists as an input property on one of the element's directives,
|
||||
* the component property will be set instead of the element property. This check must
|
||||
* be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
|
||||
*
|
||||
* @param propName The name of the property to update
|
||||
* @param prefix Static value used for concatenation only.
|
||||
* @param v0 Value checked for change.
|
||||
* @param suffix Static value used for concatenation only.
|
||||
* @returns itself, so that it may be chained.
|
||||
*/
|
||||
export function ΔpropertyInterpolate(propName: string, v0: any): TsickleIssue1009 {
|
||||
ΔpropertyInterpolate1(propName, EMPTY_STRING, v0, EMPTY_STRING);
|
||||
return ΔpropertyInterpolate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Update an interpolated property on an element with single bound value surrounded by text.
|
||||
*
|
||||
* Used when the value passed to a property has 1 interpolated value in it:
|
||||
*
|
||||
* ```html
|
||||
* <div title="prefix{{v0}}suffix"></div>
|
||||
* ```
|
||||
*
|
||||
* Its compiled representation is::
|
||||
*
|
||||
* ```ts
|
||||
* ΔpropertyInterpolate1('title', 'prefix', v0, 'suffix');
|
||||
* ```
|
||||
*
|
||||
* If the property name also exists as an input property on one of the element's directives,
|
||||
* the component property will be set instead of the element property. This check must
|
||||
* be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
|
||||
*
|
||||
* @param propName The name of the property to update
|
||||
* @param prefix Static value used for concatenation only.
|
||||
* @param v0 Value checked for change.
|
||||
* @param suffix Static value used for concatenation only.
|
||||
* @returns itself, so that it may be chained.
|
||||
*/
|
||||
export function ΔpropertyInterpolate1(
|
||||
propName: string, prefix: string, v0: any, suffix: string): TsickleIssue1009 {
|
||||
const index = getSelectedIndex();
|
||||
elementPropertyInternal(index, propName, Δinterpolation1(prefix, v0, suffix));
|
||||
return ΔpropertyInterpolate1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Update an interpolated property on an element with 2 bound values surrounded by text.
|
||||
*
|
||||
* Used when the value passed to a property has 2 interpolated values in it:
|
||||
*
|
||||
* ```html
|
||||
* <div title="prefix{{v0}}-{{v1}}suffix"></div>
|
||||
* ```
|
||||
*
|
||||
* Its compiled representation is::
|
||||
*
|
||||
* ```ts
|
||||
* ΔpropertyInterpolate2('title', 'prefix', v0, '-', v1, 'suffix');
|
||||
* ```
|
||||
*
|
||||
* If the property name also exists as an input property on one of the element's directives,
|
||||
* the component property will be set instead of the element property. This check must
|
||||
* be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
|
||||
*
|
||||
* @param propName The name of the property to update
|
||||
* @param prefix Static value used for concatenation only.
|
||||
* @param v0 Value checked for change.
|
||||
* @param i0 Static value used for concatenation only.
|
||||
* @param v1 Value checked for change.
|
||||
* @param suffix Static value used for concatenation only.
|
||||
* @returns itself, so that it may be chained.
|
||||
*/
|
||||
export function ΔpropertyInterpolate2(
|
||||
propName: string, prefix: string, v0: any, i0: string, v1: any,
|
||||
suffix: string): TsickleIssue1009 {
|
||||
const index = getSelectedIndex();
|
||||
elementPropertyInternal(index, propName, Δinterpolation2(prefix, v0, i0, v1, suffix));
|
||||
return ΔpropertyInterpolate2;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Update an interpolated property on an element with 3 bound values surrounded by text.
|
||||
*
|
||||
* Used when the value passed to a property has 3 interpolated values in it:
|
||||
*
|
||||
* ```html
|
||||
* <div title="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div>
|
||||
* ```
|
||||
*
|
||||
* Its compiled representation is::
|
||||
*
|
||||
* ```ts
|
||||
* ΔpropertyInterpolate3(
|
||||
* 'title', 'prefix', v0, '-', v1, '-', v2, 'suffix');
|
||||
* ```
|
||||
*
|
||||
* If the property name also exists as an input property on one of the element's directives,
|
||||
* the component property will be set instead of the element property. This check must
|
||||
* be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
|
||||
*
|
||||
* @param propName The name of the property to update
|
||||
* @param prefix Static value used for concatenation only.
|
||||
* @param v0 Value checked for change.
|
||||
* @param i0 Static value used for concatenation only.
|
||||
* @param v1 Value checked for change.
|
||||
* @param i1 Static value used for concatenation only.
|
||||
* @param v2 Value checked for change.
|
||||
* @param suffix Static value used for concatenation only.
|
||||
* @returns itself, so that it may be chained.
|
||||
*/
|
||||
export function ΔpropertyInterpolate3(
|
||||
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any,
|
||||
suffix: string): TsickleIssue1009 {
|
||||
const index = getSelectedIndex();
|
||||
elementPropertyInternal(index, propName, Δinterpolation3(prefix, v0, i0, v1, i1, v2, suffix));
|
||||
return ΔpropertyInterpolate3;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Update an interpolated property on an element with 4 bound values surrounded by text.
|
||||
*
|
||||
* Used when the value passed to a property has 4 interpolated values in it:
|
||||
*
|
||||
* ```html
|
||||
* <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div>
|
||||
* ```
|
||||
*
|
||||
* Its compiled representation is::
|
||||
*
|
||||
* ```ts
|
||||
* ΔpropertyInterpolate4(
|
||||
* 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
|
||||
* ```
|
||||
*
|
||||
* If the property name also exists as an input property on one of the element's directives,
|
||||
* the component property will be set instead of the element property. This check must
|
||||
* be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
|
||||
*
|
||||
* @param propName The name of the property to update
|
||||
* @param prefix Static value used for concatenation only.
|
||||
* @param v0 Value checked for change.
|
||||
* @param i0 Static value used for concatenation only.
|
||||
* @param v1 Value checked for change.
|
||||
* @param i1 Static value used for concatenation only.
|
||||
* @param v2 Value checked for change.
|
||||
* @param i2 Static value used for concatenation only.
|
||||
* @param v3 Value checked for change.
|
||||
* @param suffix Static value used for concatenation only.
|
||||
* @returns itself, so that it may be chained.
|
||||
*/
|
||||
export function ΔpropertyInterpolate4(
|
||||
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
|
||||
v3: any, suffix: string): TsickleIssue1009 {
|
||||
const index = getSelectedIndex();
|
||||
elementPropertyInternal(
|
||||
index, propName, Δinterpolation4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix));
|
||||
return ΔpropertyInterpolate4;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Update an interpolated property on an element with 5 bound values surrounded by text.
|
||||
*
|
||||
* Used when the value passed to a property has 5 interpolated values in it:
|
||||
*
|
||||
* ```html
|
||||
* <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div>
|
||||
* ```
|
||||
*
|
||||
* Its compiled representation is::
|
||||
*
|
||||
* ```ts
|
||||
* ΔpropertyInterpolate5(
|
||||
* 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
|
||||
* ```
|
||||
*
|
||||
* If the property name also exists as an input property on one of the element's directives,
|
||||
* the component property will be set instead of the element property. This check must
|
||||
* be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
|
||||
*
|
||||
* @param propName The name of the property to update
|
||||
* @param prefix Static value used for concatenation only.
|
||||
* @param v0 Value checked for change.
|
||||
* @param i0 Static value used for concatenation only.
|
||||
* @param v1 Value checked for change.
|
||||
* @param i1 Static value used for concatenation only.
|
||||
* @param v2 Value checked for change.
|
||||
* @param i2 Static value used for concatenation only.
|
||||
* @param v3 Value checked for change.
|
||||
* @param i3 Static value used for concatenation only.
|
||||
* @param v4 Value checked for change.
|
||||
* @param suffix Static value used for concatenation only.
|
||||
* @returns itself, so that it may be chained.
|
||||
*/
|
||||
export function ΔpropertyInterpolate5(
|
||||
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
|
||||
v3: any, i3: string, v4: any, suffix: string): TsickleIssue1009 {
|
||||
const index = getSelectedIndex();
|
||||
elementPropertyInternal(
|
||||
index, propName, Δinterpolation5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix));
|
||||
return ΔpropertyInterpolate5;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Update an interpolated property on an element with 6 bound values surrounded by text.
|
||||
*
|
||||
* Used when the value passed to a property has 6 interpolated values in it:
|
||||
*
|
||||
* ```html
|
||||
* <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div>
|
||||
* ```
|
||||
*
|
||||
* Its compiled representation is::
|
||||
*
|
||||
* ```ts
|
||||
* ΔpropertyInterpolate6(
|
||||
* 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
|
||||
* ```
|
||||
*
|
||||
* If the property name also exists as an input property on one of the element's directives,
|
||||
* the component property will be set instead of the element property. This check must
|
||||
* be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
|
||||
*
|
||||
* @param propName The name of the property to update
|
||||
* @param prefix Static value used for concatenation only.
|
||||
* @param v0 Value checked for change.
|
||||
* @param i0 Static value used for concatenation only.
|
||||
* @param v1 Value checked for change.
|
||||
* @param i1 Static value used for concatenation only.
|
||||
* @param v2 Value checked for change.
|
||||
* @param i2 Static value used for concatenation only.
|
||||
* @param v3 Value checked for change.
|
||||
* @param i3 Static value used for concatenation only.
|
||||
* @param v4 Value checked for change.
|
||||
* @param i4 Static value used for concatenation only.
|
||||
* @param v5 Value checked for change.
|
||||
* @param suffix Static value used for concatenation only.
|
||||
* @returns itself, so that it may be chained.
|
||||
*/
|
||||
export function ΔpropertyInterpolate6(
|
||||
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
|
||||
v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string): TsickleIssue1009 {
|
||||
const index = getSelectedIndex();
|
||||
elementPropertyInternal(
|
||||
index, propName, Δinterpolation6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix));
|
||||
return ΔpropertyInterpolate6;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Update an interpolated property on an element with 7 bound values surrounded by text.
|
||||
*
|
||||
* Used when the value passed to a property has 7 interpolated values in it:
|
||||
*
|
||||
* ```html
|
||||
* <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div>
|
||||
* ```
|
||||
*
|
||||
* Its compiled representation is::
|
||||
*
|
||||
* ```ts
|
||||
* ΔpropertyInterpolate7(
|
||||
* 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
|
||||
* ```
|
||||
*
|
||||
* If the property name also exists as an input property on one of the element's directives,
|
||||
* the component property will be set instead of the element property. This check must
|
||||
* be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
|
||||
*
|
||||
* @param propName The name of the property to update
|
||||
* @param prefix Static value used for concatenation only.
|
||||
* @param v0 Value checked for change.
|
||||
* @param i0 Static value used for concatenation only.
|
||||
* @param v1 Value checked for change.
|
||||
* @param i1 Static value used for concatenation only.
|
||||
* @param v2 Value checked for change.
|
||||
* @param i2 Static value used for concatenation only.
|
||||
* @param v3 Value checked for change.
|
||||
* @param i3 Static value used for concatenation only.
|
||||
* @param v4 Value checked for change.
|
||||
* @param i4 Static value used for concatenation only.
|
||||
* @param v5 Value checked for change.
|
||||
* @param i5 Static value used for concatenation only.
|
||||
* @param v6 Value checked for change.
|
||||
* @param suffix Static value used for concatenation only.
|
||||
* @returns itself, so that it may be chained.
|
||||
*/
|
||||
export function ΔpropertyInterpolate7(
|
||||
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
|
||||
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any,
|
||||
suffix: string): TsickleIssue1009 {
|
||||
const index = getSelectedIndex();
|
||||
elementPropertyInternal(
|
||||
index, propName,
|
||||
Δinterpolation7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix));
|
||||
return ΔpropertyInterpolate7;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Update an interpolated property on an element with 8 bound values surrounded by text.
|
||||
*
|
||||
* Used when the value passed to a property has 8 interpolated values in it:
|
||||
*
|
||||
* ```html
|
||||
* <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div>
|
||||
* ```
|
||||
*
|
||||
* Its compiled representation is::
|
||||
*
|
||||
* ```ts
|
||||
* ΔpropertyInterpolate8(
|
||||
* 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix');
|
||||
* ```
|
||||
*
|
||||
* If the property name also exists as an input property on one of the element's directives,
|
||||
* the component property will be set instead of the element property. This check must
|
||||
* be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
|
||||
*
|
||||
* @param propName The name of the property to update
|
||||
* @param prefix Static value used for concatenation only.
|
||||
* @param v0 Value checked for change.
|
||||
* @param i0 Static value used for concatenation only.
|
||||
* @param v1 Value checked for change.
|
||||
* @param i1 Static value used for concatenation only.
|
||||
* @param v2 Value checked for change.
|
||||
* @param i2 Static value used for concatenation only.
|
||||
* @param v3 Value checked for change.
|
||||
* @param i3 Static value used for concatenation only.
|
||||
* @param v4 Value checked for change.
|
||||
* @param i4 Static value used for concatenation only.
|
||||
* @param v5 Value checked for change.
|
||||
* @param i5 Static value used for concatenation only.
|
||||
* @param v6 Value checked for change.
|
||||
* @param i6 Static value used for concatenation only.
|
||||
* @param v7 Value checked for change.
|
||||
* @param suffix Static value used for concatenation only.
|
||||
* @returns itself, so that it may be chained.
|
||||
*/
|
||||
export function ΔpropertyInterpolate8(
|
||||
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
|
||||
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any,
|
||||
suffix: string): TsickleIssue1009 {
|
||||
const index = getSelectedIndex();
|
||||
elementPropertyInternal(
|
||||
index, propName,
|
||||
Δinterpolation8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix));
|
||||
return ΔpropertyInterpolate8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an interpolated property on an element with 8 or more bound values surrounded by text.
|
||||
*
|
||||
* Used when the number of interpolated values exceeds 7.
|
||||
*
|
||||
* ```html
|
||||
* <div
|
||||
* title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix"></div>
|
||||
* ```
|
||||
*
|
||||
* Its compiled representation is::
|
||||
*
|
||||
* ```ts
|
||||
* ΔpropertyInterpolateV(
|
||||
* 'title', ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
|
||||
* 'suffix']);
|
||||
* ```
|
||||
*
|
||||
* If the property name also exists as an input property on one of the element's directives,
|
||||
* the component property will be set instead of the element property. This check must
|
||||
* be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
|
||||
*
|
||||
* @param propName The name of the property to update.
|
||||
* @param values The a collection of values and the strings inbetween those values, beginning with a
|
||||
* string prefix and ending with a string suffix.
|
||||
* (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
|
||||
* @returns itself, so that it may be chained.
|
||||
*/
|
||||
export function ΔpropertyInterpolateV(propName: string, values: any[]): TsickleIssue1009 {
|
||||
const index = getSelectedIndex();
|
||||
|
||||
elementPropertyInternal(index, propName, ΔinterpolationV(values));
|
||||
return ΔpropertyInterpolateV;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import {HEADER_OFFSET, TVIEW} from '../interfaces/view';
|
|||
import {getCheckNoChangesMode, getLView, setSelectedIndex} from '../state';
|
||||
|
||||
/**
|
||||
* Selects an index of an item to act on and flushes lifecycle hooks up to this point
|
||||
* Selects an element for later binding instructions.
|
||||
*
|
||||
* Used in conjunction with instructions like {@link property} to act on elements with specified
|
||||
* indices, for example those created with {@link element} or {@link elementStart}.
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
import {Injector} from '../../di';
|
||||
import {ErrorHandler} from '../../error_handler';
|
||||
import {Type} from '../../interface/type';
|
||||
import {SchemaMetadata} from '../../metadata/schema';
|
||||
import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata} from '../../metadata/schema';
|
||||
import {validateAgainstEventProperties} from '../../sanitization/sanitization';
|
||||
import {Sanitizer} from '../../sanitization/security';
|
||||
import {assertDataInRange, assertDefined, assertDomNode, assertEqual, assertLessThan, assertNotEqual} from '../../util/assert';
|
||||
import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect';
|
||||
import {assertLView, assertPreviousIsParent} from '../assert';
|
||||
import {attachPatchData, getComponentViewByInstance} from '../context_discovery';
|
||||
import {attachLContainerDebug, attachLViewDebug} from '../debug';
|
||||
|
@ -23,17 +25,19 @@ import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from '../interfaces/inj
|
|||
import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from '../interfaces/node';
|
||||
import {LQueries} from '../interfaces/query';
|
||||
import {RComment, RElement, RText, Renderer3, RendererFactory3, isProceduralRenderer} from '../interfaces/renderer';
|
||||
import {SanitizerFn} from '../interfaces/sanitization';
|
||||
import {StylingContext} from '../interfaces/styling';
|
||||
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TVIEW, TView, T_HOST} from '../interfaces/view';
|
||||
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, T_HOST} from '../interfaces/view';
|
||||
import {assertNodeOfPossibleTypes, assertNodeType} from '../node_assert';
|
||||
import {isNodeMatchingSelectorList} from '../node_selector_matcher';
|
||||
import {enterView, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, incrementActiveDirectiveId, isCreationMode, leaveView, resetComponentState, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setIsParent, setPreviousOrParentTNode, setSelectedIndex, ΔnamespaceHTML} from '../state';
|
||||
import {initializeStaticContext as initializeStaticStylingContext} from '../styling/class_and_style_bindings';
|
||||
import {ANIMATION_PROP_PREFIX, isAnimationProp} from '../styling/util';
|
||||
import {NO_CHANGE} from '../tokens';
|
||||
import {attrsStylingIndexOf} from '../util/attrs_utils';
|
||||
import {INTERPOLATION_DELIMITER, renderStringify} from '../util/misc_utils';
|
||||
import {getLViewParent, getRootContext} from '../util/view_traversal_utils';
|
||||
import {getComponentViewByIndex, getNativeByTNode, isComponentDef, isContentQueryHost, isRootView, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector} from '../util/view_utils';
|
||||
import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isComponent, isComponentDef, isContentQueryHost, isRootView, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector} from '../util/view_utils';
|
||||
|
||||
|
||||
|
||||
|
@ -430,7 +434,7 @@ export function renderEmbeddedTemplate<T>(viewToRender: LView, tView: TView, con
|
|||
ΔnamespaceHTML();
|
||||
|
||||
// Reset the selected index so we can assert that `select` was called later
|
||||
ngDevMode && setSelectedIndex(-1);
|
||||
setSelectedIndex(-1);
|
||||
|
||||
tView.template !(getRenderFlags(viewToRender), context);
|
||||
// This must be set to false immediately after the first creation run because in an
|
||||
|
@ -465,7 +469,7 @@ function renderComponentOrTemplate<T>(
|
|||
ΔnamespaceHTML();
|
||||
|
||||
// Reset the selected index so we can assert that `select` was called later
|
||||
ngDevMode && setSelectedIndex(-1);
|
||||
setSelectedIndex(-1);
|
||||
|
||||
templateFn(RenderFlags.Create, context);
|
||||
}
|
||||
|
@ -810,16 +814,169 @@ export function generatePropertyAliases(tNode: TNode, direction: BindingDirectio
|
|||
return propStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping between attributes names that don't correspond to their element property names.
|
||||
*/
|
||||
const ATTR_TO_PROP: {[name: string]: string} = {
|
||||
'class': 'className',
|
||||
'for': 'htmlFor',
|
||||
'formaction': 'formAction',
|
||||
'innerHtml': 'innerHTML',
|
||||
'readonly': 'readOnly',
|
||||
'tabindex': 'tabIndex',
|
||||
};
|
||||
|
||||
//////////////////////////
|
||||
//// Text
|
||||
//////////////////////////
|
||||
export function elementPropertyInternal<T>(
|
||||
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null,
|
||||
nativeOnly?: boolean,
|
||||
loadRendererFn?: ((tNode: TNode, lView: LView) => Renderer3) | null): void {
|
||||
if (value === NO_CHANGE) return;
|
||||
const lView = getLView();
|
||||
const element = getNativeByIndex(index, lView) as RElement | RComment;
|
||||
const tNode = getTNode(index, lView);
|
||||
let inputData: PropertyAliases|null|undefined;
|
||||
let dataValue: PropertyAliasValue|undefined;
|
||||
if (!nativeOnly && (inputData = initializeTNodeInputs(tNode)) &&
|
||||
(dataValue = inputData[propName])) {
|
||||
setInputsForProperty(lView, dataValue, value);
|
||||
if (isComponent(tNode)) markDirtyIfOnPush(lView, index + HEADER_OFFSET);
|
||||
if (ngDevMode) {
|
||||
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.Container) {
|
||||
setNgReflectProperties(lView, element, tNode.type, dataValue, value);
|
||||
}
|
||||
}
|
||||
} else if (tNode.type === TNodeType.Element) {
|
||||
propName = ATTR_TO_PROP[propName] || propName;
|
||||
|
||||
if (ngDevMode) {
|
||||
validateAgainstEventProperties(propName);
|
||||
validateAgainstUnknownProperties(lView, element, propName, tNode);
|
||||
ngDevMode.rendererSetProperty++;
|
||||
}
|
||||
|
||||
savePropertyDebugData(tNode, lView, propName, lView[TVIEW].data, nativeOnly);
|
||||
|
||||
//////////////////////////
|
||||
//// Directive
|
||||
//////////////////////////
|
||||
const renderer = loadRendererFn ? loadRendererFn(tNode, lView) : lView[RENDERER];
|
||||
// It is assumed that the sanitizer is only added when the compiler determines that the property
|
||||
// is risky, so sanitization can be done without further checks.
|
||||
value = sanitizer != null ? (sanitizer(value, tNode.tagName || '', propName) as any) : value;
|
||||
if (isProceduralRenderer(renderer)) {
|
||||
renderer.setProperty(element as RElement, propName, value);
|
||||
} else if (!isAnimationProp(propName)) {
|
||||
(element as RElement).setProperty ? (element as any).setProperty(propName, value) :
|
||||
(element as any)[propName] = value;
|
||||
}
|
||||
} else if (tNode.type === TNodeType.Container) {
|
||||
// If the node is a container and the property didn't
|
||||
// match any of the inputs or schemas we should throw.
|
||||
if (ngDevMode && !matchingSchemas(lView, tNode.tagName)) {
|
||||
throw createUnknownPropertyError(propName, tNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** If node is an OnPush component, marks its LView dirty. */
|
||||
function markDirtyIfOnPush(lView: LView, viewIndex: number): void {
|
||||
ngDevMode && assertLView(lView);
|
||||
const childComponentLView = getComponentViewByIndex(viewIndex, lView);
|
||||
if (!(childComponentLView[FLAGS] & LViewFlags.CheckAlways)) {
|
||||
childComponentLView[FLAGS] |= LViewFlags.Dirty;
|
||||
}
|
||||
}
|
||||
|
||||
function setNgReflectProperties(
|
||||
lView: LView, element: RElement | RComment, type: TNodeType, inputs: PropertyAliasValue,
|
||||
value: any) {
|
||||
for (let i = 0; i < inputs.length; i += 3) {
|
||||
const renderer = lView[RENDERER];
|
||||
const attrName = normalizeDebugBindingName(inputs[i + 2] as string);
|
||||
const debugValue = normalizeDebugBindingValue(value);
|
||||
if (type === TNodeType.Element) {
|
||||
isProceduralRenderer(renderer) ?
|
||||
renderer.setAttribute((element as RElement), attrName, debugValue) :
|
||||
(element as RElement).setAttribute(attrName, debugValue);
|
||||
} else if (value !== undefined) {
|
||||
const value = `bindings=${JSON.stringify({[attrName]: debugValue}, null, 2)}`;
|
||||
if (isProceduralRenderer(renderer)) {
|
||||
renderer.setValue((element as RComment), value);
|
||||
} else {
|
||||
(element as RComment).textContent = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateAgainstUnknownProperties(
|
||||
hostView: LView, element: RElement | RComment, propName: string, tNode: TNode) {
|
||||
// If the tag matches any of the schemas we shouldn't throw.
|
||||
if (matchingSchemas(hostView, tNode.tagName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If prop is not a known property of the HTML element...
|
||||
if (!(propName in element) &&
|
||||
// and we are in a browser context... (web worker nodes should be skipped)
|
||||
typeof Node === 'function' && element instanceof Node &&
|
||||
// and isn't a synthetic animation property...
|
||||
propName[0] !== ANIMATION_PROP_PREFIX) {
|
||||
// ... it is probably a user error and we should throw.
|
||||
throw createUnknownPropertyError(propName, tNode);
|
||||
}
|
||||
}
|
||||
|
||||
function matchingSchemas(hostView: LView, tagName: string | null): boolean {
|
||||
const schemas = hostView[TVIEW].schemas;
|
||||
|
||||
if (schemas !== null) {
|
||||
for (let i = 0; i < schemas.length; i++) {
|
||||
const schema = schemas[i];
|
||||
if (schema === NO_ERRORS_SCHEMA ||
|
||||
schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores debugging data for this property binding on first template pass.
|
||||
* This enables features like DebugElement.properties.
|
||||
*/
|
||||
function savePropertyDebugData(
|
||||
tNode: TNode, lView: LView, propName: string, tData: TData,
|
||||
nativeOnly: boolean | undefined): void {
|
||||
const lastBindingIndex = lView[BINDING_INDEX] - 1;
|
||||
|
||||
// Bind/interpolation functions save binding metadata in the last binding index,
|
||||
// but leave the property name blank. If the interpolation delimiter is at the 0
|
||||
// index, we know that this is our first pass and the property name still needs to
|
||||
// be set.
|
||||
const bindingMetadata = tData[lastBindingIndex] as string;
|
||||
if (bindingMetadata[0] == INTERPOLATION_DELIMITER) {
|
||||
tData[lastBindingIndex] = propName + bindingMetadata;
|
||||
|
||||
// We don't want to store indices for host bindings because they are stored in a
|
||||
// different part of LView (the expando section).
|
||||
if (!nativeOnly) {
|
||||
if (tNode.propertyMetadataStartIndex == -1) {
|
||||
tNode.propertyMetadataStartIndex = lastBindingIndex;
|
||||
}
|
||||
tNode.propertyMetadataEndIndex = lastBindingIndex + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an error that should be thrown when encountering an unknown property on an element.
|
||||
* @param propName Name of the invalid property.
|
||||
* @param tNode Node on which we encountered the error.
|
||||
*/
|
||||
function createUnknownPropertyError(propName: string, tNode: TNode): Error {
|
||||
return new Error(
|
||||
`Template error: Can't bind to '${propName}' since it isn't a known property of '${tNode.tagName}'.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a root component.
|
||||
|
@ -1515,7 +1672,7 @@ export function checkView<T>(hostView: LView, component: T) {
|
|||
creationMode && executeViewQueryFn(RenderFlags.Create, hostTView, component);
|
||||
|
||||
// Reset the selected index so we can assert that `select` was called later
|
||||
ngDevMode && setSelectedIndex(-1);
|
||||
setSelectedIndex(-1);
|
||||
|
||||
templateFn(getRenderFlags(hostView), component);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import {NgForOfContext} from '@angular/common';
|
||||
import {ΔpropertyInterpolate, ΔpropertyInterpolate1, ΔpropertyInterpolate2, ΔpropertyInterpolate3, ΔpropertyInterpolate4, ΔpropertyInterpolate5, ΔpropertyInterpolate6, ΔpropertyInterpolate7, ΔpropertyInterpolate8, ΔpropertyInterpolateV} from '@angular/core/src/render3/instructions/all';
|
||||
|
||||
import {ΔdefineComponent} from '../../src/render3/definition';
|
||||
import {RenderFlags, Δbind, Δelement, ΔelementAttribute, ΔelementEnd, ΔelementProperty, ΔelementStart, ΔelementStyleProp, ΔelementStyling, ΔelementStylingApply, ΔelementStylingMap, Δinterpolation1, Δproperty, Δselect, Δtemplate, Δtext, ΔtextBinding} from '../../src/render3/index';
|
||||
|
@ -161,7 +162,7 @@ describe('instructions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('select', () => {
|
||||
describe('Δselect', () => {
|
||||
it('should error in DevMode if index is out of range', () => {
|
||||
// Only one constant added, meaning only index `0` is valid.
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 0);
|
||||
|
@ -173,7 +174,7 @@ describe('instructions', () => {
|
|||
|
||||
describe('property', () => {
|
||||
// TODO(benlesh): Replace with TestBed tests once the instruction is being generated.
|
||||
it('should set properties of the selected element', () => {
|
||||
it('should set properties of the Δselected element', () => {
|
||||
// <div [title]="title"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 1);
|
||||
t.update(() => {
|
||||
|
@ -241,7 +242,7 @@ describe('instructions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should error in dev mode if select was not called prior', () => {
|
||||
it('should error in dev mode if Δselect was not called prior', () => {
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 1);
|
||||
expect(() => { t.update(() => { Δproperty('title', 'test'); }); }).toThrow();
|
||||
expect(() => {
|
||||
|
@ -253,6 +254,724 @@ describe('instructions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* TODO: REMOVE ALL OF THESE TemplateFixture TESTS FOR TestBed TESTS AFTER COMPILER IS UPDATED
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
*/
|
||||
describe('ΔpropertyInterpolate instructions', () => {
|
||||
describe('ΔpropertyInterpolate', () => {
|
||||
it('should interpolate one value', () => {
|
||||
// <div title="{{123}}"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 1);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate('title', 123);
|
||||
});
|
||||
expect(t.html).toEqual('<div title="123"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate('title', 'abc');
|
||||
});
|
||||
expect(t.html).toEqual('<div title="abc"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it('should chain', () => {
|
||||
// <div title="{{123}}" accesskey="{{'A'}}"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 2);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate('title', 123)('accessKey', 'A');
|
||||
});
|
||||
expect(t.html).toEqual('<div accesskey="A" title="123"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate('title', 'abc')('accessKey', 'B');
|
||||
});
|
||||
expect(t.html).toEqual('<div accesskey="B" title="abc"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 4,
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if called without Δselect called first', () => {
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 1);
|
||||
expect(() => { t.update(() => { ΔpropertyInterpolate('title', 123); }); }).toThrow();
|
||||
expect(() => {
|
||||
Δselect(0);
|
||||
t.update(() => { ΔpropertyInterpolate('title', 123); });
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ΔpropertyInterpolate1', () => {
|
||||
it('should interpolate one value', () => {
|
||||
// <div title="start{{123}}end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 1);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate1('title', 'start', 123, 'end');
|
||||
});
|
||||
expect(t.html).toEqual('<div title="start123end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate1('title', 'start', 'abc', 'end');
|
||||
});
|
||||
expect(t.html).toEqual('<div title="startabcend"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it('should chain', () => {
|
||||
// <div title="start{{123}}end" data-teststartstart{{'A'}}end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 2);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate1('title', 'start', 123, 'end')('accessKey', 'start', 'A', 'end');
|
||||
});
|
||||
expect(t.html).toEqual('<div accesskey="startAend" title="start123end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate1('title', 'start', 'abc', 'end')('accessKey', 'start', 'B', 'end');
|
||||
});
|
||||
expect(t.html).toEqual('<div accesskey="startBend" title="startabcend"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 4,
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if called without Δselect called first', () => {
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 1);
|
||||
expect(() => {
|
||||
t.update(() => { ΔpropertyInterpolate1('title', 'start', 'whatever', 'end'); });
|
||||
}).toThrow();
|
||||
expect(() => {
|
||||
Δselect(0);
|
||||
t.update(() => { ΔpropertyInterpolate1('title', 'start', 'whatever', 'end'); });
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ΔpropertyInterpolate2', () => {
|
||||
it('should interpolate two values', () => {
|
||||
// <div title="start: {{v0}}, 1: {{v1}}, end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 2);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate2('title', 'start: ', 0, ', 1: ', 1, ', end');
|
||||
});
|
||||
expect(t.html).toEqual('<div title="start: 0, 1: 1, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate2('title', 'start: ', 'A', ', 1: ', 'B', ', end');
|
||||
});
|
||||
expect(t.html).toEqual('<div title="start: A, 1: B, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it('should chain', () => {
|
||||
// <div title="start: {{v0}} 1: {{v1}}, end" accesskey="start: {{v0}}, 1: {{v1}},
|
||||
// end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 4);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate2('title', 'start: ', 0, ', 1: ', 1, ', end')(
|
||||
'accessKey', 'start: ', 0, ', 1: ', 1, ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: 0, 1: 1, end" title="start: 0, 1: 1, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate2('title', 'start: ', 'A', ', 1: ', 'B', ', end')(
|
||||
'accessKey', 'start: ', 'A', ', 1: ', 'B', ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: A, 1: B, end" title="start: A, 1: B, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 4,
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if called without Δselect called first', () => {
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 2);
|
||||
expect(() => {
|
||||
t.update(() => { ΔpropertyInterpolate2('title', '', '', '', '', ''); });
|
||||
}).toThrow();
|
||||
expect(() => {
|
||||
Δselect(0);
|
||||
t.update(() => { ΔpropertyInterpolate2('title', '', '', '', '', ''); });
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ΔpropertyInterpolate3', () => {
|
||||
it('should interpolate three values', () => {
|
||||
// <div title="start: {{v0}}, 1: {{v1}}, 2: {{v2}}, end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 3);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate3('title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', end');
|
||||
});
|
||||
expect(t.html).toEqual('<div title="start: 0, 1: 1, 2: 2, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate3('title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', end');
|
||||
});
|
||||
expect(t.html).toEqual('<div title="start: A, 1: B, 2: C, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it('should chain', () => {
|
||||
// <div title="start: {{v0}} 1: {{v1}} 2: {{v2}}, end" accesskey="start: {{v0}}, 1: {{v1}},
|
||||
// 2: {{v2}}, end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 6);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate3('title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', end')(
|
||||
'accessKey', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: 0, 1: 1, 2: 2, end" title="start: 0, 1: 1, 2: 2, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate3('title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', end')(
|
||||
'accessKey', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: A, 1: B, 2: C, end" title="start: A, 1: B, 2: C, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 4,
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if called without Δselect called first', () => {
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 3);
|
||||
expect(() => {
|
||||
t.update(() => { ΔpropertyInterpolate3('title', '', '', '', '', '', '', ''); });
|
||||
}).toThrow();
|
||||
expect(() => {
|
||||
Δselect(0);
|
||||
t.update(() => { ΔpropertyInterpolate3('title', '', '', '', '', '', '', ''); });
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('ΔpropertyInterpolate4', () => {
|
||||
it('should interpolate four values', () => {
|
||||
// <div title="start: {{v0}}, 1: {{v1}}, 2: {{v2}}, 3: {{v3}}, end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 4);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate4('title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', end');
|
||||
});
|
||||
expect(t.html).toEqual('<div title="start: 0, 1: 1, 2: 2, 3: 3, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate4(
|
||||
'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', end');
|
||||
});
|
||||
expect(t.html).toEqual('<div title="start: A, 1: B, 2: C, 3: D, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it('should chain', () => {
|
||||
// <div title="start: {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}}, end" accesskey="start: {{v0}} 1:
|
||||
// {{v1}} 2: {{v2}} 3: {{v3}}, end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 8);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate4('title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', end')(
|
||||
'accessKey', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: 0, 1: 1, 2: 2, 3: 3, end" title="start: 0, 1: 1, 2: 2, 3: 3, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate4(
|
||||
'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', end')(
|
||||
'accessKey', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: A, 1: B, 2: C, 3: D, end" title="start: A, 1: B, 2: C, 3: D, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 4,
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if called without Δselect called first', () => {
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 4);
|
||||
expect(() => {
|
||||
t.update(() => { ΔpropertyInterpolate4('title', '', '', '', '', '', '', '', '', ''); });
|
||||
}).toThrow();
|
||||
expect(() => {
|
||||
Δselect(0);
|
||||
t.update(() => { ΔpropertyInterpolate4('title', '', '', '', '', '', '', '', '', ''); });
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ΔpropertyInterpolate5', () => {
|
||||
it('should interpolate five values', () => {
|
||||
// <div title="start: {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}} 4: {{v4}}, end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 5);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate5(
|
||||
'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', end');
|
||||
});
|
||||
expect(t.html).toEqual('<div title="start: 0, 1: 1, 2: 2, 3: 3, 4: 4, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate5(
|
||||
'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E',
|
||||
', end');
|
||||
});
|
||||
expect(t.html).toEqual('<div title="start: A, 1: B, 2: C, 3: D, 4: E, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it('should chain', () => {
|
||||
// <div title="start: {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}} 4: {{v4}}, end" accesskey="start:
|
||||
// {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}} 4: {{v4}}, end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 10);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate5(
|
||||
'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', end')(
|
||||
'accessKey', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: 0, 1: 1, 2: 2, 3: 3, 4: 4, end" title="start: 0, 1: 1, 2: 2, 3: 3, 4: 4, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate5(
|
||||
'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E',
|
||||
', end')(
|
||||
'accessKey', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E',
|
||||
', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: A, 1: B, 2: C, 3: D, 4: E, end" title="start: A, 1: B, 2: C, 3: D, 4: E, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 4,
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if called without Δselect called first', () => {
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 5);
|
||||
expect(() => {
|
||||
t.update(() => {
|
||||
ΔpropertyInterpolate5('title', '', '', '', '', '', '', '', '', '', '', '');
|
||||
});
|
||||
}).toThrow();
|
||||
expect(() => {
|
||||
Δselect(0);
|
||||
t.update(() => {
|
||||
ΔpropertyInterpolate5('title', '', '', '', '', '', '', '', '', '', '', '');
|
||||
});
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ΔpropertyInterpolate6', () => {
|
||||
it('should interpolate six values', () => {
|
||||
// <div title="start: {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}} 4: {{v4}}, 5: {{v5}}, end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 6);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate6(
|
||||
'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5,
|
||||
', end');
|
||||
});
|
||||
expect(t.html).toEqual('<div title="start: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate6(
|
||||
'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E',
|
||||
', 5: ', 'F', ', end');
|
||||
});
|
||||
expect(t.html).toEqual('<div title="start: A, 1: B, 2: C, 3: D, 4: E, 5: F, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it('should chain', () => {
|
||||
// <div title="start: {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}} 4: {{v4}}, 5: {{v5}}, end"
|
||||
// accesskey="start: {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}} 4: {{v4}}, 5: {{v5}}, end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 12);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate6(
|
||||
'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5,
|
||||
', end')(
|
||||
'accessKey', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5,
|
||||
', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, end" title="start: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate6(
|
||||
'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E',
|
||||
', 5: ', 'F', ', end')(
|
||||
'accessKey', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E',
|
||||
', 5: ', 'F', ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: A, 1: B, 2: C, 3: D, 4: E, 5: F, end" title="start: A, 1: B, 2: C, 3: D, 4: E, 5: F, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 4,
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if called without Δselect called first', () => {
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 6);
|
||||
expect(() => {
|
||||
t.update(() => {
|
||||
ΔpropertyInterpolate6('title', '', '', '', '', '', '', '', '', '', '', '', '', '');
|
||||
});
|
||||
}).toThrow();
|
||||
expect(() => {
|
||||
Δselect(0);
|
||||
t.update(() => {
|
||||
ΔpropertyInterpolate6('title', '', '', '', '', '', '', '', '', '', '', '', '', '');
|
||||
});
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ΔpropertyInterpolate7', () => {
|
||||
it('should interpolate seven values', () => {
|
||||
// <div title="start: {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}} 4: {{v4}} 5: {{v5}}, 6: {{v6}},
|
||||
// end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 7);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate7(
|
||||
'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5,
|
||||
', 6: ', 6, ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div title="start: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate7(
|
||||
'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E',
|
||||
', 5: ', 'F', ', 6: ', 'G', ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div title="start: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it('should chain', () => {
|
||||
// <div title="start: {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}} 4: {{v4}} 5: {{v5}}, 6: {{v6}},
|
||||
// 7: {{v7}} end" accesskey="start: {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}} 4: {{v4}} 5:
|
||||
// {{v5}}, 6: {{v6}}, end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 14);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate7(
|
||||
'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5,
|
||||
', 6: ', 6, ', end')(
|
||||
'accessKey', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5,
|
||||
', 6: ', 6, ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, end" title="start: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate7(
|
||||
'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E',
|
||||
', 5: ', 'F', ', 6: ', 'G', ', end')(
|
||||
'accessKey', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E',
|
||||
', 5: ', 'F', ', 6: ', 'G', ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, end" title="start: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 4,
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if called without Δselect called first', () => {
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 7);
|
||||
expect(() => {
|
||||
t.update(() => {
|
||||
ΔpropertyInterpolate7(
|
||||
'title', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '');
|
||||
});
|
||||
}).toThrow();
|
||||
expect(() => {
|
||||
Δselect(0);
|
||||
t.update(() => {
|
||||
ΔpropertyInterpolate7(
|
||||
'title', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '');
|
||||
});
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ΔpropertyInterpolate8', () => {
|
||||
it('should interpolate eight values', () => {
|
||||
// <div title="start: {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}} 4: {{v4}} 5: {{v5}}, 6: {{v6}},
|
||||
// 7: {{v7}} end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 8);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate8(
|
||||
'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5,
|
||||
', 6: ', 6, ', 7: ', 7, ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div title="start: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate8(
|
||||
'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E',
|
||||
', 5: ', 'F', ', 6: ', 'G', ', 7: ', 'H', ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div title="start: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it('should chain', () => {
|
||||
// <div title="start: {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}} 4: {{v4}} 5: {{v5}}, 6: {{v6}},
|
||||
// 7: {{v7}} end" accesskey="start: {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}} 4: {{v4}} 5:
|
||||
// {{v5}}, 6: {{v6}}, 7: {{v7}} end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 16);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate8(
|
||||
'title', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5,
|
||||
', 6: ', 6, ', 7: ', 7, ', end')(
|
||||
'accessKey', 'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5,
|
||||
', 6: ', 6, ', 7: ', 7, ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, end" title="start: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolate8(
|
||||
'title', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E',
|
||||
', 5: ', 'F', ', 6: ', 'G', ', 7: ', 'H', ', end')(
|
||||
'accessKey', 'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E',
|
||||
', 5: ', 'F', ', 6: ', 'G', ', 7: ', 'H', ', end');
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, end" title="start: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 4,
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if called without Δselect called first', () => {
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 8);
|
||||
expect(() => {
|
||||
t.update(() => {
|
||||
ΔpropertyInterpolate8(
|
||||
'title', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '');
|
||||
});
|
||||
}).toThrow();
|
||||
expect(() => {
|
||||
Δselect(0);
|
||||
t.update(() => {
|
||||
ΔpropertyInterpolate8(
|
||||
'title', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '');
|
||||
});
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('ΔpropertyInterpolateV', () => {
|
||||
it('should interpolate eight or more values', () => {
|
||||
// <div title="start: {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}} 4: {{v4}} 5: {{v5}}, 6: {{v6}},
|
||||
// 7: {{v7}}, 8: {{v8}} end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 9);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolateV('title', [
|
||||
'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5, ', 6: ', 6,
|
||||
', 7: ', 7, ', 8: ', 8, ', end'
|
||||
]);
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div title="start: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolateV('title', [
|
||||
'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', 5: ', 'F',
|
||||
', 6: ', 'G', ', 7: ', 'H', ', 8: ', 'I', ', end'
|
||||
]);
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div title="start: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it('should chain', () => {
|
||||
// <div title="start: {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}} 4: {{v4}} 5: {{v5}}, 6: {{v6}},
|
||||
// 7: {{v7}} end" accesskey="start: {{v0}} 1: {{v1}} 2: {{v2}} 3: {{v3}} 4: {{v4}} 5:
|
||||
// {{v5}}, 6: {{v6}}, 7: {{v7}}, 8: {{v8}} end"></div>
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 18);
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolateV(
|
||||
'title',
|
||||
[
|
||||
'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5, ', 6: ',
|
||||
6, ', 7: ', 7, ', 8: ', 8, ', end'
|
||||
])(
|
||||
'accessKey', [
|
||||
'start: ', 0, ', 1: ', 1, ', 2: ', 2, ', 3: ', 3, ', 4: ', 4, ', 5: ', 5, ', 6: ',
|
||||
6, ', 7: ', 7, ', 8: ', 8, ', end'
|
||||
]);
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, end" title="start: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, end"></div>');
|
||||
t.update(() => {
|
||||
Δselect(0);
|
||||
ΔpropertyInterpolateV(
|
||||
'title',
|
||||
[
|
||||
'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', 5: ',
|
||||
'F', ', 6: ', 'G', ', 7: ', 'H', ', 8: ', 'I', ', end'
|
||||
])(
|
||||
'accessKey', [
|
||||
'start: ', 'A', ', 1: ', 'B', ', 2: ', 'C', ', 3: ', 'D', ', 4: ', 'E', ', 5: ',
|
||||
'F', ', 6: ', 'G', ', 7: ', 'H', ', 8: ', 'I', ', end'
|
||||
]);
|
||||
});
|
||||
expect(t.html).toEqual(
|
||||
'<div accesskey="start: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, end" title="start: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, end"></div>');
|
||||
expect(ngDevMode).toHaveProperties({
|
||||
firstTemplatePass: 1,
|
||||
tNode: 2, // 1 for div, 1 for host element
|
||||
tView: 2, // 1 for rootView + 1 for the template view
|
||||
rendererCreateElement: 1,
|
||||
rendererSetProperty: 4,
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if called without Δselect called first', () => {
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1, 9);
|
||||
expect(() => {
|
||||
t.update(() => {
|
||||
ΔpropertyInterpolateV(
|
||||
'title',
|
||||
['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']);
|
||||
});
|
||||
}).toThrow();
|
||||
expect(() => {
|
||||
Δselect(0);
|
||||
t.update(() => {
|
||||
ΔpropertyInterpolateV(
|
||||
'title',
|
||||
['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']);
|
||||
});
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('elementProperty', () => {
|
||||
it('should use sanitizer function when available', () => {
|
||||
const t = new TemplateFixture(createDiv, () => {}, 1);
|
||||
|
|
Loading…
Reference in New Issue