Using Subdialogs

A subdialog is a self-contained, reusable VoiceXML component. To maintain encapsulation, subdialogs do not have access to the execution context of their user. The execution context includes the variables defined in the calling dialog as well as the resources defined in the application root document pointed to by the VoiceXML document containing the calling dialog. This tutorial explains the following:

You can share data with a subdialog in two ways:

  • By passing parameters to the subdialog using the param element.
  • By using a server-side framework such as Perl CGI, Active Server Pages (ASP), or Java Server Pages (JSP).

The param element supports name, value, and expr attributes. The name attribute names the parameter. The value attribute defines a static value for the named parameter. Alternatively, you can set the expr attribute to any valid JavaScript expression to define the value for the parameter at run-time.

The following example passes the parameters "iAnswer" and "bPrompt" to a subdialog contained in the VoiceXML document "picknumber.vxml". The subdialog declares variables with the same name. When the subdialog is invoked, the VoiceXML interpreter initializes the variables with the values of the corresponding parameters.

<vxml version="2.1">
  <var name="iAnswer" expr="Math.floor(Math.random(1) *100)" />
  <var name="bPrompt" expr="true" />

  <form id="call_pick_number">
    <subdialog name="oResult" src="picknumber.vxml">
      <param name="iAnswer" expr="document.iAnswer" />
      <param name="bPrompt" expr="document.bPrompt" />

      <filled>
        <if cond="oResult.iRetval">
          <prompt>You should head to Vegas</prompt>
          <exit />
        <else />
          <if cond="oResult.iPick &gt; oResult.iAnswer">
            <prompt>Try a number lower than 
               <value expr="oResult.iPick" />
            .</prompt>
          <else />
            <prompt>Try a number higher than 
               <value expr="oResult.iPick" />
            .</prompt>
          </if>

          <assign name="bPrompt" expr="false" />
          <goto next="#call_pick_number" />
        </if>
      </filled>
    </subdialog>
  </form>
</vxml>

<!-- picknumber.vxml -->
<vxml version="2.1">
  <form>
    <var name="iAnswer" />
    <var name="bPrompt" />
    <var name="iRetval" expr="0" />

    <field name="iPick">
      <grammar>NATURAL_NUMBER_THRU_99</grammar>

      <prompt count="1">
        <if cond="bPrompt">
          <prompt>Pick a number</prompt>
        </if>
      </prompt>

      <prompt count="2">
        <prompt>Please pick a number</prompt>
      </prompt>

      <catch event="noinput nomatch">
        <prompt>Sorry. I didn't get that.</prompt>
        <reprompt />
      </catch>

      <filled>
        <if cond="iPick == iAnswer">
          <assign name="iRetval" expr="1" />
        </if>
        <return namelist="iAnswer iPick iRetval" />
      </filled>
    </field>
  </form>
</vxml>

Use the return element to return data to the user of the subdialog. You can return an event to the user using the event attribute, or you can return data to the user using the namelist attribute. The event and namelist attributes are mutually exclusive. The namelist is a space-delimited set of variables defined within the subdialog. Each variable in the list becomes a property of an object referenced by the value of the name attribute of the subdialog element. You reference the object variable from a filled handler specified as a child of the subdialog element.

In the following example, the value "oCC" names a variable that references an object with properties "sNumber" and "dExpDate". A proper implementation of the subdialog would interact with the user to retrieve the credit card number and the expiration date. In this example, the subdialog initializes sNumber with a 16-character string and dExpDate with a reference to an ECMAScript date object that stores the current date.

<vxml version="2.1">
  <form>
  <subdialog name="oCC" src="#get_ccinfo">
    <filled>
      <log>card number is 
      <value expr="oCC.sNumber" />
      </log>

      <log>expiration is 
      <value
      expr="(oCC.dExpDate.getMonth()+1) + ' ' + oCC.dExpDate.getFullYear()" />
      </log>
    </filled>
  </subdialog>
  </form>

  <form id="get_ccinfo">
    <var name="sNumber" expr="'5424000000000000'" />
    <var name="dExpDate" expr="new Date()" />
    <return namelist="sNumber dExpDate" />
  </form>
</vxml>

To catch an event thrown by the subdialog, add one or more catch elements to the subdialog element, and set the event attribute of each handler to the name of the events returned by the subdialog. If you don't specify a catch element that matches an event thrown by a subdialog, the VoiceXML interpreter will halt your application when the event is thrown. To prevent this from occuring, define a default handler as a child of the subdialog element or at dialog, document, or application scope. If the subdialog doesn't return events, use a filled element as a child of the subdialog element to handle any values returned by the subdialog.

You can pass data to a subdialog by implementing the subdialog using a server-side technology such as Perl or Active Server Pages (ASP) and by passing the data as part of a query string or via the namelist attribute of the subdialog element. This is particularly useful if you need to perform some back-end validation of data supplied by the user.

In the following example, iStudentID and iStudentPIN are defined at document scope. The dialog "get_id" collects the student ID and stores the value in the variable iStudentID. The dialog "get_pin", collects the student PIN and stores the value in the variable iStudentPIN. Transfer is controlled to the dialog "validate_proxy", and it uses the namelist attribute of the subdialog element to pass the student ID and student PIN to a subdialog generated by a Perl script.

The Perl script uses a CGI module to pull the student ID and PIN out of the HTTP request. It then calls a hypothetical helper function ValidateStudent that performs a database lookup to determine whether or not the student ID and PIN are valid. If so, it stores the string "event.valid" in a Perl variable. If not, the value "event.invalid" is stored. Finally, the script generates some VoiceXML that gets sent back to the VoiceXML interpreter including a return element which includes the result of the validation. When control is returned to the validate_proxy dialog, the catch elements within the subdialog element are evaluated. If the Perl script returns "event.valid", the VoiceXML interpreter stores the student ID and PIN in variables with application scope and navigates to the dialog stored in the application variable gsNextDialog. If the Perl script returns "event.invalid", the VoiceXML interpreter is instructed to throw a user-defined event "event.retry_login". The handler for this event, defined at documemnt scope, does the following:

  • Informs the user of the validation error
  • Increments a retry counter defined at document scope
  • Checks to ensure the counter has not reached the predefined limit defined at application scope
  • Navigates back to the dialog that collects the student ID if the counter has not been exceeded, or throws another user-defined event, "event.login_retries_exceeded", defined at application scope. The handler for this event, not shown, transfers the user to an attendant using the transfer element
<!-- login.vxml -->
<vxml version="2.1" application="tellmeu_root.vxml">
  <var name="iStudentID" />
  <var name="iStudentPIN" />
  <var name="iRetries" expr="1" />

  <catch event="event.retry_login">
    <prompt>Invalid i d or password</prompt>
    <assign name="iRetries" expr="iRetries+1" />
    <if cond="iRetries == giMaxLoginRetries">
      <throw event="event.login_retries_exceeded" />
    </if>
    <goto next="#get_id" />
  </catch>

  <form id="get_id">
     <!-- retrieve the student's id -->
     <!-- then navigate to #get_pin -->
  </form>

  <form id="get_pin">
     <!-- retrieve the student's pin -->
     <!-- then navigate to #validate_proxy -->
  </form>

  <form id="validate_proxy">
    <subdialog src="validate_id.pl" namelist="iStudentID iStudentPIN">
      <catch event="event.valid">
        <!-- the id and PIN were valid. Proceed. -->
        <assign name="giStudentID" expr="iStudentID" />
        <assign name="giStudentPIN" expr="iStudentPIN" />
        <goto expr="gsNextDialog" />
      </catch>

      <catch event="event.invalid">
        <!-- the user id or password was invalid.
             Inform the user; try again. 
        -->
        <throw event="event.retry_login" />
      </catch>

      <catch event="event.error">
        <!-- an error occurred -->
        <throw event="event.abort" />
      </catch>
    </subdialog>
  </form>
</vxml>

#!/usr/local/bin/perl -w
# --------------
# validate_id.pl
# --------------
use CGI;
$o = new CGI;
$id = $o->param("iStudentID");
$pin = $o->param("iStudentPIN");
# Determine whether or not the student id/pin is valid.
$bValid = ValidateStudent($id, $pin);
$sEvent = ($bValid ? "event.valid" : "event.invalid");
print "Content-Type: text/xml\n\n";
print <<"EOF";
<vxml version="2.1">
   <form>
   <block>
      <return event="$sEvent"/>
   </block>
   </form>
</vxml>
EOF

[24]7 Inc.| Terms of Service| Privacy Policy| General Disclaimers