I was working on an update for one of my Hybrid apps on the Google Play Store when I came across a little problem. It was when I was trying to add a speech recognition component to the app. It had something to do with zones in Angular and in this post, I will talk about my solution as well give some theory on the aspects of it.

Background

My Simple Notes is one of my apps that’s available for both iOS and Android devices. The major difference between them apart from the way they are built is the presence of a speech recognition feature on iOS. The iOS version of the app is a purely native app whereas the Android version is a hybrid app built using Ionic Framework with Angular. One reason for this was because, I developed and released the iOS version later. Hence, I managed to sneak in a more advanced feature. In hindsight I should have avoided that. In ether case, it’s only now that I have had the time to add the speech recognition feature to the Android version. Before we talk about the problem and the solution, let’s talk a little bit about,

What is My Simple Notes?

It’s a simple note taking app for the user to make notes, which are synchronised across their web, iOS and Android devices. In an event the user does not want to type a note, they can simply dictate the note and the app will transcribe it.

Hybrid speech recognition

To add this for the hybrid version, I am using the Cordova speech recognition plugin. After following the installation and setup instructions for the plugin, I realised that while I was able to implement it, I could not quite achieve what I wanted.

What was I trying to build?

The solution I was trying to build was,

  • The user taps on the record note button to start voice recognition
  • The app records and transcribes the speech
  • The app updates the TextArea for the note

It really was that simple. However, there was a problem..

Problem

The app would record the user speech and transcribe it accurately, however it simply wouldn’t update the TextArea in the app after the user finishes talking. The dialog to capture the user speech disappears but the app UI simply doesn’t get updated until the TextArea is tapped. I mean, the property in the angular component (or Ionic page) was being updated, but that wasn’t reflected in the UI. After resorting to the usual problem solving methods such as a Google search for “NgModel changes but not reflected in UI” or “changes in angular class property not reflected in the template”, I came across the solution.

Like the blog? Subscribe for updates

What was happening?

Angular reflects the changes in the template based on changes in the component and can do so as long as it operates in the “Zone”. Ok, what is a Zone?

Angular Zone

A Zone’s in Angular as the official docs put it, “an execution context that persist across async tasks”. Angular uses it to automatically detects changes in the component (JS code) and update the HTML. If you’d like to know more about Angular binds the html controls to the properties in a component, have a read of the official docs. The docs are in that, they go into details about ApplicationRef.tick() and how change detection would work in plain Javascript. I would recommend having a read through them.

Hence if Angular performs any task that transfers control outside the zone, it then needs to re-enter the zone to perform the update. In my case, this code

this.speeechRecognition.startListening(options).subscribe((pharases: string[]))

Transferred control out of the zone, which is why changes to the property bound to the TextArea were not reflected in the UI. So what’s the solution?

Solution

This one’s not that hard, it’s fairly straight forward. I mean, all I needed to do was to re-enter the zone via the Zone service.

Import Zone from Angular/Core

import { NgZone } from '@angular/core';

Initialise the variable _ngZone in your constructor and add the code below in the method that triggers speech recognition.

this.speeechRecognition.startListening(options).subscribe((pharases: string[]) => {
    this._ngZone.run(() => {
        this.note.text = (pharases.length > 0)? pharases[0] : "";
    });
});

That’s it, my problem with updating the UI after this was solved. NgZone.run() is similar to $rootScope.$apply() from AngularJS (Angular 1).

I think it would be good to briefly discuss Angular Zone’s a little before concluding this post.

 

Conclusion

While this not a very hard problem but it was good to come across this anyway. It gave me an opportunity to have a deep dive into Angular change detection and better, how change detection works in plain Javascript.

Like the blog? Subscribe for updates

As usual, if you find any of my posts useful support me by  buying or even trying one of my apps on the App Store. 

https://mydaytodo.com/apps/

Also, if you can leave a review on the App Store or Google Play Store, that would help too.


0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *