Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

Dirty checker or digest seems buggy #670

Closed
goj opened this issue Mar 3, 2014 · 7 comments
Closed

Dirty checker or digest seems buggy #670

goj opened this issue Mar 3, 2014 · 7 comments

Comments

@goj
Copy link

goj commented Mar 3, 2014

I use AngularDart at commit bb98990.

In my controller I have a final List<CodeDownloadObjects>. When handling the return of RPC adding new item I do ..clear()..addAll(downloadsFromRpcResult) to my list.

CodeDownloadObject class has hashCode and operator== overridden.

With non-empty list to begin with doing that clear()..addAll causes the following output:

Model did not stabilize in 5 digests. Last 3 iterations:
ctrl.codeDownloads: collection: Instance of 'CodeDownloadObject', Instance of 'CodeDownloadObject', Instance of 'CodeDownloadObject'
additions: 
moves: Instance of 'CodeDownloadObject', Instance of 'CodeDownloadObject'
removals: '
     <= null
ctrl.codeDownloads: collection: Instance of 'CodeDownloadObject', Instance of 'CodeDownloadObject', Instance of 'CodeDownloadObject'
additions: 
moves: Instance of 'CodeDownloadObject', Instance of 'CodeDownloadObject'
removals: '
     <= null
ctrl.codeDownloads: collection: Instance of 'CodeDownloadObject', Instance of 'CodeDownloadObject', Instance of 'CodeDownloadObject'
additions: 
moves: Instance of 'CodeDownloadObject', Instance of 'CodeDownloadObject'
removals: '
     <= null

STACKTRACE:
#0      RootScope.digest (package:angular/core/scope.dart:475:11)
#1      RootScope.digest (package:angular/core/scope.dart:480:7)
#2      RootScope.digest (package:angular/core/scope.dart:480:7)
#3      apply (package:angular/core/scope.dart:251:19)
#4      _rootRun (dart:async/zone.dart:710)
#5      _rootRun (dart:async/zone.dart:711)
#6      _rootRun (dart:async/zone.dart:711)
#7      _ZoneDelegate.run (dart:async/zone.dart:440)
#8      NgZone._finishTurn (package:angular/core/zone.dart:95:21)
#9      NgZone._onRunBase (package:angular/core/zone.dart:60:43)
#10     _onRun (package:angular/core/zone.dart:65:15)
#11     _ZoneDelegate.run (dart:async/zone.dart:440)
#12     _CustomizedZone.run (dart:async/zone.dart:650)
#13     _BaseZone.runGuarded (dart:async/zone.dart:561)
#14     _BaseZone.bindCallback.<anonymous closure> (dart:async/zone.dart:586)
#15     _createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:11)
#16     _Timer._Timer.<anonymous closure> (file:///mnt/data/b/build/slave/dartium-lucid64-full-dev/build/src/dart/tools/dom/src/native_DOMImplementation.dart:549)

I tried to dig in a bit and changed the return format of _CollectionChangeRecord.toString()

    return """
collection: ${list.map((ir) => identityHashCode(ir.item)).join(", ")}
additions: ${additions.join(", ")}
moves: ${moves.map((ItemRecord ir) =>
    "${identityHashCode(ir.item)}[${ir.previousIndex} -> ${ir.currentIndex}]").join(", ")}
removalCount: ${removals.length}
removals: ${removals.join(", ")}'
    """;

Here's what I got:

Model did not stabilize in 5 digests. Last 3 iterations:
ctrl.codeDownloads: collection: 772411239, 326349836
additions: 
moves: 772411239[0 -> 0]
removalCount: 0
removals: '
     <= null
ctrl.codeDownloads: collection: 772411239, 326349836
additions: 
moves: 772411239[0 -> 0]
removalCount: 0
removals: '
     <= null
ctrl.codeDownloads: collection: 772411239, 326349836
additions: 
moves: 772411239[0 -> 0]
removalCount: 0
removals: '
     <= null

It looks like exactly the same change (from null?!?) over and over again.

It's no different when my code changes the variable to point to new list instead of ..clear..addAll-ing the old one.

@caitp
Copy link
Contributor

caitp commented Mar 3, 2014

*subscribes to this thread*

@goj
Copy link
Author

goj commented Mar 3, 2014

I forgot to add that I'm iterating over that list with ng-repeat. Items are unique. Using ng-shallow-repeat or track by change nothing.

@chalin
Copy link
Contributor

chalin commented Mar 3, 2014

/sub

@goj
Copy link
Author

goj commented Mar 4, 2014

The fact that objects on my list have operator== and hashCode overridden seem to be very important. Removing those methods or implementing them in terms of identical and identityHashCode removes the problem.

@vicb
Copy link
Contributor

vicb commented Mar 4, 2014

Could you try to setup a minimal test case to reproduce the issue ?

@goj
Copy link
Author

goj commented Mar 7, 2014

addFooBar simulates RPC call returning new list of stuff.
You have to click twice in order to see the problem.

Note that commenting out operator== and hashCode "fixes" the problem.

<button ng-click="ctrl.addFooBar()">Click me twice!</button>
<ul>
  <li ng-repeat="foobar in ctrl.foobars">{{foobar.foo}} - {{foobar.bar}}</li>
</ul>
class FooBar {
  String foo, bar;

  FooBar(this.foo, this.bar);

  bool operator==(other) =>
      other is FooBar && foo == other.foo && bar == other.bar;

  int get hashCode =>
      foo.hashCode ^ bar.hashCode;
}

@NgComponent(
    templateUrl: "html-snippet-above.html",
    publishAs: "ctrl",
    selector: "my-component"
)
class MyComponent {
  int n = 1;
  List<FooBar> tmp = [];
  List<FooBar> foobars = [];

  void addFooBar() {
    tmp.add(new FooBar("foo $n", "bar $n"));
    n++;
    foobars..clear()..addAll(tmp.map((fb) => new FooBar(fb.foo, fb.bar)));
  }
}

@chalin
Copy link
Contributor

chalin commented Mar 15, 2014

That was a nice subtle bug to find and squash!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging a pull request may close this issue.

6 participants