Skip to content

Commit 1725137

Browse files
IgorMinarmhevery
authored andcommitted
style(ng:options): fix style and some docs
1 parent f768954 commit 1725137

File tree

2 files changed

+127
-108
lines changed

2 files changed

+127
-108
lines changed

src/widgets.js

Lines changed: 103 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
* elements.
2222
* * {@link angular.widget.@ng:required ng:required} - Verifies presence of user input.
2323
* * {@link angular.widget.@ng:validate ng:validate} - Validates content of user input.
24-
* * {@link angular.widget.HTML HTML} - Standard HTML processed by angular.
24+
* * {@link angular.widget.HTML HTML input elements} - Standard HTML input elements data-bound by
25+
* angular.
2526
* * {@link angular.widget.ng:view ng:view} - Works with $route to "include" partial templates
2627
* * {@link angular.widget.ng:switch ng:switch} - Conditionally changes DOM structure
2728
* * {@link angular.widget.ng:include ng:include} - Includes an external HTML fragment
@@ -574,11 +575,12 @@ angularWidget('button', inputWidgetSelector);
574575
* @name angular.directive.ng:options
575576
*
576577
* @description
577-
* Dynamically generate a list of `<option>` elements for a `<select>` element using the array
578-
* obtained by evaluating the `ng:options` expression.
578+
* Dynamically generate a list of `<option>` elements for a `<select>` element using an array or
579+
* an object obtained by evaluating the `ng:options` expression.
579580
*
580-
* When an item in the select menu is select, the array element represented by the selected option
581-
* will be bound to the model identified by the `name` attribute of the parent select element.
581+
* When an item in the select menu is select, the value of array element or object property
582+
* represented by the selected option will be bound to the model identified by the `name` attribute
583+
* of the parent select element.
582584
*
583585
* Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
584586
* be nested into the `<select>` element. This element will then represent `null` or "not selected"
@@ -596,69 +598,79 @@ angularWidget('button', inputWidgetSelector);
596598
* * binding to a value not in list confuses most browsers.
597599
*
598600
* @element select
599-
* @param {comprehension_expression} comprehension in following form
600-
*
601-
* * _label_ `for` _value_ `in` _array_
602-
* * _select_ `as` _label_ `for` _value_ `in` _array_
603-
* * _select_ `as` _label_ `group by` _group_ `for` _value_ `in` _array_
604-
* * _select_ `group by` _group_ `for` _value_ `in` _array_
605-
* * _label_ `for` `(`_key_`,` _value_`)` `in` _object_
606-
* * _select_ `as` _label_ `for` `(`_key_`,` _value_`)` `in` _object_
607-
* * _select_ `as` _label_ `group by` _group_ `for` `(`_key_`,` _value_`)` `in` _object_
608-
* * _select_ `group by` _group_ `for` `(`_key_`,` _value_`)` `in` _object_
601+
* @param {comprehension_expression} comprehension in one of the following forms:
602+
*
603+
* * for array data sources:
604+
* * `label` **`for`** `value` **`in`** `array`
605+
* * `select` **`as`** `label` **`for`** `value` **`in`** `array`
606+
* * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
607+
* * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array`
608+
* * for object data sources:
609+
* * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
610+
* * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
611+
* * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
612+
* * `select` **`as`** `label` **`group by`** `group`
613+
* **`for` `(`**`key`**`,`** `value`**`) in`** `object`
609614
*
610615
* Where:
611616
*
612-
* * _array_ / _object_: an expression which evaluates to an array / object to iterate over.
613-
* * _value_: local variable which will refer to each item in the _array_ or each value of
614-
* _object_ during itteration.
615-
* * _key_: local variable which will refer to the key in the _object_ during the iteration.
616-
* * _label_: The result of this expression will be the `option` label. The
617-
* `expression` will most likely refer to the _value_ variable.
618-
* * _select_: The result of this expression will be bound to the scope. If not specified,
619-
* _select_ expression will default to _value_.
620-
* * _group_: The result of this expression will be used to group options using the `optgroup`
621-
* DOM element.
617+
* * `array` / `object`: an expression which evaluates to an array / object to iterate over.
618+
* * `value`: local variable which will refer to each item in the `array` or each property value
619+
* of `object` during iteration.
620+
* * `key`: local variable which will refer to a property name in `object` during iteration.
621+
* * `label`: The result of this expression will be the label for `<option>` element. The
622+
* `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
623+
* * `select`: The result of this expression will be bound to the model of the parent `<select>`
624+
* element. If not specified, `select` expression will default to `value`.
625+
* * `group`: The result of this expression will be used to group options using the `<optgroup>`
626+
* DOM element.
622627
*
623628
* @example
624629
<doc:example>
625630
<doc:source>
626631
<script>
627632
function MyCntrl(){
628633
this.colors = [
629-
{name:'black'},
630-
{name:'white'},
631-
{name:'red'},
632-
{name:'blue'},
633-
{name:'green'}
634+
{name:'black', shade:'dark'},
635+
{name:'white', shade:'light'},
636+
{name:'red', shade:'dark'},
637+
{name:'blue', shade:'dark'},
638+
{name:'yellow', shade:'light'}
634639
];
635640
this.color = this.colors[2]; // red
636641
}
637642
</script>
638643
<div ng:controller="MyCntrl">
639644
<ul>
640645
<li ng:repeat="color in colors">
641-
Name: <input name="color.name"/> [<a href ng:click="colors.$remove(color)">X</a>]
646+
Name: <input name="color.name">
647+
[<a href ng:click="colors.$remove(color)">X</a>]
642648
</li>
643649
<li>
644650
[<a href ng:click="colors.push({})">add</a>]
645651
</li>
646652
</ul>
647653
<hr/>
648654
Color (null not allowed):
649-
<select name="color" ng:options="c.name for c in colors"></select><br/>
655+
<select name="color" ng:options="c.name for c in colors"></select><br>
650656
651657
Color (null allowed):
652-
<select name="color" ng:options="c.name for c in colors">
653-
<option value="">-- chose color --</option>
658+
<div class="nullable">
659+
<select name="color" ng:options="c.name for c in colors">
660+
<option value="">-- chose color --</option>
661+
</select>
662+
</div><br/>
663+
664+
Color grouped by shade:
665+
<select name="color" ng:options="c.name group by c.shade for c in colors">
654666
</select><br/>
655667
656-
Select <a href ng:click="color={name:'not in list'}">bogus</a>. <br/>
668+
669+
Select <a href ng:click="color={name:'not in list'}">bogus</a>.<br>
657670
<hr/>
658671
Currently selected: {{ {selected_color:color} }}
659-
<div style="border:solid 1px black;"
672+
<div style="border:solid 1px black; height:20px"
660673
ng:style="{'background-color':color.name}">
661-
&nbsp;
662674
</div>
663675
</div>
664676
</doc:source>
@@ -667,7 +679,7 @@ angularWidget('button', inputWidgetSelector);
667679
expect(binding('color')).toMatch('red');
668680
select('color').option('0');
669681
expect(binding('color')).toMatch('black');
670-
select('color').option('');
682+
using('.nullable').select('color').option('');
671683
expect(binding('color')).toMatch('null');
672684
});
673685
</doc:scenario>
@@ -678,37 +690,41 @@ var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+
678690
angularWidget('select', function(element){
679691
this.descend(true);
680692
this.directives(true);
681-
var isMultiselect = element.attr('multiple');
682-
var expression = element.attr('ng:options');
683-
var onChange = expressionCompile(element.attr('ng:change') || "").fnSelf;
684-
var match;
693+
694+
var isMultiselect = element.attr('multiple'),
695+
expression = element.attr('ng:options'),
696+
onChange = expressionCompile(element.attr('ng:change') || "").fnSelf,
697+
match;
698+
685699
if (!expression) {
686700
return inputWidgetSelector.call(this, element);
687701
}
688702
if (! (match = expression.match(NG_OPTIONS_REGEXP))) {
689703
throw Error(
690-
"Expected ng:options in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_' but got '" +
691-
expression + "'.");
704+
"Expected ng:options in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
705+
" but got '" + expression + "'.");
692706
}
693-
var displayFn = expressionCompile(match[2] || match[1]).fnSelf;
694-
var valueName = match[4] || match[6];
695-
var keyName = match[5];
696-
var groupByFn = expressionCompile(match[3] || '').fnSelf;
697-
var valueFn = expressionCompile(match[2] ? match[1] : valueName).fnSelf;
698-
var valuesFn = expressionCompile(match[7]).fnSelf;
699-
// we can't just jqLite('<option>') since jqLite is not smart enough
700-
// to create it in <select> and IE barfs otherwise.
701-
var optionTemplate = jqLite(document.createElement('option'));
702-
var optGroupTemplate = jqLite(document.createElement('optgroup'));
703-
var nullOption = false; // if false then user will not be able to select it
707+
708+
var displayFn = expressionCompile(match[2] || match[1]).fnSelf,
709+
valueName = match[4] || match[6],
710+
keyName = match[5],
711+
groupByFn = expressionCompile(match[3] || '').fnSelf,
712+
valueFn = expressionCompile(match[2] ? match[1] : valueName).fnSelf,
713+
valuesFn = expressionCompile(match[7]).fnSelf,
714+
// we can't just jqLite('<option>') since jqLite is not smart enough
715+
// to create it in <select> and IE barfs otherwise.
716+
optionTemplate = jqLite(document.createElement('option')),
717+
optGroupTemplate = jqLite(document.createElement('optgroup')),
718+
nullOption = false; // if false then user will not be able to select it
719+
704720
return function(selectElement){
705-
var scope = this;
706721

707722
// This is an array of array of existing option groups in DOM. We try to reuse these if possible
708723
// optionGroupsCache[0] is the options with no option group
709724
// optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
710-
var optionGroupsCache = [[{element: selectElement, label:''}]];
711-
var model = modelAccessor(scope, element);
725+
var optionGroupsCache = [[{element: selectElement, label:''}]],
726+
scope = this,
727+
model = modelAccessor(scope, element);
712728

713729
// find existing special options
714730
forEach(selectElement.children(), function(option){
@@ -719,13 +735,12 @@ angularWidget('select', function(element){
719735
selectElement.html(''); // clear contents
720736

721737
selectElement.bind('change', function(){
722-
var optionGroup;
723-
var collection = valuesFn(scope) || [];
724-
var key = selectElement.val();
725-
var value;
726-
var optionElement;
727-
var index, groupIndex, length, groupLength;
728-
var tempScope = scope.$new();
738+
var optionGroup,
739+
collection = valuesFn(scope) || [],
740+
key = selectElement.val(),
741+
tempScope = scope.$new(),
742+
value, optionElement, index, groupIndex, length, groupLength;
743+
729744
try {
730745
if (isMultiselect) {
731746
value = [];
@@ -767,31 +782,27 @@ angularWidget('select', function(element){
767782
});
768783

769784
scope.$onEval(function(){
770-
var scope = this;
771-
772-
// Temporary location for the option groups before we render them
773-
var optionGroups = {
774-
'':[]
775-
};
776-
var optionGroupNames = [''];
777-
var optionGroupName;
778-
var optionGroup;
779-
var option;
780-
var existingParent, existingOptions, existingOption;
781-
var values = valuesFn(scope) || [];
782-
var keys = values;
783-
var key;
784-
var groupLength, length;
785-
var fragment;
786-
var groupIndex, index;
787-
var optionElement;
788-
var optionScope = scope.$new();
789-
var modelValue = model.get();
790-
var selected;
791-
var selectedSet = false; // nothing is selected yet
792-
var isMulti = isMultiselect;
793-
var lastElement;
794-
var element;
785+
var scope = this,
786+
optionGroups = {'':[]}, // Temporary location for the option groups before we render them
787+
optionGroupNames = [''],
788+
optionGroupName,
789+
optionGroup,
790+
option,
791+
existingParent, existingOptions, existingOption,
792+
values = valuesFn(scope) || [],
793+
keys = values,
794+
key,
795+
groupLength, length,
796+
fragment,
797+
groupIndex, index,
798+
optionElement,
799+
optionScope = scope.$new(),
800+
modelValue = model.get(),
801+
selected,
802+
selectedSet = false, // nothing is selected yet
803+
isMulti = isMultiselect,
804+
lastElement,
805+
element;
795806

796807
try {
797808
if (isMulti) {
@@ -807,8 +818,7 @@ angularWidget('select', function(element){
807818
selectedSet = true;
808819
}
809820

810-
// If we have a keyName then we are iterating over on object. We
811-
// grab the keys and sort them.
821+
// If we have a keyName then we are iterating over on object. Grab the keys and sort them.
812822
if(keyName) {
813823
keys = [];
814824
for (key in values) {
@@ -930,6 +940,7 @@ angularWidget('select', function(element){
930940
};
931941
});
932942

943+
933944
/**
934945
* @workInProgress
935946
* @ngdoc widget

0 commit comments

Comments
 (0)