Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(134)

Side by Side Diff: third_party/pkg/angular/lib/directive/ng_repeat.dart

Issue 256553002: Revert "Update all Angular libs (run update_all.sh)." (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 part of angular.directive; 1 part of angular.directive;
2 2
3 class _Row {
4 var id;
5 Scope scope;
6 Block block;
7 dom.Element startNode;
8 dom.Element endNode;
9 List<dom.Element> elements;
10
11 _Row(this.id);
12 }
13
3 /** 14 /**
4 * The `ngRepeat` directive instantiates a template once per item from a 15 * The `ngRepeat` directive instantiates a template once per item from a
5 * collection. Each template instance gets its own scope, where the given loop 16 * collection. Each template instance gets its own scope, where the given loop
6 * variable is set to the current collection item, and `$index` is set to the 17 * variable is set to the current collection item, and `$index` is set to the
7 * item index or key. 18 * item index or key.
8 * 19 *
9 * Special properties are exposed on the local scope of each template instance, 20 * Special properties are exposed on the local scope of each template instance,
10 * including: 21 * including:
11 * 22 *
12 * * `$index` ([:num:]) the iterator offset of the repeated element 23 * <table>
13 * (0..length-1) 24 * <tr><th> Variable </th><th> Type </th><th> Details <th></tr>
14 * * `$first` ([:bool:]) whether the repeated element is first in the 25 * <tr><td> `$index` </td><td>[num] </td><td> iterator offset of the repeated e lement (0..length-1) <td></tr>
15 * iterator. 26 * <tr><td> `$first` </td><td>[bool]</td><td> true if the repeated element is f irst in the iterator. <td></tr>
16 * * `$middle` ([:bool:]) whether the repeated element is between the first 27 * <tr><td> `$middle` </td><td>[bool]</td><td> true if the repeated element is b etween the first and last in the iterator. <td></tr>
17 * and last in the iterator. 28 * <tr><td> `$last` </td><td>[bool]</td><td> true if the repeated element is l ast in the iterator. <td></tr>
18 * * `$last` ([:bool:]) whether the repeated element is last in the iterator. 29 * <tr><td> `$even` </td><td>[bool]</td><td> true if the iterator position `$i ndex` is even (otherwise false). <td></tr>
19 * * `$even` ([:bool:]) whether the iterator position `$index` is even. 30 * <tr><td> `$odd` </td><td>[bool]</td><td> true if the iterator position `$i ndex` is odd (otherwise false). <td></tr>
20 * * `$odd` ([:bool:]) whether the iterator position `$index` is odd. 31 * </table>
21 * 32 *
22 * 33 *
23 * [repeat_expression] ngRepeat The expression indicating how to enumerate a 34 * [repeat_expression] ngRepeat The expression indicating how to enumerate a
24 * collection. These formats are currently supported: 35 * collection. These formats are currently supported:
25 * 36 *
26 * * `variable in expression` – where variable is the user defined loop 37 * * `variable in expression` – where variable is the user defined loop
27 * variable and `expression` is a scope expression giving the collection to 38 * variable and `expression` is a scope expression giving the collection to
28 * enumerate. 39 * enumerate.
29 * 40 *
30 * For example: `album in artist.albums`. 41 * For example: `album in artist.albums`.
31 * 42 *
32 * * `variable in expression track by tracking_expression` – You can also 43 * * `variable in expression track by tracking_expression` – You can also
33 * provide an optional tracking function which can be used to associate the 44 * provide an optional tracking function which can be used to associate the
34 * objects in the collection with the DOM elements. If no tracking function is 45 * objects in the collection with the DOM elements. If no tracking function is
35 * specified the ng-repeat associates elements by identity in the collection. 46 * specified the ng-repeat associates elements by identity in the collection.
36 * It is an error to have more than one tracking function to resolve to the 47 * It is an error to have more than one tracking function to resolve to the
37 * same key. (This would mean that two distinct objects are mapped to the same 48 * same key. (This would mean that two distinct objects are mapped to the same
38 * DOM element, which is not possible.) Filters should be applied to the 49 * DOM element, which is not possible.) Filters should be applied to the
39 * expression, before specifying a tracking expression. 50 * expression, before specifying a tracking expression.
40 * 51 *
41 * For example: `item in items` is equivalent to `item in items track by 52 * For example: `item in items` is equivalent to `item in items track by
42 * $id(item)`. This implies that the DOM elements will be associated by item 53 * $id(item)`. This implies that the DOM elements will be associated by item
43 * identity in the array. 54 * identity in the array.
44 * 55 *
45 * For example: `item in items track by $id(item)`. A built in `$id()` 56 * For example: `item in items track by $id(item)`. A built in `$id()`
46 * function can be used to assign a unique `$$hashKey` property to each item 57 * function can be used to assign a unique `$$hashKey` property to each item
47 * in the array. This property is then used as a key to associated DOM 58 * in the array. This property is then used as a key to associated DOM
48 * elements with the corresponding item in the array by identity. Moving the 59 * elements with the corresponding item in the array by identity. Moving the
49 * same object in array would move the DOM element in the same way in the 60 * same object in array would move the DOM element in the same way ian the
50 * DOM. 61 * DOM.
51 * 62 *
52 * For example: `item in items track by item.id` is a typical pattern when 63 * For example: `item in items track by item.id` is a typical pattern when
53 * the items come from the database. In this case the object identity does 64 * the items come from the database. In this case the object identity does
54 * not matter. Two objects are considered equivalent as long as their `id` 65 * not matter. Two objects are considered equivalent as long as their `id`
55 * property is same. 66 * property is same.
56 * 67 *
57 * For example: `item in items | filter:searchText track by item.id` is a 68 * For example: `item in items | filter:searchText track by item.id` is a
58 * pattern that might be used to apply a formatter to items in conjunction w ith 69 * pattern that might be used to apply a filter to items in conjunction with
59 * a tracking expression. 70 * a tracking expression.
60 * 71 *
61 * # Example: 72 * # Example:
62 * 73 *
63 * <ul> 74 * <ul>
64 * <li ng-repeat="item in ['foo', 'bar', 'baz']">{{item}}</li> 75 * <li ng-repeat="item in ['foo', 'bar', 'baz']">{{item}}</li>
65 * </ul> 76 * </ul>
66 */ 77 */
67 78
68 @Decorator( 79 @NgDirective(
69 children: Directive.TRANSCLUDE_CHILDREN, 80 children: NgAnnotation.TRANSCLUDE_CHILDREN,
70 selector: '[ng-repeat]', 81 selector: '[ng-repeat]',
71 map: const {'.': '@expression'}) 82 map: const {'.': '@expression'})
72 class NgRepeat { 83 class NgRepeatDirective extends AbstractNgRepeatDirective {
73 static RegExp _SYNTAX = new RegExp(r'^\s*(.+)\s+in\s+(.*?)\s*(?:track\s+by\s+( .+)\s*)?(\s+lazily\s*)?$'); 84 NgRepeatDirective(BlockHole blockHole,
74 static RegExp _LHS_SYNTAX = new RegExp(r'^(?:([$\w]+)|\(([$\w]+)\s*,\s*([$\w]+ )\))$'); 85 BoundBlockFactory boundBlockFactory,
86 Scope scope,
87 Parser parser,
88 AstParser astParser)
89 : super(blockHole, boundBlockFactory, scope, parser, astParser);
90 }
75 91
76 final ViewPort _viewPort; 92 /**
77 final BoundViewFactory _boundViewFactory; 93 * *EXPERIMENTAL:* This feature is experimental. We reserve the right to change
94 * or delete it.
95 *
96 * [ng-shallow-repeat] is same as [ng-repeat] with some tradeoffs designed for
97 * speed. Use [ng-shallow-repeat] when you expect that your items you are
98 * repeating over do not change during the repeater lifetime.
99 *
100 * The shallow repeater introduces these changes:
101 *
102 * * The repeater only fires if the identity of the list changes or if the list
103 * [length] property changes. This means that the repeater will still see
104 * additions and deletions but not changes to the array.
105 * * The child scopes for each item are created in the lazy mode
106 * (see [Scope.$new]). This means the scopes are effectively taken out of the
107 * digest cycle and will not update on changes to the model.
108 *
109 */
110 @deprecated
111 @NgDirective(
112 children: NgAnnotation.TRANSCLUDE_CHILDREN,
113 selector: '[ng-shallow-repeat]',
114 map: const {'.': '@expression'})
115 //TODO(misko): delete me, since we can no longer do shallow digest.
116 class NgShallowRepeatDirective extends AbstractNgRepeatDirective {
117 NgShallowRepeatDirective(BlockHole blockHole,
118 BoundBlockFactory boundBlockFactory,
119 Scope scope,
120 Parser parser,
121 AstParser astParser)
122 : super(blockHole, boundBlockFactory, scope, parser, astParser)
123 {
124 print('DEPRECATED: [ng-shallow-repeat] use [ng-repeat]');
125 }
126 }
127
128 abstract class AbstractNgRepeatDirective {
129 static RegExp _SYNTAX = new RegExp(r'^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+ (.+)\s*)?(\s+lazily\s*)?$');
130 static RegExp _LHS_SYNTAX = new RegExp(r'^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\ w]+)\))$');
131
132 final BlockHole _blockHole;
133 final BoundBlockFactory _boundBlockFactory;
78 final Scope _scope; 134 final Scope _scope;
79 final Parser _parser; 135 final Parser _parser;
80 final FormatterMap formatters; 136 final AstParser _astParser;
81 137
82 String _expression; 138 String _expression;
83 String _valueIdentifier; 139 String _valueIdentifier;
84 String _keyIdentifier; 140 String _keyIdentifier;
85 String _listExpr; 141 String _listExpr;
86 List<_Row> _rows; 142 Map<dynamic, _Row> _rows = {};
87 Function _generateId = (key, value, index) => value; 143 Function _trackByIdFn = (key, value, index) => value;
88 Watch _watch; 144 Watch _watch = null;
145 Iterable _lastCollection;
89 146
90 NgRepeat(this._viewPort, this._boundViewFactory, this._scope, 147 AbstractNgRepeatDirective(this._blockHole, this._boundBlockFactory,
91 this._parser, this.formatters); 148 this._scope, this._parser, this._astParser);
92 149
93 set expression(value) { 150 set expression(value) {
94 assert(value != null);
95 _expression = value; 151 _expression = value;
96 if (_watch != null) _watch.remove(); 152 if (_watch != null) _watch.remove();
97
98 Match match = _SYNTAX.firstMatch(_expression); 153 Match match = _SYNTAX.firstMatch(_expression);
99 if (match == null) { 154 if (match == null) {
100 throw "[NgErr7] ngRepeat error! Expected expression in form of '_item_ " 155 throw "[NgErr7] ngRepeat error! Expected expression in form of '_item_ "
101 "in _collection_[ track by _id_]' but got '$_expression'."; 156 "in _collection_[ track by _id_]' but got '$_expression'.";
102 } 157 }
103
104 _listExpr = match.group(2); 158 _listExpr = match.group(2);
105 159 var trackByExpr = match.group(4);
106 var trackByExpr = match.group(3);
107 if (trackByExpr != null) { 160 if (trackByExpr != null) {
108 Expression trackBy = _parser(trackByExpr); 161 Expression trackBy = _parser(trackByExpr);
109 _generateId = ((key, value, index) { 162 _trackByIdFn = ((key, value, index) {
110 final context = <String, Object>{} 163 final trackByLocals = <String, Object>{};
164 if (_keyIdentifier != null) trackByLocals[_keyIdentifier] = key;
165 trackByLocals
111 ..[_valueIdentifier] = value 166 ..[_valueIdentifier] = value
112 ..[r'$index'] = index 167 ..[r'$index'] = index
113 ..[r'$id'] = (obj) => obj; 168 ..[r'$id'] = (obj) => obj;
114 if (_keyIdentifier != null) context[_keyIdentifier] = key; 169 return relaxFnArgs(trackBy.eval)(new ScopeLocals(_scope.context, trackBy Locals));
115 return relaxFnArgs(trackBy.eval)(new ScopeLocals(_scope.context,
116 context));
117 }); 170 });
118 } 171 }
119
120 var assignExpr = match.group(1); 172 var assignExpr = match.group(1);
121 match = _LHS_SYNTAX.firstMatch(assignExpr); 173 match = _LHS_SYNTAX.firstMatch(assignExpr);
122 if (match == null) { 174 if (match == null) {
123 throw "[NgErr8] ngRepeat error! '_item_' in '_item_ in _collection_' " 175 throw "[NgErr8] ngRepeat error! '_item_' in '_item_ in _collection_' "
124 "should be an identifier or '(_key_, _value_)' expression, but got " 176 "should be an identifier or '(_key_, _value_)' expression, but got "
125 "'$assignExpr'."; 177 "'$assignExpr'.";
126 } 178 }
127
128 _valueIdentifier = match.group(3); 179 _valueIdentifier = match.group(3);
129 if (_valueIdentifier == null) _valueIdentifier = match.group(1); 180 if (_valueIdentifier == null) _valueIdentifier = match.group(1);
130 _keyIdentifier = match.group(2); 181 _keyIdentifier = match.group(2);
131 182
132 _watch = _scope.watch( 183 _watch = _scope.watch(
133 _listExpr, 184 _astParser(_listExpr, collection: true),
134 (CollectionChangeRecord changes, _) { 185 (CollectionChangeRecord collection, _) {
135 if (changes is! CollectionChangeRecord) return; 186 //TODO(misko): we should take advantage of the CollectionChangeRecord!
136 _onChange(changes); 187 _onCollectionChange(collection == null ? [] : collection.iterable);
137 }, 188 }
138 collection: true,
139 formatters: formatters
140 ); 189 );
141 } 190 }
142 191
143 // Computes and executes DOM changes when the item list changes 192 List<_Row> _computeNewRows(Iterable collection, trackById) {
144 void _onChange(CollectionChangeRecord changes) { 193 final newRowOrder = new List<_Row>(collection.length);
145 final int length = changes.length; 194 // Same as lastBlockMap but it has the current state. It will become the
146 final rows = new List<_Row>(length); 195 // lastBlockMap on the next iteration.
147 final changeFunctions = new List<Function>(length); 196 final newRows = <dynamic, _Row>{};
148 final removedIndexes = <int>[]; 197 // locate existing items
149 final int domLength = _rows == null ? 0 : _rows.length; 198 for (var index = 0; index < newRowOrder.length; index++) {
150 final leftInDom = new List.generate(domLength, (i) => domLength - 1 - i); 199 var value = collection.elementAt(index);
151 var domIndex; 200 trackById = _trackByIdFn(index, value, index);
152 201 if (_rows.containsKey(trackById)) {
153 var addRow = (int index, value, View previousView) { 202 var row = _rows[trackById];
154 var childContext = _updateContext(new PrototypeMap(_scope.context), index, 203 _rows.remove(trackById);
155 length)..[_valueIdentifier] = value; 204 newRows[trackById] = row;
156 var childScope = _scope.createChild(childContext); 205 newRowOrder[index] = row;
157 var view = _boundViewFactory(childScope); 206 } else if (newRows.containsKey(trackById)) {
158 var nodes = view.nodes; 207 // restore lastBlockMap
159 rows[index] = new _Row(_generateId(index, value, index)) 208 newRowOrder.forEach((row) {
160 ..view = view 209 if (row != null && row.startNode != null) _rows[row.id] = row;
161 ..scope = childScope 210 });
162 ..nodes = nodes 211 // This is a duplicate and we need to throw an error
163 ..startNode = nodes.first 212 throw "[NgErr50] ngRepeat error! Duplicates in a repeater are not "
164 ..endNode = nodes.last; 213 "allowed. Use 'track by' expression to specify unique keys. "
165 _viewPort.insert(view, insertAfter: previousView); 214 "Repeater: $_expression, Duplicate key: $trackById";
166 }; 215 } else {
167 216 // new never before seen row
168 // todo(vicb) refactor once GH-774 gets fixed 217 newRowOrder[index] = new _Row(trackById);
169 if (_rows == null) { 218 newRows[trackById] = null;
170 _rows = new List<_Row>(length);
171 for (var i = 0; i < length; i++) {
172 changeFunctions[i] = (index, previousView) {
173 addRow(index, changes.iterable.elementAt(i), previousView);
174 };
175 } 219 }
176 } else {
177 changes.forEachRemoval((CollectionChangeItem removal) {
178 var index = removal.previousIndex;
179 var row = _rows[index];
180 row.scope.destroy();
181 _viewPort.remove(row.view);
182 leftInDom.removeAt(domLength - 1 - index);
183 });
184
185 changes.forEachAddition((CollectionChangeItem addition) {
186 changeFunctions[addition.currentIndex] = (index, previousView) {
187 addRow(index, addition.item, previousView);
188 };
189 });
190
191 changes.forEachMove((CollectionChangeItem move) {
192 var previousIndex = move.previousIndex;
193 var value = move.item;
194 changeFunctions[move.currentIndex] = (index, previousView) {
195 var previousRow = _rows[previousIndex];
196 var childScope = previousRow.scope;
197 var childContext = _updateContext(childScope.context, index, length);
198 if (!identical(childScope.context[_valueIdentifier], value)) {
199 childContext[_valueIdentifier] = value;
200 }
201 rows[index] = _rows[previousIndex];
202 // Only move the DOM node when required
203 if (domIndex < 0 || leftInDom[domIndex] != previousIndex) {
204 _viewPort.move(previousRow.view, moveAfter: previousView);
205 leftInDom.remove(previousIndex);
206 }
207 domIndex--;
208 };
209 });
210 } 220 }
211 221 // remove existing items
212 var previousView = null; 222 _rows.forEach((key, row) {
213 domIndex = leftInDom.length - 1; 223 row.block.remove();
214 for(var targetIndex = 0; targetIndex < length; targetIndex++) { 224 row.scope.destroy();
215 var changeFn = changeFunctions[targetIndex]; 225 });
216 if (changeFn == null) { 226 _rows = newRows;
217 rows[targetIndex] = _rows[targetIndex]; 227 return newRowOrder;
218 domIndex--;
219 // The element has not moved but `$last` and `$middle` might still need
220 // to be updated
221 _updateContext(rows[targetIndex].scope.context, targetIndex, length);
222 } else {
223 changeFn(targetIndex, previousView);
224 }
225 previousView = rows[targetIndex].view;
226 }
227
228 _rows = rows;
229 } 228 }
230 229
231 PrototypeMap _updateContext(PrototypeMap context, int index, int length) { 230 _onCollectionChange(Iterable collection) {
232 var first = (index == 0); 231 dom.Node previousNode = _blockHole.elements[0]; // current position of the n ode
233 var last = (index == length - 1); 232 dom.Node nextNode;
234 return context 233 Scope childScope;
235 ..[r'$index'] = index 234 Map childContext;
236 ..[r'$first'] = first 235 Scope trackById;
237 ..[r'$last'] = last 236 ElementWrapper cursor = _blockHole;
238 ..[r'$middle'] = !(first || last) 237
239 ..[r'$odd'] = index.isOdd 238 List<_Row> newRowOrder = _computeNewRows(collection, trackById);
240 ..[r'$even'] = index.isEven; 239
240 for (var index = 0; index < collection.length; index++) {
241 var value = collection.elementAt(index);
242 _Row row = newRowOrder[index];
243
244 if (row.startNode != null) {
245 // if we have already seen this object, then we need to reuse the
246 // associated scope/element
247 childScope = row.scope;
248 childContext = childScope.context as Map;
249
250 nextNode = previousNode;
251 do {
252 nextNode = nextNode.nextNode;
253 } while(nextNode != null);
254
255 // existing item which got moved
256 if (row.startNode != nextNode) row.block.moveAfter(cursor);
257 previousNode = row.endNode;
258 } else {
259 // new item which we don't know about
260 childScope = _scope.createChild(childContext = new PrototypeMap(_scope.c ontext));
261 }
262
263 if (!identical(childScope.context[_valueIdentifier], value)) {
264 childContext[_valueIdentifier] = value;
265 }
266 var first = (index == 0);
267 var last = (index == collection.length - 1);
268 childContext
269 ..[r'$index'] = index
270 ..[r'$first'] = first
271 ..[r'$last'] = last
272 ..[r'$middle'] = !first && !last
273 ..[r'$odd'] = index & 1 == 1
274 ..[r'$even'] = index & 1 == 0;
275
276 if (row.startNode == null) {
277 var block = _boundBlockFactory(childScope);
278 _rows[row.id] = row
279 ..block = block
280 ..scope = childScope
281 ..elements = block.elements
282 ..startNode = row.elements[0]
283 ..endNode = row.elements[row.elements.length - 1];
284 block.insertAfter(cursor);
285 }
286 cursor = row.block;
287 }
241 } 288 }
242 } 289 }
243
244 class _Row {
245 final id;
246 Scope scope;
247 View view;
248 dom.Element startNode;
249 dom.Element endNode;
250 List<dom.Element> nodes;
251
252 _Row(this.id);
253 }
OLDNEW
« no previous file with comments | « third_party/pkg/angular/lib/directive/ng_pluralize.dart ('k') | third_party/pkg/angular/lib/directive/ng_show_hide.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698