Using the power of RxJS and Angular components to <blink>
<blink> element must be the most hated HTML tag of all time. Never part of any standard, it had its moment back in the 90’s and then they took it away from us. How inconsiderate was that? They tried to amend this monumental mistake by adding
text-decoration: blink to the CSS standard but they were all like “it’s cool if you don’t actually implement that in your browsers, no biggie”.
I’m here to fix this injustice once and for all. Let’s restore
<blink> to its former glory!
We should begin by defining how our new component should behave. Since
<blink> was never part of any web standard we don’t have much to go on but luckily its Wikipedia article gives us a glimpse of hope:
In versions of Mozilla Firefox that support the tag, the text alternates between being visible for three quarters of a second and being invisible for one quarter of a second.
Ok, so that’s what we’ll do. Visible for 750 ms and then invisible for 250 ms, for a total period of 1 second. Rinse and repeat.
A sane person would suggest to simply use
setInterval to toggle the element’s
visibility property and be done with it. (Actually, a sane person would beg you not to do any of this to begin with.) You could do it that way but that’d be boring. We all know that over-engineering things is more fun so we’re going to use Observables to achieve our goal.
RxJS offers the handy
Observable.timer() method to create a repeating Observable. It takes two arguments in milliseconds: an initial delay, and how often it should emit a value. We are going to use two of them, one to show the element and one to hide it.
The first one is easy; it should show the element right away (0 ms delay) and then again every second:
What about the second one? The period should be the same, every second, but we want it to trigger three quarters of a second after the element has been shown, so that means its initial delay should be 750 milliseconds:
Since we don’t want to duplicate our logic for each of these Observables, we’re going to merge them into a single one that will emit whenever any of them emits. For that we’ll use the
Let’s see what happens if we subscribe to it:
Uh, ok. We’re getting a bunch of numbers but we don’t even know which Observable is giving us which number. What are we supposed to do with those? Nothing, that’s what. Each Observable is giving us an incrementing number every time it emits a value but we don’t really care about it, we just want to know whether we should show or hide our component. In situations like that we can use the
mapTo operator, which simply discards the emitted value and replaces it with whatever else we want.
Let’s give it a try:
That’s much better, we’re getting somewhere here.
Every time show$ emits we want to set the
visibility of our component to
visible. Likewise, every time hide$ emits we want to set it to
hidden. One way to do that would be as follows:
Ok, sure. That would work. We have achieved blinkiness! But it feels rather verbose, doesn’t it? This is not the Rx way. What if instead of having the Observable tell us what to do we simply make it give us the appropriate visibility at every given moment?
Isn’t that beautiful?
We’re almost done. The only thing left is to actually create the
<blink> component and put our logic in it.
In order to change our element’s visibility we will need to ask Angular to give us a reference to ourselves through ElementRef via dependency injection. Since directly manipulating an element is discouraged we’ll also ask for the Renderer service, which will do that for us.
That’s the basic structure of the component. Angular is injecting a reference to our own element into the constructor and we’re extracting and storing the native element from it, which we we’ll pass to the Renderer’s
The template is really simple, we’re using
<ng-content> to tell Angular to get
<blink>whatever we put in here</blink> and put it inside our component . This is called Content Projection and it’s the equivalent of what was known as Transclusion on Angular 1.x.
If you want to learn more about Content Projection, Todd Motto has a very detailed post on his blog. I definitely recommend reading it — and anything else he writes, for that matter.
Let’s put it all together:
We did it! We’re done! Right? … Right? No we’re not. See those Observables? We subscribed to them, so what happens when our
<blink> component gets destroyed? They will keep emitting values forever and ever until the end of times (or until we kill our application, whatever comes first.) You know what that’s called? A memory leak!
The intuitive way to solve this, and what I’ve seen many people do, is to keep track of all our subscriptions and then unsubscribe from them all when destroying the component, but that doesn’t scale well. What if you end up having dozens or hundreds of subscriptions? Would you really be able to tell if you’ve forgotten to keep track of some of them?
A much cleaner solution is to use the
takeWhile operator. This tells our Observable to keep emitting values while a certain predicate is true. The instant it becomes false, it will automatically unsubscribe.
With that in mind, let’s refactor our component. We’ll also add all the necessary imports while we’re at it:
And now we’re done. Congratulations!
You can read more about @HostBinding() in Thierry Templier’s exhaustive article on component composition.
This is what it would look like:
If you liked this, click the💚 below so other people will see this here on Medium. You can follow @jsayol on Twitter to read more about Angular and other stuff.