aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIgor Minar2011-07-25 13:26:45 -0700
committerMisko Hevery2011-07-26 10:11:40 -0700
commit17251372b1dedb42d27b01375125cee4f5edcce7 (patch)
tree40940b5a64c4cc38eeb0940a064bd15d503b910d
parentf768954f38a7077abfd291eaafc0500d2d1e8007 (diff)
downloadangular.js-17251372b1dedb42d27b01375125cee4f5edcce7.tar.bz2
style(ng:options): fix style and some docs
-rw-r--r--src/widgets.js195
-rw-r--r--test/widgetsSpec.js40
2 files changed, 127 insertions, 108 deletions
diff --git a/src/widgets.js b/src/widgets.js
index 17059bca..84b134e0 100644
--- a/src/widgets.js
+++ b/src/widgets.js
@@ -21,7 +21,8 @@
* elements.
* * {@link angular.widget.@ng:required ng:required} - Verifies presence of user input.
* * {@link angular.widget.@ng:validate ng:validate} - Validates content of user input.
- * * {@link angular.widget.HTML HTML} - Standard HTML processed by angular.
+ * * {@link angular.widget.HTML HTML input elements} - Standard HTML input elements data-bound by
+ * angular.
* * {@link angular.widget.ng:view ng:view} - Works with $route to "include" partial templates
* * {@link angular.widget.ng:switch ng:switch} - Conditionally changes DOM structure
* * {@link angular.widget.ng:include ng:include} - Includes an external HTML fragment
@@ -574,11 +575,12 @@ angularWidget('button', inputWidgetSelector);
* @name angular.directive.ng:options
*
* @description
- * Dynamically generate a list of `<option>` elements for a `<select>` element using the array
- * obtained by evaluating the `ng:options` expression.
+ * Dynamically generate a list of `<option>` elements for a `<select>` element using an array or
+ * an object obtained by evaluating the `ng:options` expression.
*
- * When an item in the select menu is select, the array element represented by the selected option
- * will be bound to the model identified by the `name` attribute of the parent select element.
+ * When an item in the select menu is select, the value of array element or object property
+ * represented by the selected option will be bound to the model identified by the `name` attribute
+ * of the parent select element.
*
* Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
* be nested into the `<select>` element. This element will then represent `null` or "not selected"
@@ -596,29 +598,32 @@ angularWidget('button', inputWidgetSelector);
* * binding to a value not in list confuses most browsers.
*
* @element select
- * @param {comprehension_expression} comprehension in following form
- *
- * * _label_ `for` _value_ `in` _array_
- * * _select_ `as` _label_ `for` _value_ `in` _array_
- * * _select_ `as` _label_ `group by` _group_ `for` _value_ `in` _array_
- * * _select_ `group by` _group_ `for` _value_ `in` _array_
- * * _label_ `for` `(`_key_`,` _value_`)` `in` _object_
- * * _select_ `as` _label_ `for` `(`_key_`,` _value_`)` `in` _object_
- * * _select_ `as` _label_ `group by` _group_ `for` `(`_key_`,` _value_`)` `in` _object_
- * * _select_ `group by` _group_ `for` `(`_key_`,` _value_`)` `in` _object_
+ * @param {comprehension_expression} comprehension in one of the following forms:
+ *
+ * * for array data sources:
+ * * `label` **`for`** `value` **`in`** `array`
+ * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
+ * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
+ * * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array`
+ * * for object data sources:
+ * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
+ * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
+ * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
+ * * `select` **`as`** `label` **`group by`** `group`
+ * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
*
* Where:
*
- * * _array_ / _object_: an expression which evaluates to an array / object to iterate over.
- * * _value_: local variable which will refer to each item in the _array_ or each value of
- * _object_ during itteration.
- * * _key_: local variable which will refer to the key in the _object_ during the iteration.
- * * _label_: The result of this expression will be the `option` label. The
- * `expression` will most likely refer to the _value_ variable.
- * * _select_: The result of this expression will be bound to the scope. If not specified,
- * _select_ expression will default to _value_.
- * * _group_: The result of this expression will be used to group options using the `optgroup`
- * DOM element.
+ * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
+ * * `value`: local variable which will refer to each item in the `array` or each property value
+ * of `object` during iteration.
+ * * `key`: local variable which will refer to a property name in `object` during iteration.
+ * * `label`: The result of this expression will be the label for `<option>` element. The
+ * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
+ * * `select`: The result of this expression will be bound to the model of the parent `<select>`
+ * element. If not specified, `select` expression will default to `value`.
+ * * `group`: The result of this expression will be used to group options using the `<optgroup>`
+ * DOM element.
*
* @example
<doc:example>
@@ -626,11 +631,11 @@ angularWidget('button', inputWidgetSelector);
<script>
function MyCntrl(){
this.colors = [
- {name:'black'},
- {name:'white'},
- {name:'red'},
- {name:'blue'},
- {name:'green'}
+ {name:'black', shade:'dark'},
+ {name:'white', shade:'light'},
+ {name:'red', shade:'dark'},
+ {name:'blue', shade:'dark'},
+ {name:'yellow', shade:'light'}
];
this.color = this.colors[2]; // red
}
@@ -638,7 +643,8 @@ angularWidget('button', inputWidgetSelector);
<div ng:controller="MyCntrl">
<ul>
<li ng:repeat="color in colors">
- Name: <input name="color.name"/> [<a href ng:click="colors.$remove(color)">X</a>]
+ Name: <input name="color.name">
+ [<a href ng:click="colors.$remove(color)">X</a>]
</li>
<li>
[<a href ng:click="colors.push({})">add</a>]
@@ -646,19 +652,25 @@ angularWidget('button', inputWidgetSelector);
</ul>
<hr/>
Color (null not allowed):
- <select name="color" ng:options="c.name for c in colors"></select><br/>
+ <select name="color" ng:options="c.name for c in colors"></select><br>
Color (null allowed):
- <select name="color" ng:options="c.name for c in colors">
- <option value="">-- chose color --</option>
+ <div class="nullable">
+ <select name="color" ng:options="c.name for c in colors">
+ <option value="">-- chose color --</option>
+ </select>
+ </div><br/>
+
+ Color grouped by shade:
+ <select name="color" ng:options="c.name group by c.shade for c in colors">
</select><br/>
- Select <a href ng:click="color={name:'not in list'}">bogus</a>. <br/>
+
+ Select <a href ng:click="color={name:'not in list'}">bogus</a>.<br>
<hr/>
Currently selected: {{ {selected_color:color} }}
- <div style="border:solid 1px black;"
+ <div style="border:solid 1px black; height:20px"
ng:style="{'background-color':color.name}">
- &nbsp;
</div>
</div>
</doc:source>
@@ -667,7 +679,7 @@ angularWidget('button', inputWidgetSelector);
expect(binding('color')).toMatch('red');
select('color').option('0');
expect(binding('color')).toMatch('black');
- select('color').option('');
+ using('.nullable').select('color').option('');
expect(binding('color')).toMatch('null');
});
</doc:scenario>
@@ -678,37 +690,41 @@ var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+
angularWidget('select', function(element){
this.descend(true);
this.directives(true);
- var isMultiselect = element.attr('multiple');
- var expression = element.attr('ng:options');
- var onChange = expressionCompile(element.attr('ng:change') || "").fnSelf;
- var match;
+
+ var isMultiselect = element.attr('multiple'),
+ expression = element.attr('ng:options'),
+ onChange = expressionCompile(element.attr('ng:change') || "").fnSelf,
+ match;
+
if (!expression) {
return inputWidgetSelector.call(this, element);
}
if (! (match = expression.match(NG_OPTIONS_REGEXP))) {
throw Error(
- "Expected ng:options in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_' but got '" +
- expression + "'.");
+ "Expected ng:options in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
+ " but got '" + expression + "'.");
}
- var displayFn = expressionCompile(match[2] || match[1]).fnSelf;
- var valueName = match[4] || match[6];
- var keyName = match[5];
- var groupByFn = expressionCompile(match[3] || '').fnSelf;
- var valueFn = expressionCompile(match[2] ? match[1] : valueName).fnSelf;
- var valuesFn = expressionCompile(match[7]).fnSelf;
- // we can't just jqLite('<option>') since jqLite is not smart enough
- // to create it in <select> and IE barfs otherwise.
- var optionTemplate = jqLite(document.createElement('option'));
- var optGroupTemplate = jqLite(document.createElement('optgroup'));
- var nullOption = false; // if false then user will not be able to select it
+
+ var displayFn = expressionCompile(match[2] || match[1]).fnSelf,
+ valueName = match[4] || match[6],
+ keyName = match[5],
+ groupByFn = expressionCompile(match[3] || '').fnSelf,
+ valueFn = expressionCompile(match[2] ? match[1] : valueName).fnSelf,
+ valuesFn = expressionCompile(match[7]).fnSelf,
+ // we can't just jqLite('<option>') since jqLite is not smart enough
+ // to create it in <select> and IE barfs otherwise.
+ optionTemplate = jqLite(document.createElement('option')),
+ optGroupTemplate = jqLite(document.createElement('optgroup')),
+ nullOption = false; // if false then user will not be able to select it
+
return function(selectElement){
- var scope = this;
// This is an array of array of existing option groups in DOM. We try to reuse these if possible
// optionGroupsCache[0] is the options with no option group
// optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
- var optionGroupsCache = [[{element: selectElement, label:''}]];
- var model = modelAccessor(scope, element);
+ var optionGroupsCache = [[{element: selectElement, label:''}]],
+ scope = this,
+ model = modelAccessor(scope, element);
// find existing special options
forEach(selectElement.children(), function(option){
@@ -719,13 +735,12 @@ angularWidget('select', function(element){
selectElement.html(''); // clear contents
selectElement.bind('change', function(){
- var optionGroup;
- var collection = valuesFn(scope) || [];
- var key = selectElement.val();
- var value;
- var optionElement;
- var index, groupIndex, length, groupLength;
- var tempScope = scope.$new();
+ var optionGroup,
+ collection = valuesFn(scope) || [],
+ key = selectElement.val(),
+ tempScope = scope.$new(),
+ value, optionElement, index, groupIndex, length, groupLength;
+
try {
if (isMultiselect) {
value = [];
@@ -767,31 +782,27 @@ angularWidget('select', function(element){
});
scope.$onEval(function(){
- var scope = this;
-
- // Temporary location for the option groups before we render them
- var optionGroups = {
- '':[]
- };
- var optionGroupNames = [''];
- var optionGroupName;
- var optionGroup;
- var option;
- var existingParent, existingOptions, existingOption;
- var values = valuesFn(scope) || [];
- var keys = values;
- var key;
- var groupLength, length;
- var fragment;
- var groupIndex, index;
- var optionElement;
- var optionScope = scope.$new();
- var modelValue = model.get();
- var selected;
- var selectedSet = false; // nothing is selected yet
- var isMulti = isMultiselect;
- var lastElement;
- var element;
+ var scope = this,
+ optionGroups = {'':[]}, // Temporary location for the option groups before we render them
+ optionGroupNames = [''],
+ optionGroupName,
+ optionGroup,
+ option,
+ existingParent, existingOptions, existingOption,
+ values = valuesFn(scope) || [],
+ keys = values,
+ key,
+ groupLength, length,
+ fragment,
+ groupIndex, index,
+ optionElement,
+ optionScope = scope.$new(),
+ modelValue = model.get(),
+ selected,
+ selectedSet = false, // nothing is selected yet
+ isMulti = isMultiselect,
+ lastElement,
+ element;
try {
if (isMulti) {
@@ -807,8 +818,7 @@ angularWidget('select', function(element){
selectedSet = true;
}
- // If we have a keyName then we are iterating over on object. We
- // grab the keys and sort them.
+ // If we have a keyName then we are iterating over on object. Grab the keys and sort them.
if(keyName) {
keys = [];
for (key in values) {
@@ -930,6 +940,7 @@ angularWidget('select', function(element){
};
});
+
/**
* @workInProgress
* @ngdoc widget
diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js
index fc1bb9e3..225f0a1f 100644
--- a/test/widgetsSpec.js
+++ b/test/widgetsSpec.js
@@ -594,13 +594,18 @@ describe("widget", function(){
};
function createSingleSelect(blank, unknown){
- createSelect({name:'selected', 'ng:options':'value.name for value in values'},
- blank, unknown);
+ createSelect({
+ 'name':'selected',
+ 'ng:options':'value.name for value in values'
+ }, blank, unknown);
};
function createMultiSelect(blank, unknown){
- createSelect({name:'selected', multiple:true, 'ng:options':'value.name for value in values'},
- blank, unknown);
+ createSelect({
+ 'name':'selected',
+ 'multiple':true,
+ 'ng:options':'value.name for value in values'
+ }, blank, unknown);
};
afterEach(function(){
@@ -611,9 +616,8 @@ describe("widget", function(){
it('should throw when not formated "? for ? in ?"', function(){
expect(function(){
compile('<select name="selected" ng:options="i dont parse"></select>');
- }).toThrow("Expected ng:options in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_' but got 'i dont parse'.");
-
- $logMock.error.logs.shift();
+ }).toThrow("Expected ng:options in form of '_select_ (as _label_)? for (_key_,)?_value_ in" +
+ " _collection_' but got 'i dont parse'.");
});
it('should render a list', function(){
@@ -630,7 +634,7 @@ describe("widget", function(){
it('should render an object', function(){
createSelect({
- name:'selected',
+ 'name':'selected',
'ng:options': 'value as key for (key, value) in object'
});
scope.object = {'red':'FF0000', 'green':'00FF00', 'blue':'0000FF'};
@@ -762,8 +766,9 @@ describe("widget", function(){
it('should bind to scope value and group', function(){
createSelect({
- name:'selected',
- 'ng:options':'item.name group by item.group for item in values'});
+ 'name':'selected',
+ 'ng:options':'item.name group by item.group for item in values'
+ });
scope.values = [{name:'A'},
{name:'B', group:'first'},
{name:'C', group:'second'},
@@ -793,7 +798,7 @@ describe("widget", function(){
});
it('should bind to scope value through experession', function(){
- createSelect({name:'selected', 'ng:options':'item.id as item.name for item in values'});
+ createSelect({'name':'selected', 'ng:options':'item.id as item.name for item in values'});
scope.values = [{id:10, name:'A'}, {id:20, name:'B'}];
scope.selected = scope.values[0].id;
scope.$eval();
@@ -806,8 +811,9 @@ describe("widget", function(){
it('should bind to object key', function(){
createSelect({
- name:'selected',
- 'ng:options':'key as value for (key, value) in object'});
+ 'name':'selected',
+ 'ng:options':'key as value for (key, value) in object'
+ });
scope.object = {'red':'FF0000', 'green':'00FF00', 'blue':'0000FF'};
scope.selected = 'green';
scope.$eval();
@@ -821,7 +827,8 @@ describe("widget", function(){
it('should bind to object value', function(){
createSelect({
name:'selected',
- 'ng:options':'value as key for (key, value) in object'});
+ 'ng:options':'value as key for (key, value) in object'
+ });
scope.object = {'red':'FF0000', 'green':'00FF00', 'blue':'0000FF'};
scope.selected = '00FF00';
scope.$eval();
@@ -862,7 +869,7 @@ describe("widget", function(){
expect(select.find('option').length).toEqual(2);
});
- it('should insert a unknown option if bound to not in list', function(){
+ it('should insert a unknown option if bound to something not in the list', function(){
createSingleSelect();
scope.values = [{name:'A'}];
scope.selected = {};
@@ -895,7 +902,8 @@ describe("widget", function(){
createSelect({
name:'selected',
'ng:options':'value for value in values',
- 'ng:change':'count = count + 1'});
+ 'ng:change':'count = count + 1'
+ });
scope.values = [{name:'A'}, {name:'B'}];
scope.selected = scope.values[0];
scope.count = 0;