1
1
part of angular.directive;
2
2
3
- class _Row {
4
- final 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;
@@ -96,55 +85,63 @@ class NgRepeatDirective {
96
85
String _listExpr;
97
86
Map <dynamic , _Row > _rows = {};
98
87
Function _trackByIdFn = (key, value, index) => value;
99
- Watch _watch = null ;
100
- Iterable _lastCollection;
88
+ Watch _watch;
101
89
102
90
NgRepeatDirective (this ._viewPort, this ._boundViewFactory, this ._scope,
103
- this ._parser, this .filters);
91
+ this ._parser, this ._astParser, this . filters);
104
92
105
93
set expression (value) {
106
94
assert (value != null );
107
95
_expression = value;
108
96
if (_watch != null ) _watch.remove ();
97
+
109
98
Match match = _SYNTAX .firstMatch (_expression);
110
99
if (match == null ) {
111
100
throw "[NgErr7] ngRepeat error! Expected expression in form of '_item_ "
112
101
"in _collection_[ track by _id_]' but got '$_expression '." ;
113
102
}
103
+
114
104
_listExpr = match.group (2 );
115
- var trackByExpr = match.group (4 );
105
+
106
+ var trackByExpr = match.group (3 );
116
107
if (trackByExpr != null ) {
117
108
Expression trackBy = _parser (trackByExpr);
118
109
_trackByIdFn = ((key, value, index) {
119
110
final trackByLocals = < String , Object > {};
120
111
if (_keyIdentifier != null ) trackByLocals[_keyIdentifier] = key;
121
- trackByLocals
122
- ..[_valueIdentifier ] = value
123
- ..[r'$index ' ] = index
124
- ..[ r'$id' ] = (obj) => obj;
125
- return relaxFnArgs (trackBy.eval)( new ScopeLocals (_scope.context, trackByLocals));
112
+ trackByLocals..[_valueIdentifier] = value
113
+ ..[r'$index' ] = index
114
+ ..[r'$id ' ] = (obj) => obj;
115
+ return relaxFnArgs (trackBy.eval)( new ScopeLocals (_scope.context,
116
+ trackByLocals));
126
117
});
127
118
}
119
+
128
120
var assignExpr = match.group (1 );
129
121
match = _LHS_SYNTAX .firstMatch (assignExpr);
130
122
if (match == null ) {
131
123
throw "[NgErr8] ngRepeat error! '_item_' in '_item_ in _collection_' "
132
124
"should be an identifier or '(_key_, _value_)' expression, but got "
133
125
"'$assignExpr '." ;
134
126
}
127
+
135
128
_valueIdentifier = match.group (3 );
136
129
if (_valueIdentifier == null ) _valueIdentifier = match.group (1 );
137
130
_keyIdentifier = match.group (2 );
138
131
132
+ _watch = _scope.watch (
133
+ _astParser (_listExpr, collection: true , filters: filters),
134
+ (CollectionChangeRecord changeRecord, _) {
135
+ //TODO(misko): we should take advantage of the CollectionChangeRecord!
136
+ if (changeRecord == null ) return ;
137
+ _onCollectionChange (changeRecord.iterable);
139
138
140
- _watch = _scope.watch (_listExpr, (CollectionChangeRecord collection, _) {
141
- //TODO(misko): we should take advantage of the CollectionChangeRecord!
142
- _onCollectionChange (collection == null ? [] : collection.iterable);
143
- },
144
- collection: true ,
145
- filters: filters);
139
+ }
140
+ );
146
141
}
147
142
143
+
144
+ // todo -> collection
148
145
List <_Row > _computeNewRows (Iterable collection, trackById) {
149
146
final newRowOrder = new List <_Row >(collection.length);
150
147
// Same as lastViewMap but it has the current state. It will become the
@@ -155,8 +152,7 @@ class NgRepeatDirective {
155
152
var value = collection.elementAt (index);
156
153
trackById = _trackByIdFn (index, value, index);
157
154
if (_rows.containsKey (trackById)) {
158
- var row = _rows[trackById];
159
- _rows.remove (trackById);
155
+ var row = _rows.remove (trackById);
160
156
newRows[trackById] = row;
161
157
newRowOrder[index] = row;
162
158
} else if (newRows.containsKey (trackById)) {
@@ -183,9 +179,9 @@ class NgRepeatDirective {
183
179
return newRowOrder;
184
180
}
185
181
186
- _onCollectionChange (Iterable collection) {
187
- dom. Node previousNode = _viewPort.placeholder; // current position of the
188
- // node
182
+ void _onCollectionChange (Iterable collection) {
183
+ // current position of the node
184
+ dom. Node previousNode = _viewPort.placeholder;
189
185
dom.Node nextNode;
190
186
Scope childScope;
191
187
Map childContext;
@@ -216,33 +212,44 @@ class NgRepeatDirective {
216
212
previousNode = row.endNode;
217
213
} else {
218
214
// new item which we don't know about
219
- childScope = _scope.createChild (childContext = new PrototypeMap (_scope.context));
215
+ childScope = _scope.createChild (childContext =
216
+ new PrototypeMap (_scope.context));
220
217
}
221
218
222
219
if (! identical (childScope.context[_valueIdentifier], value)) {
223
220
childContext[_valueIdentifier] = value;
224
221
}
225
222
var first = (index == 0 );
226
223
var last = (index == collection.length - 1 );
227
- childContext
228
- ..[r'$index' ] = index
229
- ..[r'$first' ] = first
230
- ..[r'$last' ] = last
231
- ..[r'$middle' ] = ! first && ! last
232
- ..[r'$odd' ] = index & 1 == 1
233
- ..[r'$even' ] = index & 1 == 0 ;
224
+ childContext..[r'$index' ] = index
225
+ ..[r'$first' ] = first
226
+ ..[r'$last' ] = last
227
+ ..[r'$middle' ] = ! first && ! last
228
+ ..[r'$odd' ] = index & 1 == 1
229
+ ..[r'$even' ] = index & 1 == 0 ;
234
230
235
231
if (row.startNode == null ) {
236
232
var view = _boundViewFactory (childScope);
237
233
_rows[row.id] = row
238
234
..view = view
239
235
..scope = childScope
240
236
..nodes = view.nodes
241
- ..startNode = row.nodes[ 0 ]
242
- ..endNode = row.nodes[row.nodes.length - 1 ] ;
237
+ ..startNode = row.nodes.first
238
+ ..endNode = row.nodes.last ;
243
239
_viewPort.insert (view, insertAfter: cursor);
244
240
}
245
241
cursor = row.view;
246
242
}
247
243
}
248
244
}
245
+
246
+ class _Row {
247
+ final id;
248
+ Scope scope;
249
+ View view;
250
+ dom.Element startNode;
251
+ dom.Element endNode;
252
+ List <dom.Element > nodes;
253
+
254
+ _Row (this .id);
255
+ }
0 commit comments