Skip to content

Commit 522ec1a

Browse files
committed
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
1 parent 9cb5777 commit 522ec1a

5 files changed

Lines changed: 249 additions & 234 deletions

File tree

docs/widget.template

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,21 @@
1313
<h2>Usage</h2>
1414
<h3>In HTML Template Binding</h3>
1515
<tt>
16+
{{^element}}
1617
<pre>
1718
&lt;{{shortName}}{{#param}} {{#default}}<i>[</i>{{/default}}{{name}}="..."{{#default}}<i>]</i>{{/default}}{{/param}}&gt;{{#usageContent}}
1819

1920
{{usageContent}}
2021
{{/usageContent}}&lt;/{{shortName}}&gt;
2122
</pre>
23+
{{/element}}
24+
{{#element}}
25+
<pre>
26+
&lt;{{element}} {{shortName}}{{#paramFirst}}="{{paramFirst.name}}{{/paramFirst}}"&gt;
27+
...
28+
&lt;/{{element}}&gt;
29+
</pre>
30+
{{/element}}
2231
</tt>
2332

2433
<h3>Parameters</h3>

src/directives.js

Lines changed: 0 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -378,160 +378,6 @@ angularDirective("ng:bind-attr", function(expression){
378378
};
379379
});
380380

381-
/**
382-
* @ngdoc directive
383-
* @name angular.directive.ng:non-bindable
384-
*
385-
* @description
386-
* Sometimes it is necessary to write code which looks like
387-
* bindings but which should be left alone by <angular/>.
388-
* Use `ng:non-bindable` to ignore a chunk of HTML.
389-
*
390-
* @element ANY
391-
* @param {string} ignore
392-
*
393-
* @exampleDescription
394-
* In this example there are two location where
395-
* <tt ng:non-bindable>{{1 + 2}}</tt> is present, but the one
396-
* wrapped in `ng:non-bindable` is left alone
397-
* @example
398-
<div>Normal: {{1 + 2}}</div>
399-
<div ng:non-bindable>Ignored: {{1 + 2}}</div>
400-
*
401-
* @scenario
402-
it('should check ng:non-bindable', function(){
403-
expect(using('.doc-example-live').binding('1 + 2')).toBe('3');
404-
expect(using('.doc-example-live').element('div:last').text()).
405-
toMatch(/1 \+ 2/);
406-
});
407-
*/
408-
angularWidget("@ng:non-bindable", noop);
409-
410-
/**
411-
* @ngdoc directive
412-
* @name angular.directive.ng:repeat
413-
*
414-
* @description
415-
* `ng:repeat` instantiates a template once per item from a
416-
* collection. The collection is enumerated with
417-
* `ng:repeat-index` attribute starting from 0. Each template
418-
* instance gets its own scope where the given loop variable
419-
* is set to the current collection item and `$index` is set
420-
* to the item index or key.
421-
*
422-
* NOTE: `ng:repeat` looks like a directive, but is actually a
423-
* attribute widget.
424-
*
425-
* @element ANY
426-
* @param {repeat} repeat_expression to itterate over.
427-
*
428-
* * `variable in expression`, where variable is the user
429-
* defined loop variable and expression is a scope expression
430-
* giving the collection to enumerate. For example:
431-
* `track in cd.tracks`.
432-
* * `(key, value) in expression`, where key and value can
433-
* be any user defined identifiers, and expression is the
434-
* scope expression giving the collection to enumerate.
435-
* For example: `(name, age) in {'adam':10, 'amalie':12}`.
436-
*
437-
* Special properties set on the local scope:
438-
* * {number} $index - iterator offset of the repeated element (0..length-1)
439-
* * {string} $position - position of the repeated element in the iterator ('first', 'middle', 'last')
440-
*
441-
* @exampleDescription
442-
* This example initializes the scope to a list of names and
443-
* than uses `ng:repeat` to display every person.
444-
* @example
445-
<div ng:init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]">
446-
I have {{friends.length}} friends. They are:
447-
<ul>
448-
<li ng:repeat="friend in friends">
449-
[{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
450-
</li>
451-
</ul>
452-
</div>
453-
* @scenario
454-
it('should check ng:repeat', function(){
455-
var r = using('.doc-example-live').repeater('ul li');
456-
expect(r.count()).toBe(2);
457-
expect(r.row(0)).toEqual(["1","John","25"]);
458-
expect(r.row(1)).toEqual(["2","Mary","28"]);
459-
});
460-
*/
461-
angularWidget("@ng:repeat", function(expression, element){
462-
element.removeAttr('ng:repeat');
463-
element.replaceWith(this.comment("ng:repeat: " + expression));
464-
var template = this.compile(element);
465-
return function(reference){
466-
var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/),
467-
lhs, rhs, valueIdent, keyIdent;
468-
if (! match) {
469-
throw Error("Expected ng:repeat in form of 'item in collection' but got '" +
470-
expression + "'.");
471-
}
472-
lhs = match[1];
473-
rhs = match[2];
474-
match = lhs.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/);
475-
if (!match) {
476-
throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" +
477-
keyValue + "'.");
478-
}
479-
valueIdent = match[3] || match[1];
480-
keyIdent = match[2];
481-
482-
var children = [], currentScope = this;
483-
this.$onEval(function(){
484-
var index = 0,
485-
childCount = children.length,
486-
lastElement = reference,
487-
collection = this.$tryEval(rhs, reference),
488-
is_array = isArray(collection),
489-
collectionLength = 0,
490-
childScope,
491-
key;
492-
493-
if (is_array) {
494-
collectionLength = collection.length;
495-
} else {
496-
for (key in collection)
497-
if (collection.hasOwnProperty(key))
498-
collectionLength++;
499-
}
500-
501-
for (key in collection) {
502-
if (!is_array || collection.hasOwnProperty(key)) {
503-
if (index < childCount) {
504-
// reuse existing child
505-
childScope = children[index];
506-
childScope[valueIdent] = collection[key];
507-
if (keyIdent) childScope[keyIdent] = key;
508-
} else {
509-
// grow children
510-
childScope = template(quickClone(element), createScope(currentScope));
511-
childScope[valueIdent] = collection[key];
512-
if (keyIdent) childScope[keyIdent] = key;
513-
lastElement.after(childScope.$element);
514-
childScope.$index = index;
515-
childScope.$position = index == 0 ?
516-
'first' :
517-
(index == collectionLength - 1 ? 'last' : 'middle');
518-
childScope.$element.attr('ng:repeat-index', index);
519-
childScope.$init();
520-
children.push(childScope);
521-
}
522-
childScope.$eval();
523-
lastElement = childScope.$element;
524-
index ++;
525-
}
526-
}
527-
// shrink children
528-
while(children.length > index) {
529-
children.pop().$element.remove();
530-
}
531-
}, reference);
532-
};
533-
});
534-
535381

536382
/**
537383
* @ngdoc directive

src/widgets.js

Lines changed: 158 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,7 @@ var ngSwitch = angularWidget('ng:switch', function (element){
691691
* changing the location or causing page reloads, e.g.:
692692
* <a href="" ng:click="model.$save()">Save</a>
693693
*/
694-
angular.widget('a', function() {
694+
angularWidget('a', function() {
695695
this.descend(true);
696696
this.directives(true);
697697

@@ -702,4 +702,160 @@ angular.widget('a', function() {
702702
});
703703
}
704704
};
705-
});
705+
});
706+
707+
708+
/**
709+
* @ngdoc widget
710+
* @name angular.widget.@ng:repeat
711+
*
712+
* @description
713+
* `ng:repeat` instantiates a template once per item from a collection. The collection is enumerated
714+
* with `ng:repeat-index` attribute starting from 0. Each template instance gets its own scope where
715+
* the given loop variable is set to the current collection item and `$index` is set to the item
716+
* index or key.
717+
*
718+
* There are special properties exposed on the local scope of each template instance:
719+
*
720+
* * `$index` – `{number}` – iterator offset of the repeated element (0..length-1)
721+
* * `$position` – {string} – position of the repeated element in the iterator. One of: `'first'`,
722+
* `'middle'` or `'last'`.
723+
*
724+
* NOTE: `ng:repeat` looks like a directive, but is actually an attribute widget.
725+
*
726+
* @element ANY
727+
* @param {string} repeat_expression The expression indicating how to enumerate a collection. Two
728+
* formats are currently supported:
729+
*
730+
* * `variable in expression` – where variable is the user defined loop variable and `expression`
731+
* is a scope expression giving the collection to enumerate.
732+
*
733+
* For example: `track in cd.tracks`.
734+
* * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
735+
* and `expression` is the scope expression giving the collection to enumerate.
736+
*
737+
* For example: `(name, age) in {'adam':10, 'amalie':12}`.
738+
*
739+
* @exampleDescription
740+
* This example initializes the scope to a list of names and
741+
* than uses `ng:repeat` to display every person.
742+
* @example
743+
<div ng:init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]">
744+
I have {{friends.length}} friends. They are:
745+
<ul>
746+
<li ng:repeat="friend in friends">
747+
[{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
748+
</li>
749+
</ul>
750+
</div>
751+
* @scenario
752+
it('should check ng:repeat', function(){
753+
var r = using('.doc-example-live').repeater('ul li');
754+
expect(r.count()).toBe(2);
755+
expect(r.row(0)).toEqual(["1","John","25"]);
756+
expect(r.row(1)).toEqual(["2","Mary","28"]);
757+
});
758+
*/
759+
angularWidget("@ng:repeat", function(expression, element){
760+
element.removeAttr('ng:repeat');
761+
element.replaceWith(this.comment("ng:repeat: " + expression));
762+
var template = this.compile(element);
763+
return function(reference){
764+
var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/),
765+
lhs, rhs, valueIdent, keyIdent;
766+
if (! match) {
767+
throw Error("Expected ng:repeat in form of 'item in collection' but got '" +
768+
expression + "'.");
769+
}
770+
lhs = match[1];
771+
rhs = match[2];
772+
match = lhs.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/);
773+
if (!match) {
774+
throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" +
775+
keyValue + "'.");
776+
}
777+
valueIdent = match[3] || match[1];
778+
keyIdent = match[2];
779+
780+
var children = [], currentScope = this;
781+
this.$onEval(function(){
782+
var index = 0,
783+
childCount = children.length,
784+
lastElement = reference,
785+
collection = this.$tryEval(rhs, reference),
786+
is_array = isArray(collection),
787+
collectionLength = 0,
788+
childScope,
789+
key;
790+
791+
if (is_array) {
792+
collectionLength = collection.length;
793+
} else {
794+
for (key in collection)
795+
if (collection.hasOwnProperty(key))
796+
collectionLength++;
797+
}
798+
799+
for (key in collection) {
800+
if (!is_array || collection.hasOwnProperty(key)) {
801+
if (index < childCount) {
802+
// reuse existing child
803+
childScope = children[index];
804+
childScope[valueIdent] = collection[key];
805+
if (keyIdent) childScope[keyIdent] = key;
806+
} else {
807+
// grow children
808+
childScope = template(quickClone(element), createScope(currentScope));
809+
childScope[valueIdent] = collection[key];
810+
if (keyIdent) childScope[keyIdent] = key;
811+
lastElement.after(childScope.$element);
812+
childScope.$index = index;
813+
childScope.$position = index == 0 ?
814+
'first' :
815+
(index == collectionLength - 1 ? 'last' : 'middle');
816+
childScope.$element.attr('ng:repeat-index', index);
817+
childScope.$init();
818+
children.push(childScope);
819+
}
820+
childScope.$eval();
821+
lastElement = childScope.$element;
822+
index ++;
823+
}
824+
}
825+
// shrink children
826+
while(children.length > index) {
827+
children.pop().$element.remove();
828+
}
829+
}, reference);
830+
};
831+
});
832+
833+
834+
/**
835+
* @ngdoc widget
836+
* @name angular.widget.@ng:non-bindable
837+
*
838+
* @description
839+
* Sometimes it is necessary to write code which looks like bindings but which should be left alone
840+
* by angular. Use `ng:non-bindable` to make angular ignore a chunk of HTML.
841+
*
842+
* NOTE: `ng:non-bindable` looks like a directive, but is actually an attribute widget.
843+
*
844+
* @element ANY
845+
*
846+
* @exampleDescription
847+
* In this example there are two location where a siple binding (`{{}}`) is present, but the one
848+
* wrapped in `ng:non-bindable` is left alone.
849+
*
850+
* @example
851+
<div>Normal: {{1 + 2}}</div>
852+
<div ng:non-bindable>Ignored: {{1 + 2}}</div>
853+
*
854+
* @scenario
855+
it('should check ng:non-bindable', function(){
856+
expect(using('.doc-example-live').binding('1 + 2')).toBe('3');
857+
expect(using('.doc-example-live').element('div:last').text()).
858+
toMatch(/1 \+ 2/);
859+
});
860+
*/
861+
angularWidget("@ng:non-bindable", noop);

0 commit comments

Comments
 (0)