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

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

Issue 124053002: Adding Angular and dependent packages for testing (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 11 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
(Empty)
1 part of angular.directive;
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
14 /**
15 * The `ngRepeat` directive instantiates a template once per item from a collect ion. Each template
16 * instance gets its own scope, where the given loop variable is set to the curr ent collection item,
17 * and `$index` is set to the item index or key.
18 *
19 * Special properties are exposed on the local scope of each template instance, including:
20 *
21 * <table>
22 * <tr><th> Variable </th><th> Type </th><th> Details <th></tr>
23 * <tr><td> `$index` </td><td>[num] </td><td> iterator offset of the repeated e lement (0..length-1) <td></tr>
24 * <tr><td> `$first` </td><td>[bool]</td><td> true if the repeated element is f irst in the iterator. <td></tr>
25 * <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>
26 * <tr><td> `$last` </td><td>[bool]</td><td> true if the repeated element is l ast in the iterator. <td></tr>
27 * <tr><td> `$even` </td><td>[bool]</td><td> true if the iterator position `$i ndex` is even (otherwise false). <td></tr>
28 * <tr><td> `$odd` </td><td>[bool]</td><td> true if the iterator position `$i ndex` is odd (otherwise false). <td></tr>
29 * </table>
30 *
31 *
32 * [repeat_expression] ngRepeat The expression indicating how to enumerate a col lection. These
33 * formats are currently supported:
34 *
35 * * `variable in expression` – where variable is the user defined loop variab le and `expression`
36 * is a scope expression giving the collection to enumerate.
37 *
38 * For example: `album in artist.albums`.
39 *
40 * * `variable in expression track by tracking_expression` – You can also prov ide an optional
41 * tracking function which can be used to associate the objects in the colle ction with the DOM
42 * elements. If no tracking function is specified the ng-repeat associates e lements by identity
43 * in the collection. It is an error to have more than one tracking function to resolve to the
44 * same key. (This would mean that two distinct objects are mapped to the sa me DOM element,
45 * which is not possible.) Filters should be applied to the expression, bef ore specifying a
46 * tracking expression.
47 *
48 * For example: `item in items` is equivalent to `item in items track by $id (item)'. This
49 * implies that the DOM elements will be associated by item identity in the array.
50 *
51 * For example: `item in items track by $id(item)`. A built in `$id()` funct ion can be used
52 * to assign a unique `$$hashKey` property to each item in the array. This p roperty is then
53 * used as a key to associated DOM elements with the corresponding item in t he array by
54 * identity. Moving the same object in array would move the DOM element in t he same way ian the
55 * DOM.
56 *
57 * For example: `item in items track by item.id` is a typical pattern when t he items come from
58 * the database. In this case the object identity does not matter. Two objec ts are considered
59 * equivalent as long as their `id` property is same.
60 *
61 * For example: `item in items | filter:searchText track by item.id` is a pa ttern that might be
62 * used to apply a filter to items in conjunction with a tracking expression .
63 *
64 *
65 * # Example:
66 *
67 * <ul ng-repeat="item in ['foo', 'bar', 'baz']">
68 * <li>{{$item}}</li>
69 * </ul>
70 */
71
72 @NgDirective(
73 children: NgAnnotation.TRANSCLUDE_CHILDREN,
74 selector: '[ng-repeat]',
75 map: const {'.': '@expression'})
76 class NgRepeatDirective extends AbstractNgRepeatDirective {
77 NgRepeatDirective(BlockHole blockHole,
78 BoundBlockFactory boundBlockFactory,
79 Scope scope): super(blockHole, boundBlockFactory, scope);
80 get _shalow => false;
81 }
82
83 /**
84 * *EXPERIMENTAL:* This feature is experimental. We reserve the right to change or delete it.
85 *
86 * [ng-shallow-repeat] is same as [ng-repeat] with some tradeoffs designed for s peed. Use
87 * [ng-shollow-repeat] when you expect that your items you are repeating over do not change
88 * during the repeater lifetime.
89 *
90 * The shallow repeater introduces these changes:
91 *
92 * * The repeater only fires if the identity of the list changes or if the list [length] property
93 * changes. This means that the repeater will still see additions and deletio ns but not changes
94 * to the array.
95 * * The child scopes for each item are created in the lazy mode (see [Scope.$n ew]). This means
96 * the scopes are effectivly taken out of the digest cycle and will not updat e on changes
97 * to the model.
98 *
99 */
100 @NgDirective(
101 children: NgAnnotation.TRANSCLUDE_CHILDREN,
102 selector: '[ng-shallow-repeat]',
103 map: const {'.': '@expression'})
104 class NgShalowRepeatDirective extends AbstractNgRepeatDirective {
105 NgShalowRepeatDirective(BlockHole blockHole,
106 BoundBlockFactory boundBlockFactory,
107 Scope scope): super(blockHole, boundBlockFactory, scop e);
108 get _shalow => true;
109 }
110
111 abstract class AbstractNgRepeatDirective {
112 static RegExp _SYNTAX = new RegExp(r'^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+ (.+)\s*)?(\s+lazily\s*)?$');
113 static RegExp _LHS_SYNTAX = new RegExp(r'^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\ w]+)\))$');
114
115 BlockHole _blockHole;
116 BoundBlockFactory _boundBlockFactory;
117 Scope _scope;
118
119 String _expression;
120 String _valueIdentifier;
121 String _keyIdentifier;
122 String _listExpr;
123 Map<Object, _Row> _rows = new Map<dynamic, _Row>();
124 Function _trackByIdFn = (key, value, index) => value;
125 Function _removeWatch = () => null;
126 Iterable _lastCollection;
127
128 AbstractNgRepeatDirective(BlockHole this._blockHole,
129 BoundBlockFactory this._boundBlockFactory,
130 Scope this._scope);
131
132 get _shalow;
133
134 set expression(value) {
135 _expression = value;
136 _removeWatch();
137 Match match = _SYNTAX.firstMatch(_expression);
138 if (match == null) {
139 throw "[NgErr7] ngRepeat error! Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '$_expression'.";
140 }
141 _listExpr = match.group(2);
142 var assignExpr = match.group(1);
143 match = _LHS_SYNTAX.firstMatch(assignExpr);
144 if (match == null) {
145 throw "[NgErr8] ngRepeat error! '_item_' in '_item_ in _collection_' shoul d be an identifier or '(_key_, _value_)' expression, but got '$assignExpr'.";
146 }
147 _valueIdentifier = match.group(3);
148 if (_valueIdentifier == null) _valueIdentifier = match.group(1);
149 _keyIdentifier = match.group(2);
150
151 _removeWatch = _scope.$watchCollection(_listExpr, _onCollectionChange, value , _shalow);
152 }
153
154 List<_Row> _computeNewRows(Iterable collection, trackById) {
155 List<_Row> newRowOrder = [];
156 // Same as lastBlockMap but it has the current state. It will become the
157 // lastBlockMap on the next iteration.
158 Map<dynamic, _Row> newRows = new Map<dynamic, _Row>();
159 var arrayLength = collection.length;
160 // locate existing items
161 var length = newRowOrder.length = collection.length;
162 for (var index = 0; index < length; index++) {
163 var value = collection.elementAt(index);
164 trackById = _trackByIdFn(index, value, index);
165 if (_rows.containsKey(trackById)) {
166 var row = _rows[trackById];
167 _rows.remove(trackById);
168 newRows[trackById] = row;
169 newRowOrder[index] = row;
170 } else if (newRows.containsKey(trackById)) {
171 // restore lastBlockMap
172 newRowOrder.forEach((row) {
173 if (row != null && row.startNode != null) {
174 _rows[row.id] = row;
175 }
176 });
177 // This is a duplicate and we need to throw an error
178 throw "[NgErr50] ngRepeat error! Duplicates in a repeater are not allowe d. Use 'track by' expression to specify unique keys. Repeater: $_expression, Dup licate key: $trackById";
179 } else {
180 // new never before seen row
181 newRowOrder[index] = new _Row(trackById);
182 newRows[trackById] = null;
183 }
184 }
185 // remove existing items
186 _rows.forEach((key, row){
187 row.block.remove();
188 row.scope.$destroy();
189 });
190 _rows = newRows;
191 return newRowOrder;
192 }
193
194 _onCollectionChange(Iterable collection) {
195 var previousNode = _blockHole.elements[0], // current position of the no de
196 nextNode,
197 childScope,
198 trackById,
199 cursor = _blockHole,
200 arrayChange = _lastCollection != collection;
201
202 if (arrayChange) { _lastCollection = collection; }
203 if (collection is! Iterable) {
204 collection = [];
205 }
206
207 List<_Row> newRowOrder = _computeNewRows(collection, trackById);
208
209 for (var index = 0, length = collection.length; index < length; index++) {
210 var key = index;
211 var value = collection.elementAt(index);
212 _Row row = newRowOrder[index];
213
214 if (row.startNode != null) {
215 // if we have already seen this object, then we need to reuse the
216 // associated scope/element
217 childScope = row.scope;
218
219 nextNode = previousNode;
220 do {
221 nextNode = nextNode.nextNode;
222 } while(nextNode != null);
223
224 if (row.startNode == nextNode) {
225 // do nothing
226 } else {
227 // existing item which got moved
228 row.block.moveAfter(cursor);
229 }
230 previousNode = row.endNode;
231 } else {
232 // new item which we don't know about
233 childScope = _scope.$new(lazy:_shalow);
234 }
235
236 if (!identical(childScope[_valueIdentifier], value)) {
237 childScope[_valueIdentifier] = value;
238 childScope.$dirty();
239 }
240 childScope[r'$index'] = index;
241 childScope[r'$first'] = (index == 0);
242 childScope[r'$last'] = (index == (collection.length - 1));
243 childScope[r'$middle'] = !(childScope.$first || childScope.$last);
244 childScope[r'$odd'] = index & 1 == 1;
245 childScope[r'$even'] = index & 1 == 0;
246 if (arrayChange && _shalow) {
247 childScope.$dirty();
248 }
249
250 if (row.startNode == null) {
251 _rows[row.id] = row;
252 var block = _boundBlockFactory(childScope);
253 row..block = block
254 ..scope = childScope
255 ..elements = block.elements
256 ..startNode = row.elements[0]
257 ..endNode = row.elements[row.elements.length - 1];
258 block.insertAfter(cursor);
259 }
260 cursor = row.block;
261 }
262 }
263 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698