Start United States USA — software Advanced RxJS With Angular and Web Speech (Part 2)

Advanced RxJS With Angular and Web Speech (Part 2)

304
0
TEILEN

Finish your RxJS app with keyboard fallbacks for users, using operators to work with API requests, authentication, and using an observable for a progress bar.
In Part 2, we will cover:
Let’s pick up right where we left off.
Not all browsers support speech recognition. If we view our app right now in a browser that doesn’t, we’ll see nothing but a header. An app that doesn’t work in most browsers isn’t useful. Let’s make a Keyboard component to use in place of the Listen component so users with unsupported browsers can still create a fun madlib using the Words Form component.
Create another new component with the Angular CLI like so:
Open the keyboard.component.ts file and implement the following code:
This is a very simple component that primarily consists of a template. It is, however, still going to be a parent component (with Words Form as a child), so we’ll import the Words class and set up the nouns, verbs, and adjs arrays like we did in the Listen component. We can then pass these as inputs to the Words Form component.
Let’s open the keyboard.component.html file and add our template:
This simply adds some instructions similar to the Listen component and displays the WordsForm component.
Now let’s display the Keyboard component instead of the Listen component if speech is not supported. Open the app.component.html template and make the following addition:
Now if speech recognition is not supported, the user will see the Keyboard component and can still enter words manually with the form. The app should look like this in a browser that doesn’t support speech recognition:
Now that we can speak and type words to generate a madlib, there’s one more method we want to offer to users to create their custom story: automatic word generation using a prebuilt Node API.
Clone the madlibs-api locally to a folder of your choosing. Then open a command prompt or terminal window in that folder and run the following commands:
This will install the required dependencies and then run the API on localhost:8084. You should be able to visit the API in the browser at http://localhost:8084/api to confirm it’s working properly. You can then access its endpoints. Check out the repository’s README to see all the available endpoints. You can try them out in the browser to see what they return (for example, http://localhost:8084/api/noun). Take a minute to become familiar with the API and its endpoints and give some thought to how we might leverage the API in our application to generate arrays of the different parts of speech.
Now that you have the madlibs API set up and running and you’ve familiarized yourself with how it works, let’s consider what our intended functionality is.
Recall the specifics of what we’re expecting the user to respond with based on our word arrays and the Words Form placeholders. In order to generate the appropriate words automatically, we’ll need the following from the API:
The API, however, does not return arrays, it returns single text strings. Also, we have different endpoints for people, places, things, present and past tenses, etc. How can we reconcile the API functionality with our requirements?
Luckily, we have RxJS available to us! Let’s explore how we can use this powerful library to get exactly what we want from the API.
The first thing we need to do is add Angular HTTP to our App module. Open the app.module.ts file and add:
We’ll import the HttpClientModule and add it to the NgModule’s imports array, making the module available to our application.
Now open the madlibs.service.ts file. We’ll add our HTTP requests to this file.
Let’s go over these additions step by step. First we’ll add some new imports: HttpClient and HttpErrorResponse from Angular common, Observable from RxJS, and the map and catch RxJS operators.
Next we’ll add some new properties: _API to store the API URI and a words object to store the words retrieved from the API.
We’ll make HttpClient available to our component in the constructor.
Next we have our private _stringSuccessHandler(). This method takes a text HTTP response and strips the double quotes ( „) that are automatically added to it, leaving just the word. We’ll use this success handler with all of our API requests that return a text response.
The private _errorHandler() method cancels the observable with an error message in case something went wrong with the request.
Next, let’s take a closer look at the getNouns$() method (and the two methods that follow it, getVerbs$() and getAdjs$()).
We’ll return Observable.forkJoin(), passing in the array of observables we’d like to use in the expected order. If all requests are successful, subscribing to this observable will produce an array that looks like this:
We’ll use forkJoin again with our getVerbs$() method to return a stream that emits an array of verbs in the following tenses:
To get adjectives, we’ll forkJoin an adjective request five times, since all adjectives are the same, but each one requires its own request. This will result in:
Once we receive the resulting array that includes the zipped arrays from the nouns, verbs, and adjectives observables, we’ll map the response to an easy-to-read object.
We can now subscribe to the getWords$() observable in components and receive an object that looks like this:
This is exactly what we want from the API when generating words, and it’s easy to accomplish, thanks to RxJS.
Now that we’ve implemented the necessary methods to get nouns, verbs, and adjectives from the API, it’s time to put them to use in our application.
Let’s create a Generate Words component. This component will use the Madlibs service to set up a subscription to fetch the words from the API when the user clicks a button. It will then emit an event containing the API data so that other components (such as the Words Form component) can use that data.
Generate the new component:
Open the generate-words.component.ts file and add the following code:
First, we’ll add some imports. The OnDestroy lifecycle hook is necessary to clean up subscriptions when the component is destroyed. Output and EventEmitter are needed to emit an event from this component to a parent. Then we’ll import our MadlibsService to get API data, as well as Subscription from RxJS.
We’ll implement the OnDestroy lifecycle hook when we export our GenerateWordsComponent class.
We’ll set up an @Output() fetchedWords = new EventEmitter property for a parent component to listen for a child event. We also need a wordsSub subscription to the getWords$() observable, and three boolean properties so the UI can reflect the appropriate states of the app: loading, generated, and error.
We’ll make MadlibsService available to the component in the constructor function.
The fetchWords() method will be executed when the user clicks a button to generate words via the API. When run, this function should indicate that the app is loading, words have not yet been generated, and there are currently no error s.
The wordsSub subscription should be set up as well. This subscribes to the getWords$() observable from the Madlibs service. On a successful response, it updates the app states and emits the fetchedWords event with a payload containing the API response. If you recall from our code above, this is an object containing the noun, verb, and adjective arrays. If an error occurs, the app states reflect this and a warning is raised in the console.
Finally, in the ngOnDestroy() lifecycle function, we’ll check to see if the wordsSub exists and unsubscribe() from it if so.
Now open the generate-words.component.html template:
This is a straightforward template that displays some copy informing the user that they can generate words from the API, but doing so will replace any words they may have already entered using speech recognition or the form.
It shows a button that executes the fetchWords() method when clicked and changes label depending on the loading state.
If data is successfully generated using the API, a „Success!“ message is shown. Simultaneously (and behind the scenes), an event is emitted with the API data. If an error occurred, an error message is displayed.
We now need to add our Generate Words component to both the Listen and Keyboard Components. We’ll also need to add a little bit of functionality to these components so they can listen for the fetchedWords event and react to it by updating their local property data with the words from the API.
Open the listen.component.ts and keyboard.component.ts component classes and add the following method to each file:
This is the handler for the fetchedWords event. It takes the event payload and uses it to define the values of the local nouns, verbs, and adjs properties, thus updating all of these arrays to the data from the API.
Open the listen.component.html template and at the top, add the element right inside the opening :
This element listens for the (fetchedWords) event and runs the onFetchedAPIWords($event) handler when the event is emitted, sending the $event data as a parameter.
If speech recognition is supported, the app should now look like this in the browser:
Now open the keyboard.component.html template. Below the unordered list in the keyboard instructions, add the Generate Words component like so:
Now both the Listen and Keyboard components support word generation with the API. Make sure the API is running locally.
You (or the user) should now be able to click the „Generate Words“ button whether speech recognition is supported or not. The form should populate with words retrieved from the API. If the user clicks the button again, new random words should be fetched and will overwrite any existing words.
In browsers that support speech recognition, the user should be able to delete API-generated words and then use the speech commands to fill them back in. In any browser, the user can edit or replace words manually by typing in the form.
The next component we’re going to add is a bit of flair. It’s a progress bar that we’ll build with RxJS. Although it won’t represent the app actually generating the madlib story (because that happens so quickly a progress indicator would be essentially pointless), it does lend a nice visual and helps us explore another feature of RxJS: creating timer observables.
We’ll also call the API and fetch a pronoun while the progress bar is running, but again, with a server running on localhost, this happens so quickly the UI progress bar won’t represent the API request and response speed.
The progress bar will appear after the user clicks the „Go!“ button to generate their madlib. When we’re finished, it will look like this in the browser:
Let’s go over the features of our Progress Bar component:
As you can see, several of these features rely on the Madlibs service, so let’s make some updates there before we tackle the Progress Bar component itself.
Open the madlibs.service.ts file:
First, we’ll add some new properties: madlibReady to indicate when the timer observable has completed, and the pronoun object acquired from the API.
We’ll need a way for components to set the value of madlibReady, so we’ll create a setter method called setMadlibReady() that accepts a boolean argument that updates the value of the madlibReady property.
We’ll also need a way for components to set the value of pronoun, so we’ll create another setter method called setPronoun() .
Finally, we’ll add a getPronoun$() HTTP request that returns an observable. This request fetches a pronoun object from our API, which can be accessed via subscription in our Progress Bar component.

Continue reading...