AngularJS is one of the hottest JavaScript frameworks on the internet providing a full stack for creating single page applications (SPAs).
Angular Directives are a way to teach HTML new tricks. During DOM compilation directives are matched against the HTML and executed. This allows directives to register behavior, or transform the DOM.
The Web Speech API provides an alternative input method for web applications (without using a keyboard). Developers can give web applications the ability to transcribe voice to text, from the computer's microphone.
To quickly get started creating a custom component for AngularJS, install the AngularJS Component Generator, execute the following command:
$ sudo npm install -g generator-angular-component
Now you'll be able to scaffold a angular component project.
Proceed to create the project folder and then cd into that directory.
$ mkdir angular-webspeech-directive && cd angular-webspeech-directive
Now use Yeoman to create the project files, execute the following command:
$ yo angular-component
Then proceed to answer a few questions about your project.
[?] Would you mind telling me your username on Github? jonniespratley
[?] What's the base name of your project? angular-webspeech-directive
[?] Under which lincense your project shall be released? MIT
[?] Do you need the unstable branch of AngularJS? Yes
[?] Does your module requires CSS styles? Yes
For distribution, register the new project with Bower (a web library package manager), execute the following command:
$ bower register [component-name] [component-github]
Now the component is available to the world via the bower
package manager.
To create a directive with AngularJS it is best to create a module for the directive, then attach the directive definition to your module instance.
This allows users of the component to easily include the required scripts
and declare the component in the existing applications dependencies array.
To define the module, use the angular.module()
method to create a module instance, in this case the variable _app
is the components module.
# Module instance
_app = angular.module('jonniespratley.angularWebSpeechDirective', [])
The angular.module
is a global method for creating, registering and retrieving Angular modules.
-
When passed two or more arguments, a new module is created.
angular.module('jonniespratley.angularWebSpeechDirective', [])
-
If passed only one argument, an existing module (the name passed as the first argument to module) is retrieved.
angular.module('jonniespratley.angularWebSpeechDirective')
All modules that should be available to an application must be registered using this method.
The factory
module is a good way to store methods or properties that can be reused throughout your directive. We create a factory
for storing the icons, messages and some utility methods that the directive will use.
To register a service factory, which will be called to return the service instance, use the following format:
_app.service 'jsSpeechFactory', (['$rootScope', ($rootScope) ->
jsSpeechFactory =
icons:
start: 'http://goo.gl/2bfneP'
recording: 'http://goo.gl/p2jHO9'
blocked: 'http://goo.gl/vd4AKi'
messages:
info_speak_now: 'Speak now... or <a href="#" ng-click="abort()">Cancel</a>'
info_stop: 'Proccessing your voice...'
info_no_speech: 'No Speech was detected. You may need to adjust your <a href="//support.google.com/chrome/bin/answer.py?hl=en&answer=1407892">microphone settings</a>.'
info_no_mic: 'No microphone was found. Ensure that a microphone is installed.'
info_blocked: 'Permission to use microphone is blocked. To change, go to <a href="chrome://settings/contentExceptions#media-stream">chrome://settings/contentExceptions#media-stream</a>.'
info_denied: 'Permission to use microphone was denied.'
info_setup: 'Click on the microphone icon to enable Web Speech.'
info_upgrade: 'Web Speech API is not supported by this browser. Upgrade to <a href="//www.google.com/chrome" target="_blank">Chrome</a> version 25 or later.'
info_allow: 'Click the "Allow" button above to enable your microphone.'
linebreak: (s) ->
s.replace(/\n\n/g, "<p></p>").replace /\n/g, "<br>"
capitalize: (s) ->
s.replace /\S/, (m) ->
m.toUpperCase()
])
The directive
definition object options available are as follows:
Property | Description |
---|---|
restrict | Declare how directive can be used in a template as an element, attribute, class, comment, or any combination. |
priority | Set the order of execution in the template relative to other directives on the element. |
template | Specify an inline template as a string. Not used if you’re specifying your template as a URL. |
templateUrl | Specify the template to be loaded by URL. This is not used if you’ve specified an inline template as a string. |
replace | If true, replace the current element. If false or unspecified, append this directive to the current element. |
transclude | Lets you move the original children of a directive to a location inside the new template. |
scope | Create a new scope for this directive rather than inheriting the parent scope. |
controller | Create a controller which publishes an API for communicating across directives. |
require | Require that another directive be present for this directive to function correctly |
link | Programmatically modify resulting DOM element instances, add event listeners, and set up data binding. |
compile | Programmatically modify the DOM template for features across copies of a directive, as when used in ng-repeat |
The definition object that this directive will use is as follows:
_app.directive 'jsSpeech', (['jsSpeechFactory', (jsSpeechFactory)->
restrict: 'AE'
replace: true
transclude: true
require: '^ngModel'
scope:
ngModel: '='
template: """
<div class="jsSpeechFactory-container">
<a href="" class="jsSpeechFactory-btn" ng-click="toggleStartStop()">
<img ng-src="{{speech.icon}}" class="jsSpeechFactory-icon"/></a>
<input type="text" class="form-control" ng-model="ngModel.value"/>
<p class="text-muted jsSpeechFactory-hint" ng-bind-html-unsafe="speech.msg"></p>
</div>
"""
link: (scope, element, attrs, ngModel) ->
#Contents below
])
Directives that modify the DOM use the link option, which takes a function with the following signature:
function link(scope, element, attrs, /*ngModel optional*/) {
#Adjust the DOM, add event listeners
}
Parameter descriptions:
scope
- is an Angular scope object.element
- is the jqLite-wrapped element that this directive matches.attrs
- is an object with the normalized attribute names and their corresponding values.ngModel
- is angModelController
object that provides an API for theng-model
directive, with services for data-binding, validation, CSS updates, and value formatting and parsing.
In order to properly hook into the directive to attach event listeners and manipulate the DOM provide a link function.
link: (scope, element, attrs, ngModel) ->
$scope = scope
Setup the user interface with default options.
$scope.speech =
msg: jsSpeechFactory.messages.info_setup
icon: jsSpeechFactory.icons.start
recognizing: false
To watch the model for any changes call the $watch
method on the scope.
scope.$watch('ngModel', (newVal, oldVal) ->
console.log newVal
, true)
Utility for doing a safe $apply
, basically this method checks to see if a $apply
is already in progress.
safeApply = (fn) ->
phase = scope.$root.$$phase
if phase is "$apply" or phase is "$digest"
fn() if fn and (typeof (fn) is "function")
else
scope.$apply fn
Utility method for setting the message value in the UI.
setMsg = (msg) ->
safeApply(()->
$scope.speech.msg = jsSpeechFactory.messages[msg]
)
Utility method for setting the image icon in the UI.
setIcon = (icon) ->
safeApply(()->
$scope.speech.icon = jsSpeechFactory.icons[icon]
)
Handle checking to see if the browser has the api.
init = ->
reset()
if 'webkitSpeechRecognition' of window
recognition = new webkitSpeechRecognition()
recognition.continuous = true
recognition.interimResults = true
recognition.onerror = onerror
recognition.onend = reset
recognition.onresult = onresult
recognition.onstart = onstart
else
recognition = {}
upgrade()
Handle changing the UI by setting the message and icon.
upgrade = ->
setMsg 'info_upgrade'
setIcon 'blocked'
Handle when the recording starts up.
onstart = (event) ->
setIcon 'recording'
setMsg 'info_speak_now'
console.log 'onstart', event
Handle any errors from the Speech Recognition API.
onerror = (event, message) ->
console.log 'onerror', event, message
switch event.error
when "not-allowed"
setMsg 'info_blocked'
when "no-speech"
setMsg 'info_no_speech'
when "service-not-allowed"
setMsg 'info_denied'
else
console.log event
Handle processing the results from the Speech Recognition API.
onresult = (event) ->
setIcon 'recording'
setMsg 'info_speak_now'
resultIndex = event.resultIndex
trans = ''
i = resultIndex
while i < event.results.length
result = event.results[i][0]
trans = jsSpeechFactory.capitalize(result.transcript)
safeApply(()->
$scope.ngModel.interimResults = trans
)
if event.results[i].isFinal
safeApply(()->
$scope.ngModel.value = trans
)
++i
Handle reseting the UI after recognition is complete.
reset = (event) ->
console.log 'reset', event
$scope.speech.recognizing = false
setIcon 'start'
setMsg 'info_setup'
Allow the user to toggle starting and stopping the recognition.
$scope.toggleStartStop = ->
if $scope.speech.recognizing
recognition.stop()
reset()
else
recognition.start()
$scope.speech.recognizing = true
setIcon 'blocked'
Finally start the initialization of the directive.
init()
Now that we have the basic structure and logic to get the Web Speech Recognition API working with a custom UI, extending this directive to add additional functionality should be pretty seamless.
The code is available on Github, so feel free to contribute more customizable options, keyword event maps and other logic to make this directive more effective and efficient.
Download the production version or the development version.
Or install via bower:
bower install angular-webspeech-directive --save
Add to main page:
<script src="angular.js"></script>
<script src="dist/angular-webspeech-directive.min.js"></script>
Add to main script:
app = angular.module("plunker", ["jonniespratley.angularWebSpeechDirective"])
Add to view:
<js-speech ng-model="speech"></js-speech>
Add to controller:
app.controller "MainCtrl", ($scope) ->
$scope.speech =
maxResults: 25
continuous: true
interimResults: true
value: ''
For detailed information about the W3C Web Speech API visit the website.
For an example visit the Plunkr.