0

I'm using Angular2-meteor which is using Angular2 Beta 1 (as of right now).

I have a simple component containing:

  • Button to add a document. The new document is displayed with a button to remove it by its _id.
  • There's also a "Remove All" button which loops through the collection.find() removing each document by _id.

It mostly works fine. You can add documents and remove them with their individual remove button. When you "Remove All" it removes them all from the database. The cursor reports a count() of 0. A new collection.find().count() reports 0. But it only removes the first document which is displayed on the template by the *ngFor on the client that initiated the removeAll(). The other documents still show in the browser. When you reload the page, it displays the the correct contents of the database. Other connected clients always display the correct contents of the collection. Only the client that initiates the removeAll() is affected.

The template, a "Remove All" button and an *ngFor displaying the documents

<input type="button" value="Remove All" (click)="removeAll()">
<ul>
  <li *ngFor="#doc of docs">
    <input type="button" value="Remove" (click)="remove(doc._id)">
    point: x={{ doc.x }} y={{ doc.y }} _id={{ doc._id }}
  </li>
</ul>

The component:

@Component({
    selector: 'db-test',
    templateUrl: 'client/db-test/db-test.html',
    directives: [CORE_DIRECTIVES],
})
export class DbTestComponent{
    coll = DbTestCollection;
    docs: Mongo.Cursor<Object>;

    constructor() {
        this.docs = this.coll.find();
    }

    removeAll() {
        this.docs.forEach((d) => {
            this.coll.remove(d._id);
        });
    }

    remove(id) {
        this.coll.remove({ _id: id });
    }

    add(point: Point = {x: Math.random(), y: Math.random()}) {
        this.coll.insert(point);
    }

}
tomahh
  • 13,441
  • 3
  • 49
  • 70
drewlio
  • 767
  • 1
  • 6
  • 10
  • I'm going to clean this up and make the title of the question more fitting now that I understand a bit more about the problem. – drewlio Feb 01 '16 at 17:18

1 Answers1

0

Run also the code in removeAll, remove, and add within the zone

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • I don't know if you saw the edit at the bottom, the `Tracker.autorun()` isn't even necessary as Cursor returned from `this.coll.find()` will be reactive through Meteor. So I'm not sure what you mean by run them within the zone. Can you please elaborate? – drewlio Feb 01 '16 at 17:16
  • I mean you should try one of these in each of the listed methods http://stackoverflow.com/a/34829089/217408. Do the calls to `coll.remove` and `coll.insert` return promises? – Günter Zöchbauer Feb 01 '16 at 17:19
  • The cursor is reactive and by default the `.remove` is in the zone. However, Meteor's `.remove` is async and can take a callback. I think what's happening is the `.forEach` quickly runs over the cursor and change detect happens before the items are removed from the collection and that propagates through Meteor's reactivity. From the 2nd answer in the link you provided, it works to wrap the `.remove` in `window.setTimeout( , 0)`. Does this trigger a change detect immediately following the `.remove`? Is this a race condition? – drewlio Feb 03 '16 at 14:07
  • That's what I intended to communicate with my answer. You already used `zone.run(...)` in the constructor before you edited your question. My answer was about using it also in the other methods. I don't know Meteor or what the calls return or what side effects they have. I can only derive a solution from what your question shows. – Günter Zöchbauer Feb 03 '16 at 14:10
  • for others following this, this is being discussed in https://github.com/angular/angular/issues/6005 – drewlio Feb 03 '16 at 14:46