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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: third_party/pkg/angular/lib/directive/ng_repeat.dart
diff --git a/third_party/pkg/angular/lib/directive/ng_repeat.dart b/third_party/pkg/angular/lib/directive/ng_repeat.dart
index 5f4515df6cee7afda78bff8e71393d3a57860374..603b413c1f6a89e02da5c4e8173971523ebe5734 100644
--- a/third_party/pkg/angular/lib/directive/ng_repeat.dart
+++ b/third_party/pkg/angular/lib/directive/ng_repeat.dart
@@ -1,5 +1,16 @@
part of angular.directive;
+class _Row {
+ var id;
+ Scope scope;
+ Block block;
+ dom.Element startNode;
+ dom.Element endNode;
+ List<dom.Element> elements;
+
+ _Row(this.id);
+}
+
/**
* The `ngRepeat` directive instantiates a template once per item from a
* collection. Each template instance gets its own scope, where the given loop
@@ -9,15 +20,15 @@ part of angular.directive;
* Special properties are exposed on the local scope of each template instance,
* including:
*
- * * `$index` ([:num:]) the iterator offset of the repeated element
- * (0..length-1)
- * * `$first` ([:bool:]) whether the repeated element is first in the
- * iterator.
- * * `$middle` ([:bool:]) whether the repeated element is between the first
- * and last in the iterator.
- * * `$last` ([:bool:]) whether the repeated element is last in the iterator.
- * * `$even` ([:bool:]) whether the iterator position `$index` is even.
- * * `$odd` ([:bool:]) whether the iterator position `$index` is odd.
+ * <table>
+ * <tr><th> Variable </th><th> Type </th><th> Details <th></tr>
+ * <tr><td> `$index` </td><td>[num] </td><td> iterator offset of the repeated element (0..length-1) <td></tr>
+ * <tr><td> `$first` </td><td>[bool]</td><td> true if the repeated element is first in the iterator. <td></tr>
+ * <tr><td> `$middle` </td><td>[bool]</td><td> true if the repeated element is between the first and last in the iterator. <td></tr>
+ * <tr><td> `$last` </td><td>[bool]</td><td> true if the repeated element is last in the iterator. <td></tr>
+ * <tr><td> `$even` </td><td>[bool]</td><td> true if the iterator position `$index` is even (otherwise false). <td></tr>
+ * <tr><td> `$odd` </td><td>[bool]</td><td> true if the iterator position `$index` is odd (otherwise false). <td></tr>
+ * </table>
*
*
* [repeat_expression] ngRepeat The expression indicating how to enumerate a
@@ -46,7 +57,7 @@ part of angular.directive;
* function can be used to assign a unique `$$hashKey` property to each item
* in the array. This property is then used as a key to associated DOM
* elements with the corresponding item in the array by identity. Moving the
- * same object in array would move the DOM element in the same way in the
+ * same object in array would move the DOM element in the same way ian the
* DOM.
*
* For example: `item in items track by item.id` is a typical pattern when
@@ -55,7 +66,7 @@ part of angular.directive;
* property is same.
*
* For example: `item in items | filter:searchText track by item.id` is a
- * pattern that might be used to apply a formatter to items in conjunction with
+ * pattern that might be used to apply a filter to items in conjunction with
* a tracking expression.
*
* # Example:
@@ -65,58 +76,99 @@ part of angular.directive;
* </ul>
*/
-@Decorator(
- children: Directive.TRANSCLUDE_CHILDREN,
+@NgDirective(
+ children: NgAnnotation.TRANSCLUDE_CHILDREN,
selector: '[ng-repeat]',
map: const {'.': '@expression'})
-class NgRepeat {
- static RegExp _SYNTAX = new RegExp(r'^\s*(.+)\s+in\s+(.*?)\s*(?:track\s+by\s+(.+)\s*)?(\s+lazily\s*)?$');
- static RegExp _LHS_SYNTAX = new RegExp(r'^(?:([$\w]+)|\(([$\w]+)\s*,\s*([$\w]+)\))$');
+class NgRepeatDirective extends AbstractNgRepeatDirective {
+ NgRepeatDirective(BlockHole blockHole,
+ BoundBlockFactory boundBlockFactory,
+ Scope scope,
+ Parser parser,
+ AstParser astParser)
+ : super(blockHole, boundBlockFactory, scope, parser, astParser);
+}
+
+/**
+ * *EXPERIMENTAL:* This feature is experimental. We reserve the right to change
+ * or delete it.
+ *
+ * [ng-shallow-repeat] is same as [ng-repeat] with some tradeoffs designed for
+ * speed. Use [ng-shallow-repeat] when you expect that your items you are
+ * repeating over do not change during the repeater lifetime.
+ *
+ * The shallow repeater introduces these changes:
+ *
+ * * The repeater only fires if the identity of the list changes or if the list
+ * [length] property changes. This means that the repeater will still see
+ * additions and deletions but not changes to the array.
+ * * The child scopes for each item are created in the lazy mode
+ * (see [Scope.$new]). This means the scopes are effectively taken out of the
+ * digest cycle and will not update on changes to the model.
+ *
+ */
+@deprecated
+@NgDirective(
+ children: NgAnnotation.TRANSCLUDE_CHILDREN,
+ selector: '[ng-shallow-repeat]',
+ map: const {'.': '@expression'})
+//TODO(misko): delete me, since we can no longer do shallow digest.
+class NgShallowRepeatDirective extends AbstractNgRepeatDirective {
+ NgShallowRepeatDirective(BlockHole blockHole,
+ BoundBlockFactory boundBlockFactory,
+ Scope scope,
+ Parser parser,
+ AstParser astParser)
+ : super(blockHole, boundBlockFactory, scope, parser, astParser)
+ {
+ print('DEPRECATED: [ng-shallow-repeat] use [ng-repeat]');
+ }
+}
+
+abstract class AbstractNgRepeatDirective {
+ static RegExp _SYNTAX = new RegExp(r'^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?(\s+lazily\s*)?$');
+ static RegExp _LHS_SYNTAX = new RegExp(r'^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$');
- final ViewPort _viewPort;
- final BoundViewFactory _boundViewFactory;
+ final BlockHole _blockHole;
+ final BoundBlockFactory _boundBlockFactory;
final Scope _scope;
final Parser _parser;
- final FormatterMap formatters;
+ final AstParser _astParser;
String _expression;
String _valueIdentifier;
String _keyIdentifier;
String _listExpr;
- List<_Row> _rows;
- Function _generateId = (key, value, index) => value;
- Watch _watch;
+ Map<dynamic, _Row> _rows = {};
+ Function _trackByIdFn = (key, value, index) => value;
+ Watch _watch = null;
+ Iterable _lastCollection;
- NgRepeat(this._viewPort, this._boundViewFactory, this._scope,
- this._parser, this.formatters);
+ AbstractNgRepeatDirective(this._blockHole, this._boundBlockFactory,
+ this._scope, this._parser, this._astParser);
set expression(value) {
- assert(value != null);
_expression = value;
if (_watch != null) _watch.remove();
-
Match match = _SYNTAX.firstMatch(_expression);
if (match == null) {
throw "[NgErr7] ngRepeat error! Expected expression in form of '_item_ "
"in _collection_[ track by _id_]' but got '$_expression'.";
}
-
_listExpr = match.group(2);
-
- var trackByExpr = match.group(3);
+ var trackByExpr = match.group(4);
if (trackByExpr != null) {
Expression trackBy = _parser(trackByExpr);
- _generateId = ((key, value, index) {
- final context = <String, Object>{}
+ _trackByIdFn = ((key, value, index) {
+ final trackByLocals = <String, Object>{};
+ if (_keyIdentifier != null) trackByLocals[_keyIdentifier] = key;
+ trackByLocals
..[_valueIdentifier] = value
..[r'$index'] = index
..[r'$id'] = (obj) => obj;
- if (_keyIdentifier != null) context[_keyIdentifier] = key;
- return relaxFnArgs(trackBy.eval)(new ScopeLocals(_scope.context,
- context));
+ return relaxFnArgs(trackBy.eval)(new ScopeLocals(_scope.context, trackByLocals));
});
}
-
var assignExpr = match.group(1);
match = _LHS_SYNTAX.firstMatch(assignExpr);
if (match == null) {
@@ -124,130 +176,114 @@ class NgRepeat {
"should be an identifier or '(_key_, _value_)' expression, but got "
"'$assignExpr'.";
}
-
_valueIdentifier = match.group(3);
if (_valueIdentifier == null) _valueIdentifier = match.group(1);
_keyIdentifier = match.group(2);
_watch = _scope.watch(
- _listExpr,
- (CollectionChangeRecord changes, _) {
- if (changes is! CollectionChangeRecord) return;
- _onChange(changes);
- },
- collection: true,
- formatters: formatters
+ _astParser(_listExpr, collection: true),
+ (CollectionChangeRecord collection, _) {
+ //TODO(misko): we should take advantage of the CollectionChangeRecord!
+ _onCollectionChange(collection == null ? [] : collection.iterable);
+ }
);
}
- // Computes and executes DOM changes when the item list changes
- void _onChange(CollectionChangeRecord changes) {
- final int length = changes.length;
- final rows = new List<_Row>(length);
- final changeFunctions = new List<Function>(length);
- final removedIndexes = <int>[];
- final int domLength = _rows == null ? 0 : _rows.length;
- final leftInDom = new List.generate(domLength, (i) => domLength - 1 - i);
- var domIndex;
-
- var addRow = (int index, value, View previousView) {
- var childContext = _updateContext(new PrototypeMap(_scope.context), index,
- length)..[_valueIdentifier] = value;
- var childScope = _scope.createChild(childContext);
- var view = _boundViewFactory(childScope);
- var nodes = view.nodes;
- rows[index] = new _Row(_generateId(index, value, index))
- ..view = view
- ..scope = childScope
- ..nodes = nodes
- ..startNode = nodes.first
- ..endNode = nodes.last;
- _viewPort.insert(view, insertAfter: previousView);
- };
-
- // todo(vicb) refactor once GH-774 gets fixed
- if (_rows == null) {
- _rows = new List<_Row>(length);
- for (var i = 0; i < length; i++) {
- changeFunctions[i] = (index, previousView) {
- addRow(index, changes.iterable.elementAt(i), previousView);
- };
+ List<_Row> _computeNewRows(Iterable collection, trackById) {
+ final newRowOrder = new List<_Row>(collection.length);
+ // Same as lastBlockMap but it has the current state. It will become the
+ // lastBlockMap on the next iteration.
+ final newRows = <dynamic, _Row>{};
+ // locate existing items
+ for (var index = 0; index < newRowOrder.length; index++) {
+ var value = collection.elementAt(index);
+ trackById = _trackByIdFn(index, value, index);
+ if (_rows.containsKey(trackById)) {
+ var row = _rows[trackById];
+ _rows.remove(trackById);
+ newRows[trackById] = row;
+ newRowOrder[index] = row;
+ } else if (newRows.containsKey(trackById)) {
+ // restore lastBlockMap
+ newRowOrder.forEach((row) {
+ if (row != null && row.startNode != null) _rows[row.id] = row;
+ });
+ // This is a duplicate and we need to throw an error
+ throw "[NgErr50] ngRepeat error! Duplicates in a repeater are not "
+ "allowed. Use 'track by' expression to specify unique keys. "
+ "Repeater: $_expression, Duplicate key: $trackById";
+ } else {
+ // new never before seen row
+ newRowOrder[index] = new _Row(trackById);
+ newRows[trackById] = null;
}
- } else {
- changes.forEachRemoval((CollectionChangeItem removal) {
- var index = removal.previousIndex;
- var row = _rows[index];
- row.scope.destroy();
- _viewPort.remove(row.view);
- leftInDom.removeAt(domLength - 1 - index);
- });
+ }
+ // remove existing items
+ _rows.forEach((key, row) {
+ row.block.remove();
+ row.scope.destroy();
+ });
+ _rows = newRows;
+ return newRowOrder;
+ }
- changes.forEachAddition((CollectionChangeItem addition) {
- changeFunctions[addition.currentIndex] = (index, previousView) {
- addRow(index, addition.item, previousView);
- };
- });
+ _onCollectionChange(Iterable collection) {
+ dom.Node previousNode = _blockHole.elements[0]; // current position of the node
+ dom.Node nextNode;
+ Scope childScope;
+ Map childContext;
+ Scope trackById;
+ ElementWrapper cursor = _blockHole;
- changes.forEachMove((CollectionChangeItem move) {
- var previousIndex = move.previousIndex;
- var value = move.item;
- changeFunctions[move.currentIndex] = (index, previousView) {
- var previousRow = _rows[previousIndex];
- var childScope = previousRow.scope;
- var childContext = _updateContext(childScope.context, index, length);
- if (!identical(childScope.context[_valueIdentifier], value)) {
- childContext[_valueIdentifier] = value;
- }
- rows[index] = _rows[previousIndex];
- // Only move the DOM node when required
- if (domIndex < 0 || leftInDom[domIndex] != previousIndex) {
- _viewPort.move(previousRow.view, moveAfter: previousView);
- leftInDom.remove(previousIndex);
- }
- domIndex--;
- };
- });
- }
+ List<_Row> newRowOrder = _computeNewRows(collection, trackById);
+
+ for (var index = 0; index < collection.length; index++) {
+ var value = collection.elementAt(index);
+ _Row row = newRowOrder[index];
- var previousView = null;
- domIndex = leftInDom.length - 1;
- for(var targetIndex = 0; targetIndex < length; targetIndex++) {
- var changeFn = changeFunctions[targetIndex];
- if (changeFn == null) {
- rows[targetIndex] = _rows[targetIndex];
- domIndex--;
- // The element has not moved but `$last` and `$middle` might still need
- // to be updated
- _updateContext(rows[targetIndex].scope.context, targetIndex, length);
+ if (row.startNode != null) {
+ // if we have already seen this object, then we need to reuse the
+ // associated scope/element
+ childScope = row.scope;
+ childContext = childScope.context as Map;
+
+ nextNode = previousNode;
+ do {
+ nextNode = nextNode.nextNode;
+ } while(nextNode != null);
+
+ // existing item which got moved
+ if (row.startNode != nextNode) row.block.moveAfter(cursor);
+ previousNode = row.endNode;
} else {
- changeFn(targetIndex, previousView);
+ // new item which we don't know about
+ childScope = _scope.createChild(childContext = new PrototypeMap(_scope.context));
}
- previousView = rows[targetIndex].view;
- }
- _rows = rows;
- }
+ if (!identical(childScope.context[_valueIdentifier], value)) {
+ childContext[_valueIdentifier] = value;
+ }
+ var first = (index == 0);
+ var last = (index == collection.length - 1);
+ childContext
+ ..[r'$index'] = index
+ ..[r'$first'] = first
+ ..[r'$last'] = last
+ ..[r'$middle'] = !first && !last
+ ..[r'$odd'] = index & 1 == 1
+ ..[r'$even'] = index & 1 == 0;
- PrototypeMap _updateContext(PrototypeMap context, int index, int length) {
- var first = (index == 0);
- var last = (index == length - 1);
- return context
- ..[r'$index'] = index
- ..[r'$first'] = first
- ..[r'$last'] = last
- ..[r'$middle'] = !(first || last)
- ..[r'$odd'] = index.isOdd
- ..[r'$even'] = index.isEven;
+ if (row.startNode == null) {
+ var block = _boundBlockFactory(childScope);
+ _rows[row.id] = row
+ ..block = block
+ ..scope = childScope
+ ..elements = block.elements
+ ..startNode = row.elements[0]
+ ..endNode = row.elements[row.elements.length - 1];
+ block.insertAfter(cursor);
+ }
+ cursor = row.block;
+ }
}
}
-
-class _Row {
- final id;
- Scope scope;
- View view;
- dom.Element startNode;
- dom.Element endNode;
- List<dom.Element> nodes;
-
- _Row(this.id);
-}
« 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