mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-28 20:10:29 +00:00
style(ng:options): fix style and some docs
This commit is contained in:
parent
f768954f38
commit
17251372b1
2 changed files with 126 additions and 107 deletions
193
src/widgets.js
193
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
|
||||
* @param {comprehension_expression} comprehension in one of the following forms:
|
||||
*
|
||||
* * _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_
|
||||
* * 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}">
|
||||
|
||||
</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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue