@@ -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 + i n \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