Lesson 8 -- Events and Links

VoiceXML has the capability of generating events and handling them, using the <throw> and <catch> elements. A comprehensive discussion of event handling can be found in Section 5.2 of the VoiceXML 2.0 specification: http://www.w3.org/TR/2004/REC-voicexml20-20040316/.

See also:

http://msdn.microsoft.com/en-us/library/ff929027.aspx

http://msdn.microsoft.com/en-us/library/ff928997.aspx

Throwing and catching events

An application can throw pre-defined events or custom events that the programmer defines. Examples of throwing pre-defined events are:

  • <throw event="noinput"/>, thrown when the caller says nothing

  • <throw event="nomatch"/>, thrown when the caller says something not in the grammar

Note

You do not have to throw noinput or nomatch events in your VoiceXML code—the platform throws them for you.

An example of a programmer-defined event is:

<throw event="password_invalid"/>

<catch> elements contain executable content. When an application throws an event, the VoiceXML interpreter looks for the corresponding event handler (a <catch> element with the exact name of the thrown event) and executes the instructions in the <catch> element.

At their simplest, <catch> elements look like this:

<catch event="noinput">
     <!--    do something   -->
</catch>

An example

Here is a working example. It uses the publicly-available Tellme grammar confirm.grxml. You can run this in the Tellme Studio Scratchpad and see what happens if you say nothing, or say the wrong thing, or say the right thing.

<?xml version="1.0" encoding="UTF-8"?>
<vxml xmlns="http://www.w3.org/2001/vxml" version="2.1" revision="4">
<form id="YesOrNo">
  <field name="YesOrNo">
    <prompt>
      Is that correct?
    </prompt>

    <grammar
      src="http://grammar.svc.tellme.com/yesno/mss/v1/confirm.grxml"/>

    <catch event="noinput">
      I didn't hear that. Please try again.
      <log>caller didn't say anything</log>
      <reprompt/>
    </catch>

    <catch event="nomatch">
      I'm sorry. I didn't get that. Please try again.
      <log>caller's utterance didn't match anything in the grammar</log>
      <reprompt/>
    </catch>
    
    <filled>
      <log>returned answer = <value expr=" YesOrNo "/> </log>
      You said <value expr=" YesOrNo "/>
    </filled>
  </field>
</form>
</vxml>

Scope and inheritance of <catch> elements

The <catch> element for a given event can be placed at the form item level, the dialog level, the document level (just after the <vxml> element, but before any dialogs), or at the application root level (just after the <vxml> element of the application root) if there is an application root.

Important

An element inherits the <catch> element from each of its ancestor elements, as needed. So a <catch> element defined at the document level is available everywhere in the document and a <catch> element defined at the application root level is available everywhere in the application. When there are <catch> elements at several levels, the one closest to where the event is thrown is the one that applies.

When an event is thrown, the interpreter looks for a <catch> element for an event of that name in the element where the event was thrown. If there is one, it is executed. If there is none, the interpreter looks for a document level <catch> element and executes it. If there is none at the document level, the interpreter looks for an application level <catch> element. If there is none, the application ignores the event or exits, depending on the nature of the event.

Platform-defined <catch> elements

Certain events are so common that the platform provides special <catch> elements, as required by the VoiceXML 2.0 specification. They are:

  • <error> -- handles errors thrown by the platform or by your code.

  • <help> -- handles events thrown by the platform when the caller says "help" (requires a <link> with a help grammar).

  • <noinput> -- handles events thrown by the platform when the caller says nothing before the end of the timeout interval (set by the timeout property, default = 7 seconds).

  • <nomatch> -- handles events thrown by the platform when the caller says something not in the grammar.

As an example, if the caller does not say anything, the platform throws the noinput event. You can use a <catch> element like this:

<catch event="noinput">
     You didn't say anything. Try again.
     <reprompt/>
<catch/>

Alternatively, you can use the special <noinput> catch element:

<noinput>
     You didn't say anything. Try again.
     <reprompt/>
<noinput/>

As with <block> or <prompt> elements, text found in <catch> elements is rendered as TTS. Therefore, you do not need to use a <prompt> element in the <noinput> element, although you can. The <noinput> element above could have been written:

<noinput>
     <prompt>You didn't say anything. Try again.</prompt>
     <reprompt/>
<noinput/>

Catching more than one event

The event attribute of the <catch> element is optional. If it is not present, the <catch> element will handle all of the events thrown by the platform or by your code:

<catch>
     <!-- will handle ALL events -->
</catch>

The event attribute can also include a space-delimited list of events to be handled:

<catch event="noinput nomatch">
     <!-- will handle both noinput and nomatch events -->
</catch>

Other attributes of the <catch> element

In addition to the event attribute, the <catch> element has two other optional attributes:

  • cond – equals a JavaScript expression that evaluates to true or false.

  • count – equals the number of times this event has been thrown in this context.

When the cond attribute is used, the condition expression must evaluate to true or the code in the <catch> element is not executed.

Note

The shorthand <catch> elements (<error>, <help>, <noinput>, and <nomatch>) also have the optional cond and count attributes.

The count attribute for a <catch> element in a form, or in one of the form's form items, is reset to 1 each time the form is re-entered. The count attribute for a <catch> element in a form item is also reset to 1 whenever the form item variable is cleared.

The count attribute is incremented each time the event is caught. This allows the code to handle the event differently, depending on the number of times that the event has occurred without leaving the form. For example, we can modify the VoiceXML document shown earlier, as follows (changes shown in bold font):

<?xml version="1.0" encoding="UTF-8"?>
<vxml xmlns="http://www.w3.org/2001/vxml" version="2.1" revision="4">
<form id="YesOrNo">
  <field name="YesOrNo">
    <prompt>
      Is that correct?
    </prompt>

    <grammar
      src="http://grammar.svc.tellme.com/yesno/mss/v1/confirm.grxml"/>

    <catch event="noinput nomatch">
      Sorry. Didn't get that. Please try again.
      <reprompt/>
    </catch>

    <catch event="noinput nomatch" count="3">
      Sorry you're having trouble. Please call back later. Goodbye.
      <exit/>
    </catch>    
    <filled>
      <log>returned answer = <value expr=" YesOrNo "/> </log>
      You said  <value expr=" YesOrNo "/>
    </filled>
  </field>
</form>
</vxml>

As you can see, we are now handling both the noinput and nomatch events with the same <catch> element and we are hanging up on the caller after three failures.

Important

You should always include reprompt text or a <reprompt> element in <noinput> or <nomatch> elements. The <noinput> or <nomatch> elements both reset the timeout timer and resume listening for caller input. If you do not include a <reprompt> or text to the same effect, the caller will not be made aware that the application is once again waiting for input.

Using the <help> element

The <help> element can be used in situations where you have a complex prompt that a caller might misinterpret. You can clarify the original prompt in the body of the <help> element.

To use the <help> element, you must have an active grammar that contains "help" as an utterance and returns help as the match value. Such a grammar is usually placed in a <link> element. We will show an example of a <help> element when we talk about links.

Using <noinput> and <nomatch> in fields

If a caller does not say anything or does not say something that matches an active grammar during the time period defined by the timeout property (default = 7 seconds), the system throws a noinput or nomatch event respectively. What happens then depends on the noinput or nomatch handlers in your field:

  1. If there is no handler, the application exits immediately with an error.

  2. If there is a handler, the application increments the prompt counter by 1, restarts the timeout period, and listens for input again.

    1. If the handler contains text, the text is rendered as TTS, whether or not the text is enclosed in <prompt>.....</prompt> tags.

    2. If the handler has a transfer of control (for example, a <goto>) or <exit/> statement, the application will transfer out of the field or exit the application.

    3. If the handler includes a <reprompt/> element, the field's main prompt (with the current count attribute, if any) will be played.

Here are some common cases that do not include transfer of control or exit instructions:

  1. An empty handler such as <noinput/> -- When a noinput event is thrown, the application will restart the timeout period and start listening again, but otherwise do nothing. Therefore, the application is listening for input again but the caller has no way of knowing that. The application will throw a noinput event and listen for the timeout period, throw a noinput event and listen for the timeout period, throw a noinput event and listen for the timeout period, etc. It will do this repeatedly until the caller utters something or hangs up.

  2. A noinput handler with text but no reprompt – When a noinput event is thrown, the application will render the text (for example, "I didn't get that. Please try again."), restart the timeout period, and start listening again. Depending on the text, the caller may or may not understand that they should say something. This, too, will repeat until the caller says something or the caller hangs up.

  3. A handler with a reprompt – Such a handler may or may not have text to render as TTS. If it does, the text will be delivered. In any case, the field's main prompt (with the current count attribute, if any) will be replayed, the timeout period will then begin, and the application will listen. In this case, the caller should understand clearly that he or she is being asked for input.

Tip

For a good caller experience, you should include a <reprompt/> (or equivalent text) in your <catch> elements.

A <link> element specifies a grammar and either:

  • A destination to go to if the grammar is matched.

  • An event to throw if the grammar is matched.

The <link> element has two important attributes that are used most frequently. For other attributes, see http://msdn.microsoft.com/en-us/library/ff928992.aspx. The two most common attributes are:

  • next – the URI of a form or document to transition to.

  • event – the name of an event to throw.

Here is an example of a <link> element with a destination. When the caller says "exit," "stop," or "quit" this link transitions to a form with id="exit" that contains code to terminate the application (shown just below the <link> element).

<link next="#exit">
   <grammar mode="voice"
         root="root_rule"
         version="1.0"
         xml:lang="en-US">
      <rule id="root_rule">
         <one-of>
            <item>exit</item>
            <item>stop</item>
            <item>quit</item>
         </one-of>
      </rule>
   </grammar>
</link>


<form id="exit">
   <block>
      <exit/>
   </block>
</form>

Here is an example of a <link> element that throws a help event when the caller says "help":

<link event="help">
   <grammar mode="voice"
         root="root_rule"
         version="1.0"
         xml:lang="en-US">
      <rule id="root_rule">
            <item>help</item>
      </rule>
   </grammar>
</link>

Note

<grammar> elements are the only allowed children of the <link> element.

<link> elements can be children of <vxml>, <form>, or <field> elements. They are most commonly used as children of <vxml> so that they have document or application scope.

A new version of app-root.vxml

Starting with the version of app-root.vxml that we listed at the end of Lesson 8, we add the following:

  • A <link> element to exit the application if the caller says "exit," "stop," or "quit."

  • A <link> element to throw a help event if the caller says "help."

  • <catch> elements for noinput and nomatch in the main_selection and new_reservation_type fields.

  • A <help> element in the main_selection and new_reservation_type fields to catch help events.

The new code is shown in bold font.

<?xml version="1.0" encoding="UTF-8"?>
<vxml xmlns="http://www.w3.org/2001/vxml" version="2.1" xml:lang="en-US"
      revision="4"
      xml:base="https://studio.tellme.com/vxml-tutorial/">

   <script><![CDATA[

      var doNewPlaneRes = false;

      var doNewHotelRes = false;
      var doNewCarRes = false;
      var doChangePlaneRes = false;
      var doChangeHotelRes = false;
      var doChangeCarRes = false;
      var doRestaurantRec = false;

   ]]></script>
   
   <link event="help">

      <grammar mode="voice" root="root_rule" version="1.0" xml:lang="en-US">
         <rule id="root_rule">
               <item>help</item>
         </rule>
      </grammar>
   </link><link next="#exit">
      <grammar mode="voice" root="root_rule" version="1.0" xml:lang="en-US">
         <rule id="root_rule">
            <one-of>
               <item>exit</item>
               <item>stop</item>
               <item>quit</item>
            </one-of>
         </rule>
      </grammar>
   </link>

   <form id="main">
      <field name="main_selection">
         <prompt bargein="true" bargeintype="speech">
            Welcome to Contoso Travel<break/>
            Say new reservation<break size="small"/> or press 1<break/>
            Say change reservation<break size="small"/> or press 2<break/>
            Say restaurant recommendation<break size="small"/> or press 3
         </prompt>

         <grammar version="1.0" root="top" tag-format="semantics/1.0">
            <rule id="top">
               <item><ruleref uri="#nonsense"/></item>
                  <one-of>
                     <item>new reservation
                     <tag>out="new reservation";</tag></item>

                     <item>change reservation
                     <tag>out="change reservation";</tag></item>

                     <item>restaurant recommendation
                     <tag>out="restaurant recommendation";</tag></item>
               </one-of>
               <item><ruleref uri="#nonsense"/></item>
            </rule>
            <rule id="nonsense">
               <one-of>
                  <item><ruleref special="GARBAGE"/></item>
                  <item><ruleref special="NULL"/></item>
               </one-of>
            </rule>
         </grammar>
         
         <grammar mode="dtmf" version="1.0" root="top">
            <rule id="top">
               <one-of>
                  <item>1</item>
                  <item>2</item>
                  <item>3</item>
               </one-of>
            </rule>
         </grammar>

         <help>
            You should say one of these three phrases exactly. 
            New reservation, change reservation, or 
            restaurant recommendation.
         </help>

         <catch event="noinput nomatch">
            Sorry. Didn't get that. Please try again.
            <reprompt/>
         </catch>

         <catch event="noinput nomatch" count="3">
            Sorry you're having trouble. Please call back later. Goodbye.
            <exit/>
         </catch>

         <filled>
            <var name="next_destination" expr="' '"/>
            <if cond="main_selection == 'new reservation' || main_selection == '1'">
               <assign name="next_destination"
                       expr="'#new_reservation'"/>
            <elseif cond="main_selection == 'change reservation' || main_selection == '2'"/>
               <assign name ="next_destination"
                       expr="'change-reservation.vxml'"/>
            <elseif cond="main_selection == 'restaurant recommendation' || main_selection == '3'"/>
               <assign name ="next_destination" expr="'restaurant.vxml'"/>
            <else/>
               <assign name ="next_destination" expr="error.vxml"/>
            </if>
            <goto expr="next_destination"/>
         </filled>
      </field>
   </form>

   <form id ="new_reservation">
      <field name="new_reservation_type">
         <prompt bargein="true" bargeintype="speech">
            What type of reservation do you want to make<break/>Say
            plane<break size="small"/>, hotel
            <break size="small"/>, or car<break/>
            You may choose more than one.
         </prompt>
         <grammar version="1.0" root="top" tag-format="semantics/1.0">
            <rule id="top">
               <one-of>
            <!--single choice -->
                  <item><ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane";</tag></item>

                  <item><ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>
                  <tag>out="hotel";</tag></item>

                  <item><ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>
                  <tag>out="car";</tag></item>

            <!--double choice -->
                  <item><ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel";</tag></item>

                  <item><ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel";</tag></item>

                  <item><ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>
                  <tag>out="hotel and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>
                  <tag>out="hotel and car";</tag></item>

            <!--triple choice -->
                  <item><ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel and car";</tag></item>

                  <item><ruleref uri="#nonsense"/>car
                  <ruleref uri="#nonsense"/>hotel
                  <ruleref uri="#nonsense"/>plane
                  <ruleref uri="#nonsense"/>
                  <tag>out="plane and hotel and car";</tag></item>
               </one-of>
            </rule>
            <rule id="nonsense">
               <one-of>
                  <item><ruleref special="GARBAGE"/></item>
                  <item><ruleref special="NULL"/></item>
               </one-of>
            </rule>
         </grammar>
         
         <help>
            Just say one word for each choice. Choices are plane, 
            hotel, and car.
         </help>
         
         <catch event="noinput nomatch">
            Sorry. Didn't get that. Please try again.
            <reprompt/>
         </catch>

         <catch event="noinput nomatch" count="3">
            Sorry you're having trouble. Please call back later. Goodbye.
            <exit/>
         </catch>
      </field>

      <subdialog name="objAnswer" src="#confirmation">
         <param name="caller_response" expr="new_reservation_type"/>
         <filled>
            <if cond="objAnswer.confirmation == 'no'">
              <clear namelist="new_reservation_type objAnswer"/>

            <else/>
               <var name="next_destination" expr="' '"/>
               <if cond="new_reservation_type == 'plane'">
                  <assign name ="next_destination" expr="'new-plane.vxml'"/>
               <elseif cond="new_reservation_type == 'hotel'"/>
                  <assign name ="next_destination" expr="'new-hotel.vxml'"/>
               <elseif cond="new_reservation_type == 'car'"/>
                  <assign name ="next_destination" expr="'new-car.vxml'"/>
               <elseif cond="new_reservation_type == 'plane and hotel'"/>{
                  <assign name ="next_destination" expr="'new-plane.vxml'"/>
                  <assign name ="doNewHotelRes" expr="true"/>
               }
               <elseif cond="new_reservation_type == 'plane and car'"/>{
                  <assign name ="next_destination" expr="'new-plane.vxml'"/>
                  <assign name ="doNewCarRes" expr="true"/>
               }
               <elseif cond="new_reservation_type == 'hotel and car'"/>{
                  <assign name ="next_destination" expr="'new-hotel.vxml'"/>
                  <assign name ="doNewCarRes" expr="true"/>
               }
               <elseif cond="new_reservation_type == 'plane and hotel and car'"/>{
                  <assign name ="next_destination" expr="'new-plane.vxml'"/>
                  <assign name ="doNewHotelRes" expr="true"/>
                  <assign name ="doNewCarRes" expr="true"/>
               }
               <else/>
                  <assign name ="next_destination" expr="'error.vxml'"/>
               </if>

               <goto expr="next_destination"/>
            </if>
         </filled>
      </subdialog>
   </form>

   <form id="confirmation">
      <var name="caller_response"/>
      <field name="confirmation">
         <prompt>
            I think I heard you say <value expr="caller_response"/>.
            Is that correct?
         </prompt>
         <grammar src=
           "http://grammar.svc.tellme.com/yesno/mss/v2/confirm.grxml"/>

         <catch event="noinput nomatch">
            Sorry. Didn't get that. Please try again.
            <reprompt/>
         </catch>

         <catch event="noinput nomatch" count="3">
            Sorry you're having trouble. Please call back later. Goodbye.
            <exit/>
         </catch>

         <filled>
            <return namelist="confirmation"/>
         </filled>
      </field>
   </form>

   <form id="exit">
      <block>
         <exit/>
      </block>
   </form>

</vxml>

You should run this in the Tellme Studio Scratchpad.

What's next?

In this lesson, we have covered two important VoiceXML mechanisms: events and links. Their addition makes app-root.vxml much more robust.

In the next lesson, Lesson 9, we will look at the <prompt> element in more detail, including the use of the <audio> element to play pre-recorded prompts. We will use Tellme Studio's "record by phone" utility to make recordings of most of the app-root.vxml prompts and then play them in the application in lieu of TTS.