Filtering Data Part 1

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

Table of Contents
Previous LessonOperatorsNext LessonFiltering Data Part 2

This lesson preview is part of the Mastering RxJS: A Compact Journey from Beginner to Pro course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to Mastering RxJS: A Compact Journey from Beginner to Pro, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course Mastering RxJS: A Compact Journey from Beginner to Pro

Hey guys, in this lesson, we are going to build our search functionality. For this, we head into our product service. In the product service, we will extend the fetch products functionality. We will pass a search term to our fetch products and inside the pipe function, we want to add another operator, which is map, let's import it. And the map operator will get the product list as input. So, let's type in products. And in here, we want to implement the logic that will filter the products based on the search term. The first step would be to check if the search term is present. And if it is, let's do the filter logic. So this is the filter logic. Let me format this. Okay. Now one of the key principles of functional programming is immutability. This means that data is not directly changed, but instead new versions of the data are being created. Also, this is something that stuck with me from when I was using Redux. I think this is a good practice and can help mitigate undesired behavior in the future. Let's refactor this. I will create a filter products variable and just create a copy of the products themselves. And then just assign this filter products with the output from this filter here. And lastly, we will return our filter products. So in case there is no search term, we will just return a copy of the products themselves. Also, just to make our testing a little bit faster, I will put the delay to two seconds in here. The next thing we want to do is create a component. Let's type into the CLI ng g c search, which is ng generate component named search. Let's update the template. In our search component, I will just paste that in here. Don't worry about the code. I will include it in the description of this video. It is complaining because we haven't imported our ReactiveFormsModule. There we have it. And now it stopped complaining. OK. And the last thing is in our app.component.html Let's add the search component here at the top. And of course, this needs the search term. For now, let's just give it an empty string. Alright. Let's run our application. Here we have our input and fortunately as we type nothing happens. This is fine because right now nothing is glued together. Our next step will be to glue all of this together. Let's go back to our application. Let's start with the search component first. We want to emit the value that has been typed in our input. So, to emit the value in Angular, let's use the output decorator and let's call it search results. And then we will create a new event emitter, which will emit a string. Step one is done. The next step is when the component will initialize, we want to add a listener to this search control so that whenever we type anything in our input field, we know which value is being typed. Let's implement OnInit. So, how do we listen to events emitted by our FormControl? The Angular reactive module exposes a property on the search control, which is called valueChanges, which is an observable. And because it is unobservable, we can subscribe to it. Let me demonstrate how this works. Let's go back to our application. If we look carefully at the console log, every time we do a keycap, we get our new value. Back to the application. As you member from our previous lesson, you also need to unsubscribe. Pause this video and try to write down subscribe implementation yourself. Or if you want to follow along, this is also fine. Let's implement onDestroy and also add the lifecycle onDestroy method. Let's create a new subscription, make it private. Let's store the subscription. Then in ngOnDestroy, we will unsubscribe from it. There we go. Let's assume for now that every time we type something in this form control, it will automatically be sent to our service, which will send the request to the back end, which will filter our product list and give us a filter list back. If we leave it like this, then on every keystroke, we will send the request to our server. This is not desirable. There are some optimizations that we can do. Let me share those with you. First, we will need to call the pipe function. Then we will add three operators. The first one is debounceTime, which will emit the latest value after a certain amount of time has passed. It's similar to the delay function, but it will only emit its latest value. Let's add time in here for the demonstration purposes. I will add two seconds. Let's go to our application. Now when we type in here anything, as you can see, I can still type and nothing will be fired because I have to stop generating a new event. Then two second pauses and then the latest value is emitted. Now if you go back to our application in a real-world scenario, this will be more like 300 milliseconds. Let's go back to the app. Now when we type, it looks more familiar like a real user would type something in. A real user might think before he types, might add something, he might delete something. This way we are limiting the amount of requests that is being sent as opposed to on every keystroke. Another optimization that we can do is when the user is copy-pasting something into the input. So let's say I am typing a camera in here, I will copy it and I will paste it again. We can see that the event has been emitted. Now let me select everything, let me paste it again. Whenever I paste the same value, a new event is emitted, which is not desirable . There is an operator called distinctUntilChanged and this operator makes sure that a new event will be emitted only if the new value is different from the previous one. Let's head back to our application. Now when I am copy-pasting camera, let me do that again and again. As you can see, no new event is being fired. The last thing that we want to add is a filter operator, which will actually filter all the search terms that are being emitted from the FormControl. Let's add the search term in here. So we want to filter all the values that have more than two characters. Let's filter this. Now if you go back to our application, when I type in two characters, nothing happens when I type the third one, new event gets emitted and if I go back to two, nothing happens. There is one small problem in here. If we clear the search, nothing happens. So we need to add that logic as well. For this, let's add an or and we need to check if the search terms length is equal to zero. Let's go back to characters, nothing. The third one, a new event has been emitted and if we clear, we can see that a new event has been emitted as well. Why do we want the last behavior to happen? When we clear all our filters, we want the list to reset so that we can start from where we started before. Lastly, we want to emit the search value. Like this and let's change the name search term to make it consistent. So what happens is whenever we type anything in our FormControl, a new event is being emitted. It goes through a bunch of operators and then we get eventually our search term , which will be emitted somewhere to a consumer, which in our case is the app component. Great. Now that we have the logic for our search component, let's go into the app component and implement the logic in here as well. The first thing that we need to do is create a function which will handle the search for us. For now, let's just console.log the search term and then in the app component, we need the app component to consume the emitted event and pass it down to handle search function. Like this. Now, let's go to our application. Let's see if everything gets outputted. Yes it does, we have glued our app together. The next thing that we need to do is somehow pass the search term to our fetch products function. So how do we do this? An easy way that some of you might think is reassigning products observable and just pause this search term to the search products function. Let's see if it works. Let me type something like DSLR. Great. It works. Let's type something else and it works again. Let's clear. As you can see, everything is fine at first sight. What you don't see is what happens behind the scenes. The problem with this approach is, although it works perfectly fine, it is not efficient. If you look closely at this function, every time we get the new search term, this products observable gets reassigned. But we are still calling a backend, so what happens, when we get a new term, but the previous request hasn't finished yet, it gets stuck in a limbo. Basically, there is no way to cancel any ongoing requests if a new search term has been entered. This is a consequence of thinking imperatively in a declarative world. Let's delete that. Go back to our console.log Now let's do it declaratively. Let's go to the drawing board. What we want to do is create an observable that whenever it gets new data, it will call our fetch products method with the search term in it. And then, the response from the search term will update the UI. So the first step is to create our observable. Let me create an observable. Let's call it filterTerm. Now inside our handle search function, we want to somehow update this observable. But as you can see, there is no way to update this observable. This is because you can only subscribe to it, but the you cannot update it. In order to update it, we need to look into a new concept, which is called the subject. In the next lesson, we will see what the subject is and how to use it. But for now, let's recap what we've seen so far. First, we've had a glance at different operators and seen how they can be changed together. And we've seen one implementation of how to do things declaratively, how to do things in an imperative way and why this is not efficient.