39
39
* * `label` **`for`** `value` **`in`** `array`
40
40
* * `select` **`as`** `label` **`for`** `value` **`in`** `array`
41
41
* * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
42
- * * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array`
42
+ * * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
43
43
* * for object data sources:
44
44
* * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
45
45
* * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
59
59
* element. If not specified, `select` expression will default to `value`.
60
60
* * `group`: The result of this expression will be used to group options using the `<optgroup>`
61
61
* DOM element.
62
+ * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
63
+ * used to identify the objects in the array. The `trackexpr` will most likely refer to the
64
+ * `value` variable (e.g. `value.propertyName`).
62
65
*
63
66
* @example
64
67
<doc:example>
123
126
124
127
var ngOptionsDirective = valueFn ( { terminal : true } ) ;
125
128
var selectDirective = [ '$compile' , '$parse' , function ( $compile , $parse ) {
126
- //0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000077770
127
- var NG_OPTIONS_REGEXP = / ^ \s * ( .* ?) (?: \s + a s \s + ( .* ?) ) ? (?: \s + g r o u p \s + b y \s + ( .* ) ) ? \s + f o r \s + (?: ( [ \$ \w ] [ \$ \w \d ] * ) | (?: \( \s * ( [ \$ \w ] [ \$ \w \d ] * ) \s * , \s * ( [ \$ \w ] [ \$ \w \d ] * ) \s * \) ) ) \s + i n \s + ( .* ) $ / ,
129
+ //0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000007777000000000000000000088888
130
+ var NG_OPTIONS_REGEXP = / ^ \s * ( .* ?) (?: \s + a s \s + ( .* ?) ) ? (?: \s + g r o u p \s + b y \s + ( .* ) ) ? \s + f o r \s + (?: ( [ \$ \w ] [ \$ \w \d ] * ) | (?: \( \s * ( [ \$ \w ] [ \$ \w \d ] * ) \s * , \s * ( [ \$ \w ] [ \$ \w \d ] * ) \s * \) ) ) \s + i n \s + ( .* ? ) (?: \s + t r a c k \s + b y \s + ( . * ? ) ) ? $ / ,
128
131
nullModelCtrl = { $setViewValue : noop } ;
129
132
130
133
return {
@@ -298,7 +301,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
298
301
299
302
if ( ! ( match = optionsExp . match ( NG_OPTIONS_REGEXP ) ) ) {
300
303
throw Error (
301
- "Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
304
+ "Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_ (track by _expr_)? '" +
302
305
" but got '" + optionsExp + "'." ) ;
303
306
}
304
307
@@ -308,6 +311,8 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
308
311
groupByFn = $parse ( match [ 3 ] || '' ) ,
309
312
valueFn = $parse ( match [ 2 ] ? match [ 1 ] : valueName ) ,
310
313
valuesFn = $parse ( match [ 7 ] ) ,
314
+ track = match [ 8 ] ,
315
+ trackFn = track ? $parse ( match [ 8 ] ) : null ,
311
316
// This is an array of array of existing option groups in DOM. We try to reuse these if possible
312
317
// optionGroupsCache[0] is the options with no option group
313
318
// optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
@@ -348,7 +353,14 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
348
353
if ( ( optionElement = optionGroup [ index ] . element ) [ 0 ] . selected ) {
349
354
key = optionElement . val ( ) ;
350
355
if ( keyName ) locals [ keyName ] = key ;
351
- locals [ valueName ] = collection [ key ] ;
356
+ if ( trackFn ) {
357
+ for ( var trackIndex = 0 ; trackIndex < collection . length ; trackIndex ++ ) {
358
+ locals [ valueName ] = collection [ trackIndex ] ;
359
+ if ( trackFn ( scope , locals ) == key ) break ;
360
+ }
361
+ } else {
362
+ locals [ valueName ] = collection [ key ] ;
363
+ }
352
364
value . push ( valueFn ( scope , locals ) ) ;
353
365
}
354
366
}
@@ -360,9 +372,19 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
360
372
} else if ( key == '' ) {
361
373
value = null ;
362
374
} else {
363
- locals [ valueName ] = collection [ key ] ;
364
- if ( keyName ) locals [ keyName ] = key ;
365
- value = valueFn ( scope , locals ) ;
375
+ if ( trackFn ) {
376
+ for ( var trackIndex = 0 ; trackIndex < collection . length ; trackIndex ++ ) {
377
+ locals [ valueName ] = collection [ trackIndex ] ;
378
+ if ( trackFn ( scope , locals ) == key ) {
379
+ value = valueFn ( scope , locals ) ;
380
+ break ;
381
+ }
382
+ }
383
+ } else {
384
+ locals [ valueName ] = collection [ key ] ;
385
+ if ( keyName ) locals [ keyName ] = key ;
386
+ value = valueFn ( scope , locals ) ;
387
+ }
366
388
}
367
389
}
368
390
ctrl . $setViewValue ( value ) ;
@@ -394,7 +416,15 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
394
416
label ;
395
417
396
418
if ( multiple ) {
397
- selectedSet = new HashMap ( modelValue ) ;
419
+ if ( trackFn && isArray ( modelValue ) ) {
420
+ selectedSet = new HashMap ( [ ] ) ;
421
+ for ( var trackIndex = 0 ; trackIndex < modelValue . length ; trackIndex ++ ) {
422
+ locals [ valueName ] = modelValue [ trackIndex ] ;
423
+ selectedSet . put ( trackFn ( scope , locals ) , modelValue [ trackIndex ] ) ;
424
+ }
425
+ } else {
426
+ selectedSet = new HashMap ( modelValue ) ;
427
+ }
398
428
}
399
429
400
430
// We now build up the list of options we need (we merge later)
@@ -406,15 +436,21 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
406
436
optionGroupNames . push ( optionGroupName ) ;
407
437
}
408
438
if ( multiple ) {
409
- selected = selectedSet . remove ( valueFn ( scope , locals ) ) != undefined ;
439
+ selected = selectedSet . remove ( trackFn ? trackFn ( scope , locals ) : valueFn ( scope , locals ) ) != undefined ;
410
440
} else {
411
- selected = modelValue === valueFn ( scope , locals ) ;
441
+ if ( trackFn ) {
442
+ var modelCast = { } ;
443
+ modelCast [ valueName ] = modelValue ;
444
+ selected = trackFn ( scope , modelCast ) === trackFn ( scope , locals ) ;
445
+ } else {
446
+ selected = modelValue === valueFn ( scope , locals ) ;
447
+ }
412
448
selectedSet = selectedSet || selected ; // see if at least one item is selected
413
449
}
414
450
label = displayFn ( scope , locals ) ; // what will be seen by the user
415
451
label = label === undefined ? '' : label ; // doing displayFn(scope, locals) || '' overwrites zero values
416
452
optionGroup . push ( {
417
- id : keyName ? keys [ index ] : index , // either the index into array or key from object
453
+ id : trackFn ? trackFn ( scope , locals ) : ( keyName ? keys [ index ] : index ) , // either the index into array or key from object
418
454
label : label ,
419
455
selected : selected // determine if we should be selected
420
456
} ) ;
0 commit comments