@@ -596,14 +596,23 @@ angularWidget('button', inputWidgetSelector);
596
596
* * binding to a value not in list confuses most browsers.
597
597
*
598
598
* @element select
599
- * @param {comprehension_expression } comprehension _select_ `as` _label_ `for` _item_ `in` _array_.
599
+ * @param {comprehension_expression } comprehension in following form
600
600
*
601
- * * _array_: an expression which evaluates to an array of objects to bind.
602
- * * _item_: local variable which will refer to the item in the _array_ during the iteration
601
+ * * _select_ `for` _value_ `in` _array_
602
+ * * _select_ `as` _label_ `for` _value_ `in` _array_
603
+ * * _select_ `for` `(`_key_`,` _value_`)` `in` _object_
604
+ * * _select_ `as` _label_ `for` `(`_key_`,` _value_`)` `in` _object_
605
+ *
606
+ * Where:
607
+ *
608
+ * * _array_ / _object_: an expression which evaluates to an array / object to iterate over.
609
+ * * _value_: local variable which will reffer to the item in the _array_ or _object_ during
610
+ * iteration
611
+ * * _key_: local variable which will refer to the key in the _object_ during the iteration
603
612
* * _select_: The result of this expression will be assigned to the scope.
604
613
* The _select_ can be ommited, in which case the _item_ itself will be assigned.
605
614
* * _label_: The result of this expression will be the `option` label. The
606
- * `expression` most likely reffers to the _item_ variable. (optional)
615
+ * `expression` most likely refers to the _item_ variable. (optional)
607
616
*
608
617
* @example
609
618
<doc:example>
@@ -658,8 +667,8 @@ angularWidget('button', inputWidgetSelector);
658
667
</doc:scenario>
659
668
</doc:example>
660
669
*/
661
-
662
- var NG_OPTIONS_REGEXP = / ^ \s * ( ( .* ) \s + a s \s + ) ? ( .* ) \s + f o r \s + ( [ \$ \w ] [ \$ \w \d ] * ) \s + i n \s + ( .* ) $ / ;
670
+ // 000012222111111111133330000000004555555555555555554666666777777777777777776666666888888888888888888888864000000009999
671
+ var NG_OPTIONS_REGEXP = / ^ \s * ( ( .* ) \s + a s \s + ) ? ( .* ) \s + f o r \s + ( ( [ \$ \w ] [ \$ \w \d ] * ) | ( \( \s * ( [ \$ \w ] [ \$ \w \d ] * ) \s * , \s * ( [ \$ \w ] [ \$ \w \d ] * ) \s * \) ) ) \s + i n \s + ( .* ) $ / ;
663
672
angularWidget ( 'select' , function ( element ) {
664
673
this . descend ( true ) ;
665
674
this . directives ( true ) ;
@@ -671,13 +680,14 @@ angularWidget('select', function(element){
671
680
}
672
681
if ( ! ( match = expression . match ( NG_OPTIONS_REGEXP ) ) ) {
673
682
throw Error (
674
- "Expected ng:options in form of '(_expression_ as )? _expresion_ for _item_ in _collection_' but got '" +
683
+ "Expected ng:options in form of '_select_ (as _label_ )? for (_key_,)?_value_ in _collection_' but got '" +
675
684
expression + "'." ) ;
676
685
}
677
686
var displayFn = expressionCompile ( match [ 3 ] ) . fnSelf ;
678
- var itemName = match [ 4 ] ;
679
- var itemFn = expressionCompile ( match [ 2 ] || itemName ) . fnSelf ;
680
- var collectionFn = expressionCompile ( match [ 5 ] ) . fnSelf ;
687
+ var valueName = match [ 5 ] || match [ 8 ] ;
688
+ var keyName = match [ 7 ] ;
689
+ var valueFn = expressionCompile ( match [ 2 ] || valueName ) . fnSelf ;
690
+ var valuesFn = expressionCompile ( match [ 9 ] ) . fnSelf ;
681
691
// we can't just jqLite('<option>') since jqLite is not smart enough
682
692
// to create it in <select> and IE barfs otherwise.
683
693
var option = jqLite ( document . createElement ( 'option' ) ) ;
@@ -696,7 +706,7 @@ angularWidget('select', function(element){
696
706
} ) ;
697
707
698
708
select . bind ( 'change' , function ( ) {
699
- var collection = collectionFn ( scope ) || [ ] ;
709
+ var collection = valuesFn ( scope ) || [ ] ;
700
710
var value = select . val ( ) ;
701
711
var index , length ;
702
712
var tempScope = scope . $new ( ) ;
@@ -705,8 +715,8 @@ angularWidget('select', function(element){
705
715
value = [ ] ;
706
716
for ( index = 0 , length = optionElements . length ; index < length ; index ++ ) {
707
717
if ( optionElements [ index ] [ 0 ] . selected ) {
708
- tempScope [ itemName ] = collection [ index ] ;
709
- value . push ( itemFn ( tempScope ) ) ;
718
+ tempScope [ valueName ] = collection [ index ] ;
719
+ value . push ( valueFn ( tempScope ) ) ;
710
720
}
711
721
}
712
722
} else {
@@ -715,8 +725,8 @@ angularWidget('select', function(element){
715
725
} else if ( value == '' ) {
716
726
value = null ;
717
727
} else {
718
- tempScope [ itemName ] = collection [ value ] ;
719
- value = itemFn ( tempScope ) ;
728
+ tempScope [ valueName ] = collection [ value ] ;
729
+ value = valueFn ( tempScope ) ;
720
730
}
721
731
}
722
732
if ( ! isUndefined ( value ) ) model . set ( value ) ;
@@ -730,7 +740,9 @@ angularWidget('select', function(element){
730
740
731
741
scope . $onEval ( function ( ) {
732
742
var scope = this ;
733
- var collection = collectionFn ( scope ) || [ ] ;
743
+ var values = valuesFn ( scope ) || [ ] ;
744
+ var keys = values ;
745
+ var key ;
734
746
var value ;
735
747
var length ;
736
748
var fragment ;
@@ -753,9 +765,20 @@ angularWidget('select', function(element){
753
765
}
754
766
}
755
767
756
- for ( index = 0 , length = collection . length ; index < length ; index ++ ) {
757
- optionScope [ itemName ] = collection [ index ] ;
758
- currentItem = itemFn ( optionScope ) ;
768
+ // If we have a keyName then we are itterating over on object. We
769
+ // grab the keys and sort them.
770
+ if ( keyName ) {
771
+ keys = [ ] ;
772
+ for ( key in values ) {
773
+ if ( values . hasOwnProperty ( key ) )
774
+ keys . push ( key ) ;
775
+ }
776
+ keys . sort ( ) ;
777
+ }
778
+
779
+ for ( index = 0 ; length = keys . length , index < length ; index ++ ) {
780
+ optionScope [ valueName ] = values [ keyName ? optionScope [ keyName ] = keys [ index ] :index ] ;
781
+ currentItem = valueFn ( optionScope ) ;
759
782
optionText = displayFn ( optionScope ) ;
760
783
if ( optionTexts . length > index ) {
761
784
// reuse
0 commit comments