Enter Your Email Address to Watch This Lesson

Your link to unlock this lesson will be sent to this email address.

Unlock this lesson and all 833 of the free egghead.io lessons, plus get RxJS content delivered directly to your inbox!



Existing egghead members will not see this. Sign in.

Just one more step!

Check your inbox for an email from us and click link to unlock your lesson.



Read effects from the DOM: click events

6:00 RxJS lesson by

So far we only had effects that write something to the external world, we are not yet reading anything from the external world into our app. This lesson shows how we can change the DOM Driver to return a "DOM Source" representing read effects, such as click events. We will leverage that to create an interactive application.

Get the Code Now
click to level up

egghead.io comment guidelines

Avatar
egghead.io

So far we only had effects that write something to the external world, we are not yet reading anything from the external world into our app. This lesson shows how we can change the DOM Driver to return a "DOM Source" representing read effects, such as click events. We will leverage that to create an interactive application.

Avatar
Steve

This video confused me... I think I see what you are doing but don't really understand the why. Why you would you want to put the DOMSource in the imperative DOMDriver? Based on your discussion in the previous videos I expected all Rx.Observables to belong in the logic/functional main(?).

What is the reason/benefit for putting some Observables in the Driver and others in main()?

In reply to egghead.io
Avatar
Andre

Hi Steve. Every Observable that represents an effect from the external world should be created in a driver. This is important later on if you want to test the main function. If we would create the Observable of clicks in the main function, you could not test the main function without involving real clicks from the user. But if the Observable of clicks is provided as an input to the main function, then it's just a function, you can provide an Observable of fake clicks and test if the main function still works as expected. That's what it means to have "main" pure.

In reply to Steve
Avatar
Steve

OK so if I think of drivers as abstracting away streams of input/output events; then would timer based observables would also benefit from being contained within in one or more "Time" drivers? Then one could test/implement activities from time based events without having to wait for time to pass. In this example you might want the screen to update hourly instead of every second. Am I thinking about this the right why?

In reply to Andre
Avatar
Andre

What you suggested is perfectly valid, but for practical purposes we can consider "Time" to be pure. If you give input streams, your output streams will behave always in the same way, even if you create Time-related Observables internally in the main(). But it's a good practice to put all created Observables out of main() and it may beneficial if you want to abstract away time from minutes and hours as you said.

In reply to Steve
Avatar
Steve

Thanks for helping me understand.

In reply to Andre

Now we have a system with main and drivers, where drivers are responsible for making effects happen. But if you notice carefully, we only have effects that write something to the external world. We're not yet reading anything from the external world into our app. Eventually we need to do that.

For instance, say you need to read from local storage, or you need to receive user events such as clicks. All of these are read effects that the main should be able to take in.

Main is now a function that only returns an output, it still has no input. Notice also that the driver only takes input but it doesn't return anything as an output. That's because the output of main, the syncs, are the inputs to the drivers.

So if we want to make a read effect then we need to return something from the driver. We're going to call this actually DOM source. We need to create that DOM source somehow later. We're going to feed in the DOM source to the main.

Where does the main source and sync come from? These are dataflow network terminologies where source can be thought of input or read effects, and sync is an output or write effect. We need to get that DOM source there so we need to create here somehow. We want the DOM source to contain read effects on the DOM. User events such as clicks and keyboard strokes, they are all read effects on the DOM.

We can actually create an observable here called DOM source, which will have all of the clicks happening on the DOM, so document and click. This will return us an observable which has all the clicks happening on the DOM. Then we still are not feeding this back to the main, because every time we call a driver -- and this is driver function -- we get an output like we're getting here from the DOM driver.

Let me just simplify -- let's not think about the iterator just yet, let's go back to the hard-coded version where we call the DOM driver, like this, giving the DOM sync. Then as we saw we will get the DOM source as an output. Now we just need to feed back the DOM source into main.

But, this is quite tricky because DOM source is still undefined in this line, so this will not work. That's because this problem is sort of analogous to A is F of B, but then G of A.

So there's a cycle going on here where we need B to make A, and we need A to make B, and that's actually where the name Cycle.js comes from. The core of the idea is that there is a loop or small cycle between drivers and the main. So how do we solve that? A is an observable and also B is an observable. If we actually instead of using B, we could use something like B proxy here. Because B proxy is now available for F as an argument.

Then that helps us to make A, and then given A we can make B. Then now that we have B, we can feed back all of the events that happen on B into B proxy. So that's what we're going to try to achieve.

How do we do that in practice, is instead of using DOM source, we're going to use a proxy DOM source which is an RxJS subject, which is basically an observable that has nothing happening, and then you can manually insert events into it.

Now we have the proxy, we can get the syncs, and then we can get the actual DOM source. So we need to do is subscribe to this DOM source, get every click that is happening there, and just feed it back into the DOM source manually. So this onNext just pushes the event into this proxy observable.

Now every time we click on the DOM it will come here as DOM source, and now we can use that. Let's try using it, let's say we can restart this timer every time it's clicked. How would we do that?

Well, we can get the click, and we can flatMap latest, each of these clicks into a new timer like that. We can also start with the fake click. OK, so now every time I click on the DOM it restarts the timer. That's because I was able to get all the clicks happening on the DOM as a source and I was able also to write to the DOM based on these clicks that I make.

We did create some messy code here, but we're going to see how to clean it up on the next lesson.

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