6

I want to load all the items on start without showing any message, but once after loaded. I want to capture any new row in subscriber and show it to the desktop notification.

The problem is, I'm not sure how to check if all the previous items are loaded and if the row is new item or is it from previous existing item.

this.items = this.af.database.list('notifications/'+this.uid+'/');
this.items.subscribe(list => {
    list.forEach(row => {
        // doing something here...
    });

    // once all the rows are finished loading, then any new row, show desktop notification message
});
Basit
  • 16,316
  • 31
  • 93
  • 154
  • AngularFire2 is doing this internally and is emitting an array of current 'rows' from the observable. You would have to examine the array yourself, to determine what's new. You could use the underlying Firebase API, instead. If you use `on("child_added", ...)` and `once("value", ...)`, the `child_added` events you receive before the `value` event will represent the initial 'rows' and subsequent `child_added` events will contain new 'rows'. – cartant Oct 02 '16 at 01:52
  • do you have example of this with angularfire code? I cant find it on there documentation on how to use on("child_added"...) – Basit Oct 02 '16 at 02:03
  • It's in the underlying [Firebase API](https://firebase.google.com/docs/reference/js/firebase.database.Reference). You can see how it's used in the [AngularFire2 source](https://github.com/angular/angularfire2/blob/2.0.0-beta.5/src/database/firebase_list_factory.ts#L102-L156) for the list observable. – cartant Oct 02 '16 at 02:09
  • `this.notifications.items._ref.on('child_added', (child: any) => { console.log(child); });` has same effect as `subscribe` method – Basit Oct 02 '16 at 18:14

3 Answers3

0

I have user lodash for the minimal code.

// this varible holds the initial loaded keys
let  loadedKeys = [];
this.items = this.af.database.list('notifications/'+this.uid+'/');
this.items.subscribe((list)=>{
  // we skip it while initial load
  if(!_.isEmpty(loadedKeys)){
    // different is the new keys
    let newKeys = _.difference(_.map(list, "$key"), loadedKeys);
    if(!_.isEmpty(newKeys)){
      // ... notification code here 
    }
  }
  loadedKeys = _.map(list, "$key");
});
nagabandaru
  • 605
  • 7
  • 20
0

The behave you are looking for is the default Subject approach in RxJS. Check this reactiveX url to follow the marble diagram of Publish Subject (the equivalent for Subject in RxJS).

So you have two easy options:

1) manually index witch rows you want to display like @bash replied

2) create a Rx.Subject() and assign only the newest's rows to it. Then you subscribe to this subject in your app workflow.

The advantage of method 2 is when a new .subscribe occur, it will not retrieve previous data.


Edit: I wrote this codepen as a guide to implement your custom RxJS Subject. Hope it helps.

raphaelbs
  • 440
  • 4
  • 13
0

Assuming your rows have something unique to match with previous rows you can do the following:

// A Row item has a unique identifier id
interface Row {
  id: number;
}

this.rows: Row[];
this.items$ = this.af.database.list(`notifications/${this.uid}/`).pipe(
  tap(list => {
    // if rows is not array, first time...
    if(!Array.isArray(this.rows)) {
      // first time nothing to do
      return;
    }
    // returns true if some item from list is not found in this.rows
    const foundNewRow = list.some(item => {
      return ! this.rows.find(row => row.id === item.id);
    });
    if (foundNewRow) {
      // call method to show desktop message here
    }
  }
);

I used a pipe and a tap operator (that you will have to import). If you subscribe to this.items$ the tap operator will do the work:

this.items$.subscribe((items => this.rows = items));

If you do not want to set this.rows when normally subscribing than you can also do this in the tap operator. But that would assume you only use it for checking difference between existing and new items.

Wilt
  • 41,477
  • 12
  • 152
  • 203