mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-03 20:54:43 +00:00
move attribute widgets to widgets.js file
- move @ng:repeat to widgets.js and its specs to widgetsSpecs.js - move @ng:non-bindable to widgets.js and its specs to widgetsSpecs.js - make widget.template suitable for attribute widgets - fix up the js docs for attribute widgets
This commit is contained in:
parent
9cb57772a4
commit
522ec1a9ec
5 changed files with 249 additions and 234 deletions
|
|
@ -13,12 +13,21 @@
|
||||||
<h2>Usage</h2>
|
<h2>Usage</h2>
|
||||||
<h3>In HTML Template Binding</h3>
|
<h3>In HTML Template Binding</h3>
|
||||||
<tt>
|
<tt>
|
||||||
|
{{^element}}
|
||||||
<pre>
|
<pre>
|
||||||
<{{shortName}}{{#param}} {{#default}}<i>[</i>{{/default}}{{name}}="..."{{#default}}<i>]</i>{{/default}}{{/param}}>{{#usageContent}}
|
<{{shortName}}{{#param}} {{#default}}<i>[</i>{{/default}}{{name}}="..."{{#default}}<i>]</i>{{/default}}{{/param}}>{{#usageContent}}
|
||||||
|
|
||||||
{{usageContent}}
|
{{usageContent}}
|
||||||
{{/usageContent}}</{{shortName}}>
|
{{/usageContent}}</{{shortName}}>
|
||||||
</pre>
|
</pre>
|
||||||
|
{{/element}}
|
||||||
|
{{#element}}
|
||||||
|
<pre>
|
||||||
|
<{{element}} {{shortName}}{{#paramFirst}}="{{paramFirst.name}}{{/paramFirst}}">
|
||||||
|
...
|
||||||
|
</{{element}}>
|
||||||
|
</pre>
|
||||||
|
{{/element}}
|
||||||
</tt>
|
</tt>
|
||||||
|
|
||||||
<h3>Parameters</h3>
|
<h3>Parameters</h3>
|
||||||
|
|
|
||||||
|
|
@ -378,160 +378,6 @@ angularDirective("ng:bind-attr", function(expression){
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc directive
|
|
||||||
* @name angular.directive.ng:non-bindable
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Sometimes it is necessary to write code which looks like
|
|
||||||
* bindings but which should be left alone by <angular/>.
|
|
||||||
* Use `ng:non-bindable` to ignore a chunk of HTML.
|
|
||||||
*
|
|
||||||
* @element ANY
|
|
||||||
* @param {string} ignore
|
|
||||||
*
|
|
||||||
* @exampleDescription
|
|
||||||
* In this example there are two location where
|
|
||||||
* <tt ng:non-bindable>{{1 + 2}}</tt> is present, but the one
|
|
||||||
* wrapped in `ng:non-bindable` is left alone
|
|
||||||
* @example
|
|
||||||
<div>Normal: {{1 + 2}}</div>
|
|
||||||
<div ng:non-bindable>Ignored: {{1 + 2}}</div>
|
|
||||||
*
|
|
||||||
* @scenario
|
|
||||||
it('should check ng:non-bindable', function(){
|
|
||||||
expect(using('.doc-example-live').binding('1 + 2')).toBe('3');
|
|
||||||
expect(using('.doc-example-live').element('div:last').text()).
|
|
||||||
toMatch(/1 \+ 2/);
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
angularWidget("@ng:non-bindable", noop);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc directive
|
|
||||||
* @name angular.directive.ng:repeat
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* `ng:repeat` instantiates a template once per item from a
|
|
||||||
* collection. The collection is enumerated with
|
|
||||||
* `ng:repeat-index` attribute starting from 0. Each template
|
|
||||||
* instance gets its own scope where the given loop variable
|
|
||||||
* is set to the current collection item and `$index` is set
|
|
||||||
* to the item index or key.
|
|
||||||
*
|
|
||||||
* NOTE: `ng:repeat` looks like a directive, but is actually a
|
|
||||||
* attribute widget.
|
|
||||||
*
|
|
||||||
* @element ANY
|
|
||||||
* @param {repeat} repeat_expression to itterate over.
|
|
||||||
*
|
|
||||||
* * `variable in expression`, where variable is the user
|
|
||||||
* defined loop variable and expression is a scope expression
|
|
||||||
* giving the collection to enumerate. For example:
|
|
||||||
* `track in cd.tracks`.
|
|
||||||
* * `(key, value) in expression`, where key and value can
|
|
||||||
* be any user defined identifiers, and expression is the
|
|
||||||
* scope expression giving the collection to enumerate.
|
|
||||||
* For example: `(name, age) in {'adam':10, 'amalie':12}`.
|
|
||||||
*
|
|
||||||
* Special properties set on the local scope:
|
|
||||||
* * {number} $index - iterator offset of the repeated element (0..length-1)
|
|
||||||
* * {string} $position - position of the repeated element in the iterator ('first', 'middle', 'last')
|
|
||||||
*
|
|
||||||
* @exampleDescription
|
|
||||||
* This example initializes the scope to a list of names and
|
|
||||||
* than uses `ng:repeat` to display every person.
|
|
||||||
* @example
|
|
||||||
<div ng:init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]">
|
|
||||||
I have {{friends.length}} friends. They are:
|
|
||||||
<ul>
|
|
||||||
<li ng:repeat="friend in friends">
|
|
||||||
[{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
* @scenario
|
|
||||||
it('should check ng:repeat', function(){
|
|
||||||
var r = using('.doc-example-live').repeater('ul li');
|
|
||||||
expect(r.count()).toBe(2);
|
|
||||||
expect(r.row(0)).toEqual(["1","John","25"]);
|
|
||||||
expect(r.row(1)).toEqual(["2","Mary","28"]);
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
angularWidget("@ng:repeat", function(expression, element){
|
|
||||||
element.removeAttr('ng:repeat');
|
|
||||||
element.replaceWith(this.comment("ng:repeat: " + expression));
|
|
||||||
var template = this.compile(element);
|
|
||||||
return function(reference){
|
|
||||||
var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/),
|
|
||||||
lhs, rhs, valueIdent, keyIdent;
|
|
||||||
if (! match) {
|
|
||||||
throw Error("Expected ng:repeat in form of 'item in collection' but got '" +
|
|
||||||
expression + "'.");
|
|
||||||
}
|
|
||||||
lhs = match[1];
|
|
||||||
rhs = match[2];
|
|
||||||
match = lhs.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/);
|
|
||||||
if (!match) {
|
|
||||||
throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" +
|
|
||||||
keyValue + "'.");
|
|
||||||
}
|
|
||||||
valueIdent = match[3] || match[1];
|
|
||||||
keyIdent = match[2];
|
|
||||||
|
|
||||||
var children = [], currentScope = this;
|
|
||||||
this.$onEval(function(){
|
|
||||||
var index = 0,
|
|
||||||
childCount = children.length,
|
|
||||||
lastElement = reference,
|
|
||||||
collection = this.$tryEval(rhs, reference),
|
|
||||||
is_array = isArray(collection),
|
|
||||||
collectionLength = 0,
|
|
||||||
childScope,
|
|
||||||
key;
|
|
||||||
|
|
||||||
if (is_array) {
|
|
||||||
collectionLength = collection.length;
|
|
||||||
} else {
|
|
||||||
for (key in collection)
|
|
||||||
if (collection.hasOwnProperty(key))
|
|
||||||
collectionLength++;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (key in collection) {
|
|
||||||
if (!is_array || collection.hasOwnProperty(key)) {
|
|
||||||
if (index < childCount) {
|
|
||||||
// reuse existing child
|
|
||||||
childScope = children[index];
|
|
||||||
childScope[valueIdent] = collection[key];
|
|
||||||
if (keyIdent) childScope[keyIdent] = key;
|
|
||||||
} else {
|
|
||||||
// grow children
|
|
||||||
childScope = template(quickClone(element), createScope(currentScope));
|
|
||||||
childScope[valueIdent] = collection[key];
|
|
||||||
if (keyIdent) childScope[keyIdent] = key;
|
|
||||||
lastElement.after(childScope.$element);
|
|
||||||
childScope.$index = index;
|
|
||||||
childScope.$position = index == 0 ?
|
|
||||||
'first' :
|
|
||||||
(index == collectionLength - 1 ? 'last' : 'middle');
|
|
||||||
childScope.$element.attr('ng:repeat-index', index);
|
|
||||||
childScope.$init();
|
|
||||||
children.push(childScope);
|
|
||||||
}
|
|
||||||
childScope.$eval();
|
|
||||||
lastElement = childScope.$element;
|
|
||||||
index ++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// shrink children
|
|
||||||
while(children.length > index) {
|
|
||||||
children.pop().$element.remove();
|
|
||||||
}
|
|
||||||
}, reference);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc directive
|
* @ngdoc directive
|
||||||
|
|
|
||||||
160
src/widgets.js
160
src/widgets.js
|
|
@ -691,7 +691,7 @@ var ngSwitch = angularWidget('ng:switch', function (element){
|
||||||
* changing the location or causing page reloads, e.g.:
|
* changing the location or causing page reloads, e.g.:
|
||||||
* <a href="" ng:click="model.$save()">Save</a>
|
* <a href="" ng:click="model.$save()">Save</a>
|
||||||
*/
|
*/
|
||||||
angular.widget('a', function() {
|
angularWidget('a', function() {
|
||||||
this.descend(true);
|
this.descend(true);
|
||||||
this.directives(true);
|
this.directives(true);
|
||||||
|
|
||||||
|
|
@ -702,4 +702,160 @@ angular.widget('a', function() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc widget
|
||||||
|
* @name angular.widget.@ng:repeat
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* `ng:repeat` instantiates a template once per item from a collection. The collection is enumerated
|
||||||
|
* with `ng:repeat-index` attribute starting from 0. Each template instance gets its own scope where
|
||||||
|
* the given loop variable is set to the current collection item and `$index` is set to the item
|
||||||
|
* index or key.
|
||||||
|
*
|
||||||
|
* There are special properties exposed on the local scope of each template instance:
|
||||||
|
*
|
||||||
|
* * `$index` – `{number}` – iterator offset of the repeated element (0..length-1)
|
||||||
|
* * `$position` – {string} – position of the repeated element in the iterator. One of: `'first'`,
|
||||||
|
* `'middle'` or `'last'`.
|
||||||
|
*
|
||||||
|
* NOTE: `ng:repeat` looks like a directive, but is actually an attribute widget.
|
||||||
|
*
|
||||||
|
* @element ANY
|
||||||
|
* @param {string} repeat_expression The expression indicating how to enumerate a collection. Two
|
||||||
|
* formats are currently supported:
|
||||||
|
*
|
||||||
|
* * `variable in expression` – where variable is the user defined loop variable and `expression`
|
||||||
|
* is a scope expression giving the collection to enumerate.
|
||||||
|
*
|
||||||
|
* For example: `track in cd.tracks`.
|
||||||
|
* * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
|
||||||
|
* and `expression` is the scope expression giving the collection to enumerate.
|
||||||
|
*
|
||||||
|
* For example: `(name, age) in {'adam':10, 'amalie':12}`.
|
||||||
|
*
|
||||||
|
* @exampleDescription
|
||||||
|
* This example initializes the scope to a list of names and
|
||||||
|
* than uses `ng:repeat` to display every person.
|
||||||
|
* @example
|
||||||
|
<div ng:init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]">
|
||||||
|
I have {{friends.length}} friends. They are:
|
||||||
|
<ul>
|
||||||
|
<li ng:repeat="friend in friends">
|
||||||
|
[{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
* @scenario
|
||||||
|
it('should check ng:repeat', function(){
|
||||||
|
var r = using('.doc-example-live').repeater('ul li');
|
||||||
|
expect(r.count()).toBe(2);
|
||||||
|
expect(r.row(0)).toEqual(["1","John","25"]);
|
||||||
|
expect(r.row(1)).toEqual(["2","Mary","28"]);
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
angularWidget("@ng:repeat", function(expression, element){
|
||||||
|
element.removeAttr('ng:repeat');
|
||||||
|
element.replaceWith(this.comment("ng:repeat: " + expression));
|
||||||
|
var template = this.compile(element);
|
||||||
|
return function(reference){
|
||||||
|
var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/),
|
||||||
|
lhs, rhs, valueIdent, keyIdent;
|
||||||
|
if (! match) {
|
||||||
|
throw Error("Expected ng:repeat in form of 'item in collection' but got '" +
|
||||||
|
expression + "'.");
|
||||||
|
}
|
||||||
|
lhs = match[1];
|
||||||
|
rhs = match[2];
|
||||||
|
match = lhs.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/);
|
||||||
|
if (!match) {
|
||||||
|
throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" +
|
||||||
|
keyValue + "'.");
|
||||||
|
}
|
||||||
|
valueIdent = match[3] || match[1];
|
||||||
|
keyIdent = match[2];
|
||||||
|
|
||||||
|
var children = [], currentScope = this;
|
||||||
|
this.$onEval(function(){
|
||||||
|
var index = 0,
|
||||||
|
childCount = children.length,
|
||||||
|
lastElement = reference,
|
||||||
|
collection = this.$tryEval(rhs, reference),
|
||||||
|
is_array = isArray(collection),
|
||||||
|
collectionLength = 0,
|
||||||
|
childScope,
|
||||||
|
key;
|
||||||
|
|
||||||
|
if (is_array) {
|
||||||
|
collectionLength = collection.length;
|
||||||
|
} else {
|
||||||
|
for (key in collection)
|
||||||
|
if (collection.hasOwnProperty(key))
|
||||||
|
collectionLength++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (key in collection) {
|
||||||
|
if (!is_array || collection.hasOwnProperty(key)) {
|
||||||
|
if (index < childCount) {
|
||||||
|
// reuse existing child
|
||||||
|
childScope = children[index];
|
||||||
|
childScope[valueIdent] = collection[key];
|
||||||
|
if (keyIdent) childScope[keyIdent] = key;
|
||||||
|
} else {
|
||||||
|
// grow children
|
||||||
|
childScope = template(quickClone(element), createScope(currentScope));
|
||||||
|
childScope[valueIdent] = collection[key];
|
||||||
|
if (keyIdent) childScope[keyIdent] = key;
|
||||||
|
lastElement.after(childScope.$element);
|
||||||
|
childScope.$index = index;
|
||||||
|
childScope.$position = index == 0 ?
|
||||||
|
'first' :
|
||||||
|
(index == collectionLength - 1 ? 'last' : 'middle');
|
||||||
|
childScope.$element.attr('ng:repeat-index', index);
|
||||||
|
childScope.$init();
|
||||||
|
children.push(childScope);
|
||||||
|
}
|
||||||
|
childScope.$eval();
|
||||||
|
lastElement = childScope.$element;
|
||||||
|
index ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// shrink children
|
||||||
|
while(children.length > index) {
|
||||||
|
children.pop().$element.remove();
|
||||||
|
}
|
||||||
|
}, reference);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc widget
|
||||||
|
* @name angular.widget.@ng:non-bindable
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Sometimes it is necessary to write code which looks like bindings but which should be left alone
|
||||||
|
* by angular. Use `ng:non-bindable` to make angular ignore a chunk of HTML.
|
||||||
|
*
|
||||||
|
* NOTE: `ng:non-bindable` looks like a directive, but is actually an attribute widget.
|
||||||
|
*
|
||||||
|
* @element ANY
|
||||||
|
*
|
||||||
|
* @exampleDescription
|
||||||
|
* In this example there are two location where a siple binding (`{{}}`) is present, but the one
|
||||||
|
* wrapped in `ng:non-bindable` is left alone.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
<div>Normal: {{1 + 2}}</div>
|
||||||
|
<div ng:non-bindable>Ignored: {{1 + 2}}</div>
|
||||||
|
*
|
||||||
|
* @scenario
|
||||||
|
it('should check ng:non-bindable', function(){
|
||||||
|
expect(using('.doc-example-live').binding('1 + 2')).toBe('3');
|
||||||
|
expect(using('.doc-example-live').element('div:last').text()).
|
||||||
|
toMatch(/1 \+ 2/);
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
angularWidget("@ng:non-bindable", noop);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
describe("directives", function(){
|
describe("directive", function(){
|
||||||
|
|
||||||
var compile, model, element;
|
var compile, model, element;
|
||||||
|
|
||||||
|
|
@ -128,83 +128,6 @@ describe("directives", function(){
|
||||||
expect(input.checked).toEqual(true);
|
expect(input.checked).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ng:non-bindable', function(){
|
|
||||||
var scope = compile('<div ng:non-bindable><span ng:bind="name"></span></div>');
|
|
||||||
scope.$set('name', 'misko');
|
|
||||||
scope.$eval();
|
|
||||||
expect(element.text()).toEqual('');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
describe('ng:repeat', function() {
|
|
||||||
|
|
||||||
it('should ng:repeat over array', function(){
|
|
||||||
var scope = compile('<ul><li ng:repeat="item in items" ng:init="suffix = \';\'" ng:bind="item + suffix"></li></ul>');
|
|
||||||
|
|
||||||
Array.prototype.extraProperty = "should be ignored";
|
|
||||||
scope.items = ['misko', 'shyam'];
|
|
||||||
scope.$eval();
|
|
||||||
expect(element.text()).toEqual('misko;shyam;');
|
|
||||||
delete Array.prototype.extraProperty;
|
|
||||||
|
|
||||||
scope.items = ['adam', 'kai', 'brad'];
|
|
||||||
scope.$eval();
|
|
||||||
expect(element.text()).toEqual('adam;kai;brad;');
|
|
||||||
|
|
||||||
scope.items = ['brad'];
|
|
||||||
scope.$eval();
|
|
||||||
expect(element.text()).toEqual('brad;');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should ng:repeat over object', function(){
|
|
||||||
var scope = compile('<ul><li ng:repeat="(key, value) in items" ng:bind="key + \':\' + value + \';\' "></li></ul>');
|
|
||||||
scope.$set('items', {misko:'swe', shyam:'set'});
|
|
||||||
scope.$eval();
|
|
||||||
expect(element.text()).toEqual('misko:swe;shyam:set;');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should error on wrong parsing of ng:repeat', function(){
|
|
||||||
var scope = compile('<ul><li ng:repeat="i dont parse"></li></ul>');
|
|
||||||
var log = "";
|
|
||||||
log += element.attr('ng-exception') + ';';
|
|
||||||
log += element.hasClass('ng-exception') + ';';
|
|
||||||
expect(log.match(/Expected ng:repeat in form of 'item in collection' but got 'i dont parse'./)).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should expose iterator offset as $index when iterating over arrays', function() {
|
|
||||||
var scope = compile('<ul><li ng:repeat="item in items" ' +
|
|
||||||
'ng:bind="item + $index + \'|\'"></li></ul>');
|
|
||||||
scope.items = ['misko', 'shyam', 'frodo'];
|
|
||||||
scope.$eval();
|
|
||||||
expect(element.text()).toEqual('misko0|shyam1|frodo2|');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should expose iterator offset as $index when iterating over objects', function() {
|
|
||||||
var scope = compile('<ul><li ng:repeat="(key, val) in items" ' +
|
|
||||||
'ng:bind="key + \':\' + val + $index + \'|\'"></li></ul>');
|
|
||||||
scope.items = {'misko':'m', 'shyam':'s', 'frodo':'f'};
|
|
||||||
scope.$eval();
|
|
||||||
expect(element.text()).toEqual('misko:m0|shyam:s1|frodo:f2|');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should expose iterator position as $position when iterating over arrays', function() {
|
|
||||||
var scope = compile('<ul><li ng:repeat="item in items" ' +
|
|
||||||
'ng:bind="item + \':\' + $position + \'|\'"></li></ul>');
|
|
||||||
scope.items = ['misko', 'shyam', 'doug', 'frodo'];
|
|
||||||
scope.$eval();
|
|
||||||
expect(element.text()).toEqual('misko:first|shyam:middle|doug:middle|frodo:last|');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should expose iterator position as $position when iterating over objects', function() {
|
|
||||||
var scope = compile('<ul><li ng:repeat="(key, val) in items" ' +
|
|
||||||
'ng:bind="key + \':\' + val + \':\' + $position + \'|\'"></li></ul>');
|
|
||||||
scope.items = {'misko':'m', 'shyam':'s', 'doug':'d', 'frodo':'f'};
|
|
||||||
scope.$eval();
|
|
||||||
expect(element.text()).toEqual('misko:m:first|shyam:s:middle|doug:d:middle|frodo:f:last|');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should ng:watch', function(){
|
it('should ng:watch', function(){
|
||||||
var scope = compile('<div ng:watch="i: count = count + 1" ng:init="count = 0">');
|
var scope = compile('<div ng:watch="i: count = count + 1" ng:init="count = 0">');
|
||||||
scope.$eval();
|
scope.$eval();
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ describe("widget", function(){
|
||||||
(before||noop).apply(scope);
|
(before||noop).apply(scope);
|
||||||
if (parent) parent.append(element);
|
if (parent) parent.append(element);
|
||||||
scope.$init();
|
scope.$init();
|
||||||
|
return scope;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -581,5 +582,85 @@ describe("widget", function(){
|
||||||
expect(document.location.href).toEqual(orgLocation);
|
expect(document.location.href).toEqual(orgLocation);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('@ng:repeat', function() {
|
||||||
|
|
||||||
|
it('should ng:repeat over array', function(){
|
||||||
|
var scope = compile('<ul><li ng:repeat="item in items" ng:init="suffix = \';\'" ng:bind="item + suffix"></li></ul>');
|
||||||
|
|
||||||
|
Array.prototype.extraProperty = "should be ignored";
|
||||||
|
scope.items = ['misko', 'shyam'];
|
||||||
|
scope.$eval();
|
||||||
|
expect(element.text()).toEqual('misko;shyam;');
|
||||||
|
delete Array.prototype.extraProperty;
|
||||||
|
|
||||||
|
scope.items = ['adam', 'kai', 'brad'];
|
||||||
|
scope.$eval();
|
||||||
|
expect(element.text()).toEqual('adam;kai;brad;');
|
||||||
|
|
||||||
|
scope.items = ['brad'];
|
||||||
|
scope.$eval();
|
||||||
|
expect(element.text()).toEqual('brad;');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ng:repeat over object', function(){
|
||||||
|
var scope = compile('<ul><li ng:repeat="(key, value) in items" ng:bind="key + \':\' + value + \';\' "></li></ul>');
|
||||||
|
scope.$set('items', {misko:'swe', shyam:'set'});
|
||||||
|
scope.$eval();
|
||||||
|
expect(element.text()).toEqual('misko:swe;shyam:set;');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error on wrong parsing of ng:repeat', function(){
|
||||||
|
var scope = compile('<ul><li ng:repeat="i dont parse"></li></ul>');
|
||||||
|
var log = "";
|
||||||
|
log += element.attr('ng-exception') + ';';
|
||||||
|
log += element.hasClass('ng-exception') + ';';
|
||||||
|
expect(log.match(/Expected ng:repeat in form of 'item in collection' but got 'i dont parse'./)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should expose iterator offset as $index when iterating over arrays', function() {
|
||||||
|
var scope = compile('<ul><li ng:repeat="item in items" ' +
|
||||||
|
'ng:bind="item + $index + \'|\'"></li></ul>');
|
||||||
|
scope.items = ['misko', 'shyam', 'frodo'];
|
||||||
|
scope.$eval();
|
||||||
|
expect(element.text()).toEqual('misko0|shyam1|frodo2|');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should expose iterator offset as $index when iterating over objects', function() {
|
||||||
|
var scope = compile('<ul><li ng:repeat="(key, val) in items" ' +
|
||||||
|
'ng:bind="key + \':\' + val + $index + \'|\'"></li></ul>');
|
||||||
|
scope.items = {'misko':'m', 'shyam':'s', 'frodo':'f'};
|
||||||
|
scope.$eval();
|
||||||
|
expect(element.text()).toEqual('misko:m0|shyam:s1|frodo:f2|');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should expose iterator position as $position when iterating over arrays', function() {
|
||||||
|
var scope = compile('<ul><li ng:repeat="item in items" ' +
|
||||||
|
'ng:bind="item + \':\' + $position + \'|\'"></li></ul>');
|
||||||
|
scope.items = ['misko', 'shyam', 'doug', 'frodo'];
|
||||||
|
scope.$eval();
|
||||||
|
expect(element.text()).toEqual('misko:first|shyam:middle|doug:middle|frodo:last|');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should expose iterator position as $position when iterating over objects', function() {
|
||||||
|
var scope = compile('<ul><li ng:repeat="(key, val) in items" ' +
|
||||||
|
'ng:bind="key + \':\' + val + \':\' + $position + \'|\'"></li></ul>');
|
||||||
|
scope.items = {'misko':'m', 'shyam':'s', 'doug':'d', 'frodo':'f'};
|
||||||
|
scope.$eval();
|
||||||
|
expect(element.text()).toEqual('misko:m:first|shyam:s:middle|doug:d:middle|frodo:f:last|');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('@ng:non-bindable', function() {
|
||||||
|
|
||||||
|
it('should prevent compilation of the owning element and its children', function(){
|
||||||
|
var scope = compile('<div ng:non-bindable><span ng:bind="name"></span></div>');
|
||||||
|
scope.$set('name', 'misko');
|
||||||
|
scope.$eval();
|
||||||
|
expect(element.text()).toEqual('');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue