Map Streams to Values to Affect State

John Lindquist
InstructorJohn Lindquist

Share this video with your friends

Send Tweet
Published 6 years ago
Updated 3 years ago

While you have multiple streams flowing into your scan operator, you'll need to map each stream to the specific values you need to update your state the way that you want. This lesson covers using the map operator to determine what the click and interval should do when the state is updated.

[00:00] Now to change the behavior so that when this pushes through, it only updates one second, and when this pushes through, it'll update an entire hour.

[00:09] What we have to do is use something like map two, and we're going to send along a string of second. I'll say map two and we'll send along a string of hour. I need to go ahead and import map two.

[00:28] Now, what comes through in this current is either second or hour, because those are the things being pushed through in this merged observable, either hour or second will come through into my scan function.

[00:41] I can go ahead and check, and see if the current is second. If it is, we'll just go ahead and update the seconds. Or if the current is equal to hour, let's go ahead and copy this, paste it, and change seconds to hours and change seconds to hours.

[01:08] Now when I hit save and I refresh, you can see the time keeps ticking along, one second per time, because second is coming through and it's hitting this if block. But if I click update, it's updating the hours, so I can move the time much faster, because it's sending the word "hour" through. It hits this if block and it sets the hours on the date to plus one and then returns the date.

Noah Rawlins
Noah Rawlins
~ 6 years ago

The code still works, so maybe it's just an error with the typing definitions for rxjs, but the typescript compiler gives an error about using a Date object in startsWith()

Argument of type 'Date' is not assignable to parameter of type 'string | Scheduler'

This only pops up when you add mapTo('second') and mapTo('hour') to the two merged observables. Anyway, it doesn't seem to stop it from working, but I'm not sure if that's just luck.

Jey Choi
Jey Choi
~ 6 years ago

I have the exact same problem. Can anyone suggest a solution of this lecture?

In my opinion, the probable cause is like below.

the very first stream for 'curr' is Date type, while 'curr' is expecting string. (in type script, compiler assumes 'curr' has to be the string as the source stream says.)

it seems like very non-deterministic in functional programming.

that is why the compiler argues about it.

Panuruj
Panuruj
~ 6 years ago

Here is the code that works for me. You can use the optional parameter on scan() to initialize the value.

this.clock = Observable.merge(
  this.click$.mapTo('hour'),
  Observable.interval(1000).mapTo('second')
)
.scan((acc: Date, curr: string): Date => {
  const date = new Date(acc.getTime());
  if (curr === 'hour') {
    date.setHours(date.getHours() + 1);
  }
  if (curr === 'second') {
    date.setSeconds(date.getSeconds() + 1);
  }
  return date;
}, new Date());  // here is the optional initial value instead of using startWith()

}

Nikolaj
Nikolaj
~ 6 years ago

But that is not the exact same behavior, because now the initial value only appears after a second (set the interval to 10000 to see it better). I just upgraded to Pro and am a little bit disappointed, that I get an example that throws errors in just my first course.

Edit: I came up with this: .startWith(new Date() || '') ... but that feels very hackish too

kyutae kang
kyutae kang
~ 5 years ago

i have got same error, so i changed this example source like this

export class AppComponent { clock$: Subject<any> = new Subject();

clock;

constructor(){ this.clock = Observable.merge( this.clock$.mapTo('hour'), Observable.interval(1000).mapTo('second') ) .startWith( new Date().toString() ) .scan((acc, curr) => { let accDate = new Date(acc); const date = new Date(accDate.getTime());

  if(curr === 'hour') {
    date.setHours(date.getHours() + 1);
  }
  if (curr === 'second') {
    date.setSeconds(date.getSeconds() + 1);
    
  }
  return date;
})

} }

startWith operator Arguments is only string or Scheduler. so i change startWith new Date to string and in scan function that string change to Date Type.. run well ..

Steven Nance
Steven Nance
~ 5 years ago

Changing

      .startWith(new Date())

to

      .startWith<Date | string>(new Date())

works for me.

technokon
technokon
~ 5 years ago
<pre class="highlight plaintext> <code>"Observable.merge any"<code> works for me </pre>
monir33
monir33
~ 4 years ago

this one works for me .startWith( <date|any> new Date())