This lesson is for PRO members.

Unlock this lesson NOW!
Already subscribed? sign in

Updating Data with Scan

2:10 RxJS lesson by

You often need to update the data flowing through the stream with custom logic based on what you need to output. This lesson covers how to use scan for collecting and updating the outputs as your stream progresses.

Get the Code Now
click to level up

egghead.io comment guidelines

Avatar
egghead.io

You often need to update the data flowing through the stream with custom logic based on what you need to output. This lesson covers how to use scan for collecting and updating the outputs as your stream progresses.

Avatar
Leesh

Would be worth mentioning you are using Rx.Js 5 Beta! It's not very clear and I spent a good while trying to figure out why switchMapTo wasn't working.

Avatar
Albert

So essentially scan is equal to reduce, right? I don't understand why they always try to make another name in the world of RFP.

Avatar
John

@Albert - no, reduce can only occur once a stream has completed. scan continues to gather output from streams over time and can continually update.

They to have the same api/usage, but the distinction is critical.

In reply to Albert
Avatar
Ahmed

@john_lindquist you've made it abundantly clear that we shouldn't include business logic instead of a .subscribe() method.

So I'm creating a canActivate guard to protect against users going to an invalid route, and I can't help but build logic into the subscribe method. Is there a better way to do this?

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, Router } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import { Subject } from 'rxjs/Subject';

import { Apollo, ApolloQueryObservable } from 'apollo-angular';

import { GQTicketId } from './ticket.model';



@Injectable()
export class TicketRouteActivatorService implements CanActivate {

  public ticket;
  private returnTrue: Subject<Boolean> = new Subject<Boolean>();
  private ticketSubscription: Subscription;

  constructor(private apollo: Apollo, 
              private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot ) {

    this.ticketSubscription = this.apollo.watchQuery({
      query: GQTicketId,
      variables: {
        _id: +route.params['id']
      }
    }).subscribe(({data})=>{
      this.ticket = data['ticket'];

      // this doesn't seem right, atleast based on the guidance from John Linquidst. Should this logic be "Rx'd" some how?
      if(!this.ticket) {
          this.router.navigate(['provision/requests/error/404']);
      } else {
        this.returnTrue.next(true);
      }

    }
    );

      if (this.returnTrue) return true;
  }

  // do we need to unsubscribe?
  ngOnDestroy():void {
      this.ticketSubscription.unsubscribe();
  }

}

<!-- end snippet -->

...to be able to start and then stop our stream, and then start so that it continues it, two, three instead of going back to zero and one.

You'd think, "OK, let's create a variable. Call it count. Set it to zero." Then in my subscribe block, I can just say console.log, count++. I'll hit save.

Then now, when I start, we'll get zero, one, two. I'll stop and then start again. Everything works as expected. Then again, this is wrong, undo all of these. The proper way to gather and collect data in RxJS is to use the scan operator.

Scan works just like reduce in JavaScript arrays. We take a function and initializer. Our initializer is just going to be a count of zero or an object with a count of zero. Then, that comes into this function as an accumulator.

When this function is called, I get this accumulator as this first argument. We'll just go ahead and return a new object with a property called "count," which is accumulator.count plus one. For something more advanced, you'll probably use object assign, but we just have one property for now.

I'll go ahead and save. I'll hit start. We go one, two, three. I'll hit stop, and then hit start again, and re-continue five, six, seven. This now reads as clicking a start button, which switches to a timer that can stop when you click stop.

Then, each time a new interval is pushed or you get a new tick, we start with this value which is this accumulator here.

Then we run this function, which is just going return an object with a count on it. It's going to take that previous accumulator find account property, add one. Then is going to be pushed into my subscribe and logged out...

HEY, QUICK QUESTION!
Joel's Head
Why are we asking?