@@ -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
}
@@ -1141,7 +1168,42 @@ export function elementAttribute(
1141
1168
}
1142
1169
}
1143
1170
1171
+ // TODO: Remove this when the issue is resolved.
1172
+ /**
1173
+ * Tsickle freaked out over a function returning itself
1174
+ * https://github.com/angular/tsickle/issues/1009)
1175
+ */
1176
+ export type TsickleIssue1009 = any ;
1177
+
1178
+ /**
1179
+ * Update a property on a selected element.
1180
+ *
1181
+ * Operates on the element selected by index via the {@link select} instruction.
1182
+ *
1183
+ * If the property name also exists as an input property on one of the element's directives,
1184
+ * the component property will be set instead of the element property. This check must
1185
+ * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled
1186
+ *
1187
+ * @param propName Name of property. Because it is going to DOM, this is not subject to
1188
+ * renaming as part of minification.
1189
+ * @param value New value to write.
1190
+ * @param sanitizer An optional function used to sanitize the value.
1191
+ * @param nativeOnly Whether or not we should only set native properties and skip input check
1192
+ * (this is necessary for host property bindings)
1193
+ * @returns This function returns itself so that it may be chained
1194
+ * (e.g. `property('name', ctx.name)('title', ctx.title)`)
1195
+ */
1196
+ export function property < T > (
1197
+ propName : string , value : T , sanitizer ?: SanitizerFn | null ,
1198
+ nativeOnly ?: boolean ) : TsickleIssue1009 {
1199
+ const index = getSelectedIndex ( ) ;
1200
+ const bindReconciledValue = bind ( value ) ;
1201
+ elementPropertyInternal ( index , propName , bindReconciledValue , sanitizer , nativeOnly ) ;
1202
+ return property ;
1203
+ }
1204
+
1144
1205
/**
1206
+ * **TODO: Remove this function after `property` is in use**
1145
1207
* Update a property on an element.
1146
1208
*
1147
1209
* If the property name also exists as an input property on one of the element's directives,
@@ -2648,7 +2710,12 @@ export function checkView<T>(hostView: LView, component: T) {
2648
2710
resetPreOrderHookFlags ( hostView ) ;
2649
2711
namespaceHTML ( ) ;
2650
2712
creationMode && executeViewQueryFn ( RenderFlags . Create , hostTView , component ) ;
2713
+
2714
+ // Reset the selected index so we can assert that `select` was called later
2715
+ ngDevMode && setSelectedIndex ( - 1 ) ;
2716
+
2651
2717
templateFn ( getRenderFlags ( hostView ) , component ) ;
2718
+
2652
2719
refreshDescendantViews ( hostView ) ;
2653
2720
// Only check view queries again in creation mode if there are static view queries
2654
2721
if ( ! creationMode || hostTView . staticViewQueries ) {
0 commit comments