diff --git a/lib/directive/ng_repeat.dart b/lib/directive/ng_repeat.dart index 99e7d93e9..f4b480b7c 100644 --- a/lib/directive/ng_repeat.dart +++ b/lib/directive/ng_repeat.dart @@ -132,8 +132,7 @@ class NgRepeat { _watch = _scope.watch( _listExpr, (CollectionChangeRecord changes, _) { - if (changes is! CollectionChangeRecord) return; - _onChange(changes); + _onChange((changes is CollectionChangeRecord) ? changes : null); }, collection: true, formatters: formatters @@ -142,7 +141,8 @@ class NgRepeat { // Computes and executes DOM changes when the item list changes void _onChange(CollectionChangeRecord changes) { - final int length = changes.length; + final iterable = (changes == null) ? const [] : changes.iterable; + final int length = (changes == null) ? 0 : changes.length; final rows = new List<_Row>(length); final changeFunctions = new List(length); final removedIndexes = []; @@ -170,43 +170,51 @@ class NgRepeat { _rows = new List<_Row>(length); for (var i = 0; i < length; i++) { changeFunctions[i] = (index, previousView) { - addRow(index, changes.iterable.elementAt(i), previousView); + addRow(index, iterable.elementAt(i), previousView); }; } } 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); - }); + if (changes == null) { + _rows.forEach((row) { + row.scope.destroy(); + _viewPort.remove(row.view); + }); + leftInDom.clear(); + } 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); + }); - changes.forEachAddition((CollectionChangeItem addition) { - changeFunctions[addition.currentIndex] = (index, previousView) { - addRow(index, addition.item, previousView); - }; - }); + 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--; - }; - }); + 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--; + }; + }); + } } var previousView = null; diff --git a/test/directive/ng_repeat_spec.dart b/test/directive/ng_repeat_spec.dart index 8d43d1b2d..3dde712a0 100644 --- a/test/directive/ng_repeat_spec.dart +++ b/test/directive/ng_repeat_spec.dart @@ -107,6 +107,33 @@ main() { }); + it('should gracefully handle ref changing to null and back', () { + scope.context['items'] = ['odin', 'dva',]; + element = compile( + '
' + '
    ' + '
  • {{item}};
  • ' + '
' + '
'); + scope.apply(); + expect(element.querySelectorAll('ul').length).toEqual(1); + expect(element.querySelectorAll('li').length).toEqual(2); + expect(element.text).toEqual('odin;dva;'); + + scope.context['items'] = null; + scope.apply(); + expect(element.querySelectorAll('ul').length).toEqual(1); + expect(element.querySelectorAll('li').length).toEqual(0); + expect(element.text).toEqual(''); + + scope.context['items'] = ['odin', 'dva', 'tri']; + scope.apply(); + expect(element.querySelectorAll('ul').length).toEqual(1); + expect(element.querySelectorAll('li').length).toEqual(3); + expect(element.text).toEqual('odin;dva;tri;'); + }); + + it('should support formatters', () { element = compile( '
{{item}}
');