diff --git a/lib/directive/ng_repeat.dart b/lib/directive/ng_repeat.dart index 6597ebf8e..29aa4006b 100644 --- a/lib/directive/ng_repeat.dart +++ b/lib/directive/ng_repeat.dart @@ -83,7 +83,7 @@ class NgRepeat { String _valueIdentifier; String _keyIdentifier; String _listExpr; - List<_Row> _rows; + List _views; Function _generateId = (key, value, index) => value; Watch _watch; @@ -131,109 +131,95 @@ class NgRepeat { _watch = _scope.watch( _listExpr, (changes, _) { - _onChange((changes is CollectionChangeRecord) ? changes : null); + if (changes is CollectionChangeRecord && changes != null) { + _onCollectionChange(changes); + } else if (_views != null) { + _views.forEach(_viewPort.remove); + _views = null; + } }, collection: true, formatters: formatters ); } - // Computes and executes DOM changes when the item list changes - void _onChange(CollectionChangeRecord changes) { - final iterable = (changes == null) ? const [] : changes.iterable; - final int length = (changes == null) ? 0 : changes.length; - final rows = new List<_Row>(length); + void _onCollectionChange(CollectionChangeRecord changes) { + final int length = changes.length; + final views = new List(length); final changeFunctions = new List(length); final removedIndexes = []; - final int domLength = _rows == null ? 0 : _rows.length; + final int domLength = _views == null ? 0 : _views.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); - }; + Function addFn, moveFn, removeFn; - // 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, iterable.elementAt(i), previousView); - }; - } + if (_views == null) { + addFn = changes.forEachItem; + moveFn = (_) {}; + removeFn = (_) {}; } else { - if (changes == null) { - _rows.forEach((row) { - _viewPort.remove(row.view); - }); - leftInDom.clear(); - } else { - changes.forEachRemoval((CollectionChangeItem removal) { - var index = removal.previousIndex; - var row = _rows[index]; - _viewPort.remove(row.view); - leftInDom.removeAt(domLength - 1 - index); - }); - - changes.forEachAddition((CollectionChangeItem addition) { - changeFunctions[addition.currentIndex] = (index, previousView) { - addRow(index, addition.item, previousView); - }; - }); - - 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--; - }; - }); - } + addFn = changes.forEachAddition; + moveFn = changes.forEachMove; + removeFn = changes.forEachRemoval; } + removeFn((CollectionChangeItem removal) { + var index = removal.previousIndex; + _viewPort.remove(_views[index]); + leftInDom.removeAt(domLength - 1 - index); + }); + + addFn((CollectionChangeItem addition) { + changeFunctions[addition.currentIndex] = (index, previousView) { + var childContext = _updateContext(new PrototypeMap(_scope.context), index,length) + ..[_valueIdentifier] = addition.item; + var childScope = _scope.createChild(childContext); + var view = views[index] = _boundViewFactory(childScope); + _viewPort.insert(view, insertAfter: previousView); + }; + }); + + moveFn((CollectionChangeItem move) { + var previousIndex = move.previousIndex; + var value = move.item; + changeFunctions[move.currentIndex] = (index, moveAfter) { + var previousView = _views[previousIndex]; + var childScope = previousView.scope; + var childContext = _updateContext(childScope.context, index, length); + if (!identical(childScope.context[_valueIdentifier], value)) { + childContext[_valueIdentifier] = value; + } + views[index] = _views[previousIndex]; + // Only move the DOM node when required + if (domIndex < 0 || leftInDom[domIndex] != previousIndex) { + _viewPort.move(previousView, moveAfter: moveAfter); + leftInDom.remove(previousIndex); + } + domIndex--; + }; + }); + 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]; + views[targetIndex] = _views[targetIndex]; if (domIndex < 0 || leftInDom[domIndex] != targetIndex) { - _viewPort.move(rows[targetIndex].view, moveAfter: previousView); + _viewPort.move(views[targetIndex], moveAfter: previousView); leftInDom.remove(targetIndex); } domIndex--; // The element has not moved but `$last` and `$middle` might still need to be updated - _updateContext(rows[targetIndex].scope.context, targetIndex, length); + _updateContext(views[targetIndex].scope.context, targetIndex, length); } else { changeFn(targetIndex, previousView); } - previousView = rows[targetIndex].view; + previousView = views[targetIndex]; } - _rows = rows; + _views = views; } PrototypeMap _updateContext(PrototypeMap context, int index, int length) { @@ -248,14 +234,3 @@ class NgRepeat { ..[r'$even'] = index.isEven; } } - -class _Row { - final id; - Scope scope; - View view; - dom.Element startNode; - dom.Element endNode; - List nodes; - - _Row(this.id); -}