Angular 2 — TwitchTV : Dynamic Search Result using Angular’s HTTP Service + RxJS Observables
This post will cover the generation of an interactive search result component using the Angular2 HTTP service. In our application we would like to be able to continue to interact with our page while HTTP requests are made via Twitch API calls and returned from the external server.
In this story you will need to follow the instructions and code yourself. At the end of this article our application will behave as shown!
Before we create the component lets loosely cover the basics of dealing with synchronous and asynchronous requests.
Synchronous Request: A synchronous request blocks code execution causing your application to wait until a response is returned.
Asynchronous Request: An asynchronous request doesn’t cause your application to wait and allows other operations to be performed.
In our application making a synchronous API call would impede any further code from executing. The blocking of code execution means that the application does not continue any further until a response is returned. With asynchronous API calls we can leverage the fact that we are able to continue the execution of code without having to wait for a response to the request.
Angular2 deals with asynchronous code using RxJS Observables. In short, Observables allow us to emit an asynchronous stream of events to any subjects subscribed to it. The emitted asynchronous stream of data can then be functionally operated on to return a desired output.
Step#1 — Setup
To setup Angular2 you need to install the angular-cli package globally. This will give us the tools needed to create the basic file and folder structure needed to create our Angular2 app.
$ npm install -g @angular/cli
Once angular-cli has been installed globally we can go ahead and create our seed project in the project directory.
$ ng new twitch-search
Let’s briefly understand the folder structure and the files that we are interested in.
e2e:This folder is for end to end testing as the name suggests. If you want to incorporate end-to-end testing automation use this folder. For this app we will not be using this.
node_modules: This folder will contain all the installed dependencies listed in
package.json contains a starter-set of dependencies that are required by Angular2. Inside
package.json there is a start script named
ng serve. We can run run this script to boot up our deployment server at
src: This is the main folder we will be using. It contains the app folder which contains all our code.
We will be using Bootstrap and its components to code the template. Head on over to
https://www.bootstrapcdn.comand get the complete HTML bootstrap CSS. For simplicity’s sake, we will add this external link to
app.component.html under the
Step #2 — Model
Under the directory
src/app create a folder and name it
searchbox . We will be creating the necessary files under this folder.
Create the file
streamresult class will serve as the model of our data that is requested from Twitch API calls. Add the following fields and a constructor in the class.
The idea behind
obj?:any in our constructor is that it allows us pass in an object containing our keys when a
StreamResult object is created.
Step #3 — Service
In Angular2 we can create components and nest components together with selectors. In order to share common functionalities over these components we will need to create a service to handle the requested external data.
Angular’s dependency injection allows our components to inject our service in its constructor when its needed (more on this later..).
We will be using the following endpoint provided by Twitch’s API in order to retrieve the data we desire.
Go ahead and create the service class
This class is decorated with the
@Injectable()decorator and is used with the dependency injector. The service contains the
search method that makes HTTP requests and receives HTTP responses back from the server asynchronously using Observables. In order to achieve this we add the necessary imports at the top of the class.
To use the HTTP service we inject an instance of
http in our service constructor.
The search method takes a parameter which will be used in our component to pass in dynamic keywords every time our search box is updated. Next,
http.get will fetch data using our constructed query consisting of the API key, keyword, and the number of objects returned by the array.
The return value of
http.get is of type Observable. This value is then operated on by the
map operator. The
map operator will return the response in the form of API data as JSON and the
catch operator will generate an observable that terminates with an error. Notice that by calling
<any>response.json() we are omitting the need for strict type checking.
Lets have a more detailed look at the actual
map operator. The
map operator will convert the response values into a stream of objects that is then iterated on and converted into a
streamResult object. It is important to realize here that the output of the map operator is an observable. We need to subscribe to this observable in order to extract a result.
Finally, we will need to add
streamresultService to the provider array found in
src/app. The purpose of this is to notify Angular to create a new instance of the service when
AppComponent is created. This service is then used by each component including its nested components.
In the next section we will be creating the components needed in order to subscribe to the result and display the view.
Step #4 — Components
Angular2 places a strong emphasis on components. A typical application is made up of a tree of components starting from the root and scaling down to it’s child components. We will create our first component
A couple of things to note here. The
@Component decorator is used to indicate that the class at is a component containing the appropriate selector, template, and style. Again, for the sake of demonstration we will keep the the HTML and CSS inline within the component itself.
@Input decorator defined in the class is responsible for passing data into the component itself. We have defined
result to be of type
Streamresult and thus we are able to use interpolation to display data to the component.
The next component we will create will display the search box and emit events based on the results. Lets create
src/app/searchbox. I will be breaking up the code in order to explain key concepts.
The template is a very simple search box to key in search results.
In the constructor of our class we have injected a
StreamresultService instance and an instance of
ElementRef in order to be able to access the underlying native DOM element.
The component we have created is a child component and will be able to pass values by including an
@Output decorator on a variable of type
EventEmitter. We have set two variables
result to capture the values of the custom events that are fired out.
Note that in the constructor we have implemented the
OnInit interface. Component instances have a lifecycle as they are created, modified or destroyed. One of the lifecycle hook interfaces in the core angular library is the
OnInit interface. Implementing this interface means we need to implement its hook method
ngOnInit(). Angular will call this method shortly after the component has been created.
Next, lets fill out the
ngOnInit() method. We would like this method to be called when the component is created and as such, a return value is not needed. Let us understand very carefully what is going on in the following block of code.
Observable.fromEvent is a very flexible event converter and will convert
keyup events into an observable stream(notice that the first argument
this.el.nativeElement is the native DOM element that this component is attached to). The stream is then piped to a
map operator which extracts the value of the
keyup input(our query). The
filter operator then filters the stream to store values that are not empty.
debounceTime will set a time span of 200ms before another value is emitted from the source observable. The
do observable will allow us to perform operations on the side. The operation we are performing is emitting
true from the
EventEmitter object to enable loading. The stream is then sent to the
map operator to be processed by our search method defined in our service. The
switch observable will consider the higher order Observable into a first-order Observable. This means that previous events are discarded and the most recent projected Observable is considered. Finally, the
subscribe operator will act upon the search on a success, error, and completion condition. In each of these cases we can then disable loading as the result has been handled by the subscriber. Only in the success condition do we emit our result back to the components output event handler.
We will now create the parent component by creating
src/app/searchbox. The parent component will utilize the selectors defined in the previous components.
Remember that the
my-searchbox selector was previously defined inside
StreamresultsearchboxComponent. Inside the selector tag there are two specific events that are emitted from the component. These two events are
StreamresultsearchboxComponent emits a
loading event the variable is set within the value of
$event. Likewise, when a
results event is emitted
updateResults() is called with the value of
$event . This has the effect of updating our components
results instance variable.
search-result selector was previously defined in
SearchComponent uses the
result in order to retrieve a
Streamresult object to display in its view. The
search-result selector utilizes the
ngFor directive to iterate through an array of
Streamresult objects to bind an individual
result object to display.
Now that we have completed coding our components section we will need to wire them to
Additionally, we will need to add the selector tag
twitch-search from the
TwitchsearchstreamComponent and place it inside
AppComponent is the root component of this application.
Finally we’re done! ..load up the dev server and run your application!