1
1
part of angular.directive;
2
2
3
- class _Row {
4
- var id;
5
- Scope scope;
6
- View view;
7
- dom.Element startNode;
8
- dom.Element endNode;
9
- List <dom.Element > nodes;
10
-
11
- _Row (this .id);
12
- }
13
-
14
3
/**
15
4
* The `ngRepeat` directive instantiates a template once per item from a
16
5
* collection. Each template instance gets its own scope, where the given loop
@@ -20,15 +9,15 @@ class _Row {
20
9
* Special properties are exposed on the local scope of each template instance,
21
10
* including:
22
11
*
23
- * <table>
24
- * <tr><th> Variable </th><th> Type </th><th> Details <th></tr>
25
- * <tr><td> `$index ` </td><td> [num] </td><td> iterator offset of the repeated element (0..length-1) <td></tr>
26
- * <tr><td> `$first` </td><td> [bool] </td><td> true if the repeated element is first in the iterator. <td></tr>
27
- * <tr><td> `$middle` </td><td> [ bool] </td><td> true if the repeated element is between the first and last in the iterator. <td></tr>
28
- * <tr><td> `$last` </td><td> [bool] </td><td> true if the repeated element is last in the iterator. <td></tr>
29
- * <tr><td> `$even` </td><td> [ bool] </td><td> true if the iterator position `$index` is even (otherwise false). <td></tr>
30
- * <tr><td> `$odd` </td><td> [ bool] </td><td> true if the iterator position `$index` is odd (otherwise false). <td></tr>
31
- * </table>
12
+ * * `$index` ( [:num:] ) the iterator offset of the repeated element
13
+ * (0..length-1)
14
+ * * `$first ` ( [:bool:] ) whether the repeated element is first in the
15
+ * iterator.
16
+ * * `$middle` ( [: bool:] ) whether the repeated element is between the first
17
+ * and last in the iterator.
18
+ * * `$last` ( [: bool:] ) whether the repeated element is last in the iterator.
19
+ * * `$even` ( [: bool:] ) whether the iterator position `$index` is even.
20
+ * * `$odd` ( [:bool:] ) whether the iterator position `$index` is odd.
32
21
*
33
22
*
34
23
* [repeat_expression] ngRepeat The expression indicating how to enumerate a
@@ -57,7 +46,7 @@ class _Row {
57
46
* function can be used to assign a unique `$$hashKey` property to each item
58
47
* in the array. This property is then used as a key to associated DOM
59
48
* elements with the corresponding item in the array by identity. Moving the
60
- * same object in array would move the DOM element in the same way ian the
49
+ * same object in array would move the DOM element in the same way in the
61
50
* DOM.
62
51
*
63
52
* For example: `item in items track by item.id` is a typical pattern when
@@ -81,8 +70,8 @@ class _Row {
81
70
selector: '[ng-repeat]' ,
82
71
map: const {'.' : '@expression' })
83
72
class NgRepeatDirective {
84
- static RegExp _SYNTAX = new RegExp (r'^\s*(.+)\s+in\s+(.*?)\s*(\s+ track\s+by\s+(.+)\s*)?(\s+lazily\s*)?$' );
85
- static RegExp _LHS_SYNTAX = new RegExp (r'^(?:([\ $\w]+)|\(([\ $\w]+)\s*,\s*([\ $\w]+)\))$' );
73
+ static RegExp _SYNTAX = new RegExp (r'^\s*(.+)\s+in\s+(.*?)\s*(?: track\s+by\s+(.+)\s*)?(\s+lazily\s*)?$' );
74
+ static RegExp _LHS_SYNTAX = new RegExp (r'^(?:([$\w]+)|\(([$\w]+)\s*,\s*([$\w]+)\))$' );
86
75
87
76
final ViewPort _viewPort;
88
77
final BoundViewFactory _boundViewFactory;
@@ -97,56 +86,63 @@ class NgRepeatDirective {
97
86
String _listExpr;
98
87
Map <dynamic , _Row > _rows = {};
99
88
Function _trackByIdFn = (key, value, index) => value;
100
- Watch _watch = null ;
101
- Iterable _lastCollection;
89
+ Watch _watch;
102
90
103
- NgRepeatDirective (this ._viewPort, this ._boundViewFactory,
104
- this ._scope, this ._parser, this ._astParser,
105
- this .filters);
91
+ NgRepeatDirective (this ._viewPort, this ._boundViewFactory, this ._scope,
92
+ this ._parser, this ._astParser, this .filters);
106
93
107
94
set expression (value) {
108
95
assert (value != null );
109
96
_expression = value;
110
97
if (_watch != null ) _watch.remove ();
98
+
111
99
Match match = _SYNTAX .firstMatch (_expression);
112
100
if (match == null ) {
113
101
throw "[NgErr7] ngRepeat error! Expected expression in form of '_item_ "
114
102
"in _collection_[ track by _id_]' but got '$_expression '." ;
115
103
}
104
+
116
105
_listExpr = match.group (2 );
117
- var trackByExpr = match.group (4 );
106
+
107
+ var trackByExpr = match.group (3 );
118
108
if (trackByExpr != null ) {
119
109
Expression trackBy = _parser (trackByExpr);
120
110
_trackByIdFn = ((key, value, index) {
121
111
final trackByLocals = < String , Object > {};
122
112
if (_keyIdentifier != null ) trackByLocals[_keyIdentifier] = key;
123
- trackByLocals
124
- ..[_valueIdentifier ] = value
125
- ..[r'$index ' ] = index
126
- ..[ r'$id' ] = (obj) => obj;
127
- return relaxFnArgs (trackBy.eval)( new ScopeLocals (_scope.context, trackByLocals));
113
+ trackByLocals..[_valueIdentifier] = value
114
+ ..[r'$index' ] = index
115
+ ..[r'$id ' ] = (obj) => obj;
116
+ return relaxFnArgs (trackBy.eval)( new ScopeLocals (_scope.context,
117
+ trackByLocals));
128
118
});
129
119
}
120
+
130
121
var assignExpr = match.group (1 );
131
122
match = _LHS_SYNTAX .firstMatch (assignExpr);
132
123
if (match == null ) {
133
124
throw "[NgErr8] ngRepeat error! '_item_' in '_item_ in _collection_' "
134
125
"should be an identifier or '(_key_, _value_)' expression, but got "
135
126
"'$assignExpr '." ;
136
127
}
128
+
137
129
_valueIdentifier = match.group (3 );
138
130
if (_valueIdentifier == null ) _valueIdentifier = match.group (1 );
139
131
_keyIdentifier = match.group (2 );
140
132
141
133
_watch = _scope.watch (
142
134
_astParser (_listExpr, collection: true , filters: filters),
143
- (CollectionChangeRecord collection , _) {
135
+ (CollectionChangeRecord changeRecord , _) {
144
136
//TODO(misko): we should take advantage of the CollectionChangeRecord!
145
- _onCollectionChange (collection == null ? [] : collection.iterable);
137
+ if (changeRecord == null ) return ;
138
+ _onCollectionChange (changeRecord.iterable);
139
+
146
140
}
147
141
);
148
142
}
149
143
144
+
145
+ // todo -> collection
150
146
List <_Row > _computeNewRows (Iterable collection, trackById) {
151
147
final newRowOrder = new List <_Row >(collection.length);
152
148
// Same as lastViewMap but it has the current state. It will become the
@@ -157,8 +153,7 @@ class NgRepeatDirective {
157
153
var value = collection.elementAt (index);
158
154
trackById = _trackByIdFn (index, value, index);
159
155
if (_rows.containsKey (trackById)) {
160
- var row = _rows[trackById];
161
- _rows.remove (trackById);
156
+ var row = _rows.remove (trackById);
162
157
newRows[trackById] = row;
163
158
newRowOrder[index] = row;
164
159
} else if (newRows.containsKey (trackById)) {
@@ -185,9 +180,9 @@ class NgRepeatDirective {
185
180
return newRowOrder;
186
181
}
187
182
188
- _onCollectionChange (Iterable collection) {
189
- dom. Node previousNode = _viewPort.placeholder; // current position of the
190
- // node
183
+ void _onCollectionChange (Iterable collection) {
184
+ // current position of the node
185
+ dom. Node previousNode = _viewPort.placeholder;
191
186
dom.Node nextNode;
192
187
Scope childScope;
193
188
Map childContext;
@@ -218,33 +213,44 @@ class NgRepeatDirective {
218
213
previousNode = row.endNode;
219
214
} else {
220
215
// new item which we don't know about
221
- childScope = _scope.createChild (childContext = new PrototypeMap (_scope.context));
216
+ childScope = _scope.createChild (childContext =
217
+ new PrototypeMap (_scope.context));
222
218
}
223
219
224
220
if (! identical (childScope.context[_valueIdentifier], value)) {
225
221
childContext[_valueIdentifier] = value;
226
222
}
227
223
var first = (index == 0 );
228
224
var last = (index == collection.length - 1 );
229
- childContext
230
- ..[r'$index' ] = index
231
- ..[r'$first' ] = first
232
- ..[r'$last' ] = last
233
- ..[r'$middle' ] = ! first && ! last
234
- ..[r'$odd' ] = index & 1 == 1
235
- ..[r'$even' ] = index & 1 == 0 ;
225
+ childContext..[r'$index' ] = index
226
+ ..[r'$first' ] = first
227
+ ..[r'$last' ] = last
228
+ ..[r'$middle' ] = ! first && ! last
229
+ ..[r'$odd' ] = index & 1 == 1
230
+ ..[r'$even' ] = index & 1 == 0 ;
236
231
237
232
if (row.startNode == null ) {
238
233
var view = _boundViewFactory (childScope);
239
234
_rows[row.id] = row
240
235
..view = view
241
236
..scope = childScope
242
237
..nodes = view.nodes
243
- ..startNode = row.nodes[ 0 ]
244
- ..endNode = row.nodes[row.nodes.length - 1 ] ;
238
+ ..startNode = row.nodes.first
239
+ ..endNode = row.nodes.last ;
245
240
_viewPort.insert (view, insertAfter: cursor);
246
241
}
247
242
cursor = row.view;
248
243
}
249
244
}
250
245
}
246
+
247
+ class _Row {
248
+ final id;
249
+ Scope scope;
250
+ View view;
251
+ dom.Element startNode;
252
+ dom.Element endNode;
253
+ List <dom.Element > nodes;
254
+
255
+ _Row (this .id);
256
+ }
0 commit comments