@@ -13,7 +13,7 @@ import {Type} from '../../interface/type';
13
13
import { CUSTOM_ELEMENTS_SCHEMA , NO_ERRORS_SCHEMA , SchemaMetadata } from '../../metadata/schema' ;
14
14
import { validateAgainstEventAttributes , validateAgainstEventProperties } from '../../sanitization/sanitization' ;
15
15
import { Sanitizer } from '../../sanitization/security' ;
16
- import { assertDataInRange , assertDefined , assertDomNode , assertEqual , assertLessThan , assertNotEqual } from '../../util/assert' ;
16
+ import { assertDataInRange , assertDefined , assertDomNode , assertEqual , assertGreaterThan , assertLessThan , assertNotEqual } from '../../util/assert' ;
17
17
import { isObservable } from '../../util/lang' ;
18
18
import { normalizeDebugBindingName , normalizeDebugBindingValue } from '../../util/ng_reflect' ;
19
19
import { assertHasParent , assertLContainerOrUndefined , assertLView , assertPreviousIsParent } from '../assert' ;
@@ -37,7 +37,7 @@ import {assertNodeOfPossibleTypes, assertNodeType} from '../node_assert';
37
37
import { appendChild , appendProjectedNodes , createTextNode , insertView , removeView } from '../node_manipulation' ;
38
38
import { isNodeMatchingSelectorList , matchingProjectionSelectorIndex } from '../node_selector_matcher' ;
39
39
import { applyOnCreateInstructions } from '../node_util' ;
40
- import { decreaseElementDepthCount , enterView , getActiveHostContext , getBindingsEnabled , getCheckNoChangesMode , getContextLView , getCurrentDirectiveDef , getElementDepthCount , getIsParent , getLView , getPreviousOrParentTNode , increaseElementDepthCount , isCreationMode , leaveView , nextContextImpl , resetComponentState , setActiveHost , setBindingRoot , setCheckNoChangesMode , setCurrentDirectiveDef , setCurrentQueryIndex , setIsParent , setPreviousOrParentTNode } from '../state' ;
40
+ import { decreaseElementDepthCount , enterView , getActiveHostContext , getBindingsEnabled , getCheckNoChangesMode , getContextLView , getCurrentDirectiveDef , getElementDepthCount , getIsParent , getLView , getPreviousOrParentTNode , getSelectedIndex , increaseElementDepthCount , isCreationMode , leaveView , nextContextImpl , resetComponentState , setActiveHost , setBindingRoot , setCheckNoChangesMode , setCurrentDirectiveDef , setCurrentQueryIndex , setIsParent , setPreviousOrParentTNode , setSelectedIndex } from '../state' ;
41
41
import { getInitialClassNameValue , getInitialStyleStringValue , initializeStaticContext as initializeStaticStylingContext , patchContextWithStaticAttrs , renderInitialClasses , renderInitialStyles } from '../styling/class_and_style_bindings' ;
42
42
import { ANIMATION_PROP_PREFIX , getStylingContext , hasClassInput , hasStyleInput , isAnimationProp } from '../styling/util' ;
43
43
import { NO_CHANGE } from '../tokens' ;
@@ -408,6 +408,10 @@ export function renderEmbeddedTemplate<T>(viewToRender: LView, tView: TView, con
408
408
oldView = enterView ( viewToRender , viewToRender [ T_HOST ] ) ;
409
409
resetPreOrderHookFlags ( viewToRender ) ;
410
410
namespaceHTML ( ) ;
411
+
412
+ // Reset the selected index so we can assert that `select` was called later
413
+ ngDevMode && setSelectedIndex ( - 1 ) ;
414
+
411
415
tView . template ! ( getRenderFlags ( viewToRender ) , context ) ;
412
416
// This must be set to false immediately after the first creation run because in an
413
417
// ngFor loop, all the views will be created together before update mode runs and turns
@@ -453,6 +457,10 @@ function renderComponentOrTemplate<T>(
453
457
// creation mode pass
454
458
if ( templateFn ) {
455
459
namespaceHTML ( ) ;
460
+
461
+ // Reset the selected index so we can assert that `select` was called later
462
+ ngDevMode && setSelectedIndex ( - 1 ) ;
463
+
456
464
templateFn ( RenderFlags . Create , context ) ;
457
465
}
458
466
@@ -1093,11 +1101,30 @@ export function elementEnd(): void {
1093
1101
1094
1102
1095
1103
/**
1096
- * Flushes all the lifecycle hooks for directives up until ( and excluding) that node index
1104
+ * Selects an index of an item to act on and flushes lifecycle hooks up to this point
1097
1105
*
1098
- * @param index The index of the element in the `LView`
1099
- */
1106
+ * Used in conjunction with instructions like {@link property} to act on elements with specified
1107
+ * indices, for example those created with {@link element} or {@link elementStart}.
1108
+ *
1109
+ * ```ts
1110
+ * (rf: RenderFlags, ctx: any) => {
1111
+ * if (rf & 1) {
1112
+ * element(0, 'div');
1113
+ * }
1114
+ * if (rf & 2) {
1115
+ * select(0); // Select the <div/> created above.
1116
+ * property('title', 'test');
1117
+ * }
1118
+ * }
1119
+ * ```
1120
+ * @param index the index of the item to act on with the following instructions
1121
+ */
1100
1122
export function select ( index : number ) : void {
1123
+ ngDevMode && assertGreaterThan ( index , - 1 , 'Invalid index' ) ;
1124
+ ngDevMode &&
1125
+ assertLessThan (
1126
+ index , getLView ( ) . length - HEADER_OFFSET , 'Should be within range for the view data' ) ;
1127
+ setSelectedIndex ( index ) ;
1101
1128
const lView = getLView ( ) ;
1102
1129
executePreOrderHooks ( lView , lView [ TVIEW ] , getCheckNoChangesMode ( ) , index ) ;
1103
1130
}
@@ -1142,6 +1169,34 @@ export function elementAttribute(
1142
1169
}
1143
1170
1144
1171
/**
1172
+ * Update a property on a selected element.
1173
+ *
1174
+ * Operates on the element selected by index via the {@link select} instruction.
1175
+ *
1176
+ * If the property name also exists as an input property on one of the element's directives,
1177
+ * the component property will be set instead of the element property. This check must
1178
+ * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled
1179
+ *
1180
+ * @param propName Name of property. Because it is going to DOM, this is not subject to
1181
+ * renaming as part of minification.
1182
+ * @param value New value to write.
1183
+ * @param sanitizer An optional function used to sanitize the value.
1184
+ * @param nativeOnly Whether or not we should only set native properties and skip input check
1185
+ * (this is necessary for host property bindings)
1186
+ * @returns This function returns itself so that it may be chained
1187
+ * (e.g. `property('name', ctx.name)('title', ctx.title)`)
1188
+ */
1189
+ export function property < T > (
1190
+ propName : string , value : T , sanitizer ?: SanitizerFn | null ,
1191
+ nativeOnly ?: boolean ) : typeof property {
1192
+ const index = getSelectedIndex ( ) ;
1193
+ const bindReconciledValue = bind ( value ) ;
1194
+ elementPropertyInternal ( index , propName , bindReconciledValue , sanitizer , nativeOnly ) ;
1195
+ return property ;
1196
+ }
1197
+
1198
+ /**
1199
+ * **TODO: Remove this function after `property` is in use**
1145
1200
* Update a property on an element.
1146
1201
*
1147
1202
* If the property name also exists as an input property on one of the element's directives,
@@ -2648,7 +2703,12 @@ export function checkView<T>(hostView: LView, component: T) {
2648
2703
resetPreOrderHookFlags ( hostView ) ;
2649
2704
namespaceHTML ( ) ;
2650
2705
creationMode && executeViewQueryFn ( RenderFlags . Create , hostTView , component ) ;
2706
+
2707
+ // Reset the selected index so we can assert that `select` was called later
2708
+ ngDevMode && setSelectedIndex ( - 1 ) ;
2709
+
2651
2710
templateFn ( getRenderFlags ( hostView ) , component ) ;
2711
+
2652
2712
refreshDescendantViews ( hostView ) ;
2653
2713
// Only check view queries again in creation mode if there are static view queries
2654
2714
if ( ! creationMode || hostTView . staticViewQueries ) {
0 commit comments