HELP!

Chat Deployment User Interface

Dialog Objects (aka Dialogs) can also be used to create a chat-style user interface for Viabl.ai applications. If Dialogs are not used then the Chat User interface is generated automatically to prompt for all the questions encountered during inference in a scrolling chat-style (scrolling UI) as shown below.

Scrolling UI


To Customise this automatically generated user interface see How do I customise my Chat-based user interface?

Dialogs can be built using one of two alternative editor views; XpertForm or Chat HTML Form.


XpertForm View

For Chat deployment, any Input Controls (e.g. Questions) and Labels added to the XpertForm are simply prompted for sequentially at runtime, ignoring all other XpertForm functionality.

The XpertForm has a very limited set of features for Chat deployment. Any Dialog Button controls used are ignored by Chat-deployed application.

A warning icon is presented on the XpertForm view if Chat is a possible deployment target for the knowledge base.

image title

Dialogs tied to individual Question Objects are ignored in Chat deployed applications.

Chat HTML Form View

Chat HTML Forms are a Chat deployment option which gives the developer complete control over the UI presented to the end user for capturing questions.

HTML forms are a particular view of the dialog object and differ from the XpertForm view in that they are more low-level code oriented.

There are several features specific to HTML forms...

  • Per-Form JavaScript
  • Per-Form HTML
  • Global JavaScript
  • Global CSS

and additional settings...

  • Displayed Objects
  • Captured Questions

Per-Form JavaScript

This is JavaScript associated with an individual form, for example...

let $this = form.$container;
switch(form.mode){
  case "startup":
    // This code should setup your form (called once your custom HTML has been added to the DOM)
    break;
  case "next":
    // This code is called in response to the user clicking the "Next" button
    return true;
}

Note that the per-form JavaScript is called with a form object as the sole parameter. This object has various properties and methods which are used to communicate with the rendering engine.

form object parameter Description
$container A jQuery object which references the div element which is used to wrap the per-form HTML.
mode A string which represents the current state. Possible values are "startup" and "next". See the section below entitled Form Modes for more information.
eachCaptured A method which can be used to iterate over the Captured Questions specified for the form. See the section below entitled Captured Question Iterator for more information.
getDefinition A method which is used to retrieve the definition of a Captured Question specified for the form.
getValue A method which retrieves the current value for a specific Displayed Object or Captured Question.
setSelection A method which allows the custom JavaScript to pass a selected value of a Captured Object back to the engine.
next A method which allows programmatically to perform a next (same as a user clicking the next button).
back A method which allows programmatically to perform a back (same as a user clicking the back button).
backValid A method which returns whether the back functionality is valid for the current HTML form (i.e. not the first question).

Form Modes (form.mode)

form.mode Description
"startup" Called once your per-form HTML has been added to the DOM. Use this opportunity to perform operations such as populating your custom DOM and attaching events to your elements.
"next" Called in response to the user clicking the "next" button (or programmatically calling form.next()). You should return true to allow progression or false to stop progression (e.g. if the user has not yet selected a value).

Captured Question Iterator (form.eachCaptured)

This method allows you to supply a callback function which is called for each Captured Question with an object representing its definition.

This is super useful if you would like to do a Generic renderer. A generic rendered does not need to be aware of what is being captured in this specific form, it just needs to know how to render the different question types! This allows you to change your knowledge, add Captured Questions etc. without having to modify any JavaScript or HTML!

Let's look at some sample code...

switch(form.mode){
    case "startup":
        form.eachCaptured(function(capturedDefinition) {
            switch (capturedDefinition.type) {
                case "list":
                    myRender_createDropdown(form, capturedDefinition);
                    break;
            }
        });
        break;
  	case "next":
        // more code goes here
    	return true;
}

In the above example, the callback function is called for each Captured Question passing in a capturedDefinition object. This object contains everything required to custom-render a question.

We'll investigate the captured definition object in another section (see form.getDefinition) but the above example just uses the type property which contains the question's type. If it is of type "list" then we use a custom renderer to add markup (and events) to the DOM. This custom renderer is usually contained in the HTML Form Global JavaScript (accessible from the toolbar in Viabl.ai). Again, we'll take a good look at this code in it's own section.

Captured Question Definition (form.getDefinition)

You can use this method to retrieve the definition of a Captured Question (which has been specified in Viabl.ai)...

let $this = form.$container;
switch(form.mode) {
  case "startup":
    let capturedDefinition = form.getDefinition("MyListQuestion");
    if (!capturedDefinition) {
        alert("woops, I forgot to add MyListQuestion to the captured questions");
    } else if (capturedDefinition.type != "list") {
        alert("woops, MyListQuestion is not a list question");
    } else {
        let dropDownValues = [];
        for (let c = 0; c < capturedDefinition.values.length; c++) {
            let aValue = definition.values[c];
            dropDownValues.push({
                name: aValue.display ? aValue.display : aValue.name,
                value: aValue.name,
                disabled: !aValue.valid
            });
        }
        // do something with our new "dropDownValues" array
    }
    break;
}

Looking at the code, we start by retrieving the definition of a specific question...

let capturedDefinition = form.getDefinition("MyListQuestion");

Assuming we added "MyListQuestion" to the HTML form's Captured Questions, the getDefinition method will return an object defining the structure and definition of the Viabl.ai question. If the supplied question name is not captured, the method will return null.

Once we have done a couple of sanity checks, because this is a list question, we'll loop over each list value and setup a nice array for using later on. Each element of our new array will contain...

  • Text to display for this value (this is optional so we have the fallback of the internal value)
  • The actual (internal value) for setting back
  • Whether this value is selectable or not

n.b. the reason for having separate display & internal values is so we can easily change the internals and translate the display without breaking the knowledge

A full Captured Question Definition object structure for a List Question might look like this...

{
    "name": "MyListQuestion",
    "display": "What is the value you wish to supply?",
    "type": "list",
    "multiselect": false,
    "allowblank": false,
    "baseclass": "List_Question",
    "values": [
        {
            "name": "Director",
            "display": "Director",
            "valid": true
        },
        {
            "name": "SnrMgr",
            "display": "Senior Manager",
            "valid": true
        },
        {
            "name": "JunMgr",
            "display": "Junior Manager",
            "valid": true
        }
    ]
}

At the top level the definition properties represent...

Property Description
name The internal name of the question. This is required for passing the question's answer via the form.setSelection method
display The text to display for the question. This relates to the Viabl.ai [description] property of the question. n.b. This property can be an empty string in which case you may choose to display the name property
type The type of the question. Possible values are "list", "number", "date", "text"
values For list questions, this is a list of possible answers
multiselect For list questions, this indicates whether multiple selections are allowed
allowblank Can this question be skipped (i.e. no value selected)?
baseclass The Viabl.ai base class of the question

And looking at the values array (only applicable for list questions)...

Property Description
name The internal name of the answer. This is required for passing the question's answer via the form.setSelection method
display The text to display for the question. This relates to the XpertRule [value description] property of the question. n.b. This property can be an empty string in which case you may choose to display the name property
valid Is this a valid value? The validity of values is governed by the imposition of Viabl.ai constraints. A front end might choose to exclude this value or disable it

A Captured Question Definition object structure for a Numeric Question might look like this...

{
    "name": "Cost",
    "display": "What was the daily cost of the hotel?",
    "type": "number",
    "allowblank": false,
    "baseclass": "Numeric_Question",
    "decs": 2,
    "minimum": 0,
    "maximum": 1000
}

Whilst most of these top-level properties are shared with List Questions, there are a couple of additions specific to Numeric Questions...

Property Description
decs The number of decimal places to capture / display
minimum [optional] If specified by the Viabl.ai developer, this is the minimum allowed value
maximum [optional] If specified by the Viabl.ai developer, this is the maximum allowed value

Date Questions are very similar (n.b. dates are passed as strings with a format of yyyy-mm-dd)...

{
    "name": "DOB",
    "display": "What is your date of birthday?",
    "type": "date",
    "allowblank": false,
    "baseclass": "Date_Question",
    "minimum": "1900-01-01",
    "maximum": "2020-10-27"
}

And for completeness, Text Questions look like this...

{
    "name": "applicantName",
    "display": "What is your name?",
    "type": "text",
    "allowblank": false,
    "ispassword": false,
    "baseclass": "Text_Question"
}
Property Description
ispassword The input should have password-type security?

Get a Displayed and/or Captured Question's Current Value (form.getValue)

Questions which are specified in the Displayed Objects and Captured Questions have their current value's available for use by your HTML form. Some possible uses of this feature are...

  • Display a calculated value along with your form
  • Use a calculated value to restrict the input in some way
  • Assign a default to a Captured Question prior to the form

So form.getValue is generally used in the "startup" mode. An example of one possible use case is shown below (to display the "ClaimantName" along with the HTML form)...

switch(form.mode){
    case "startup":
        $("#myForm_name").text(form.getValue("ClaimantName"));
        break;
}

With matching HTML form contents of...

Hello <span id="myForm_name"></span>, please fill in the following options...

So as you can see from the above example, form.getValue("ClaimantName") is used to get the current value of the "ClaimantName" Viabl.ai object.

n.b. If the object is not specified in the Displayed Objects or Captured Questions then the return value from form.getValue will be null

Supply a Value for a Capture Question (form.setSelection)

Once a value has been supplied (and generally in response to the user clicking the "Next" button) you should reflect the captured value back to the engine via the form.setSelection method...

let $this = $(form.container);
switch(form.mode){
    case "startup":
        // code goes here
        break;
    case "next":
        let selectedValue = form.$captured.find("select").val();
        form.setSelection('capturedGrade', selectedValue);
        return true;
}

So to look more closely at the "next" mode processing. We first get the value of the single select element from the HTML form...

let selectedValue = form.$captured.find("select").val();

The right-hand side of the assignment utilises the jQuery library (which is available as default to your HTML forms). For a nice overview of the jQuery library, w3schools has extensive help on the subject

Notice the return true; in the above sample code. This tells the engine that it's OK to proceeds to the next question. However if it's not OK to proceed (i.e. if the user has not yet chosen a value) your script should return false; for example...

case "next":
    let selectedValue = form.$captured.find("select").val();
    if (!selectedValue) {
        return false;
    }
    form.setSelection('capturedGrade', selectedValue);
    return true;

Programmatically Proceed to Next (or Previous) Question (form.next and form.back)

Sometimes you might want your UI to do an automatic "Next" when the user performs an action (e.g. clicks a particular element rendered on your form). Let's look at a small example which renders three <div> elements and, when the user clicks one of them, sets a relevant selection and proceeds to the next question.

Example HTML

<div class="my-form-click" my-value="Director">Click here to set Director</div>
<div class="my-form-click" my-value="SnrMgr">Click here to set Senior Manager</div>
<div class="my-form-click" my-value="JnrMgr">Click here to set Junior Manager</div>

Example JavaScript

switch(form.mode){
    case "startup":
        form.$container.find(".my-form-click").click(function() {
            let $clickedDiv = $(this);
            let clickedDivValue = $clickedDiv.attr("my-value");
            form.setSelection("Grade", clickedDivValue);
            form.next();
        });
        break;
    case "next":
        return true;
}

The above JavaScript first attaches code to the click event of all the <div> on the form with the my-form-click css class.

When the user clicks one of the divs, we create a local variable to associate a jQuery object with the clicked element...

let $clickedDiv = $(this);

We then create another local variable to hold the value of the my-value element attribute of the clicked div...

let clickedDivValue = $clickedDiv.attr("my-value");

Next, we set the selected value back in the engine (in this case, we are capturing the Viabl.ai "Grade" question)...

form.setSelection("Grade", clickedDivValue);

Finally, we tell the engine to progress to the next question...

form.next();

You should also notice that the "next" mode operation is still called to leave this form so (assuming your form is valid) you should just return true;

case "next":
    return true;

Additionally, you might also want to provide a method for the user to go "back". This can be instigated from your code via...

form.back();

Note: In some circumstances the back functionality is not valid (i.e. if your form is the first question). You can check whether back is valid (or not) as follows...

let renderMyBackButton = form.backValid();

Hiding the system buttons

If you are implementing your own "next" functionality, it's a good idea to hide the system "next" button. You can do this via the showButtons HTMLform Option available from the Viabl.ai editor toolbar.

Writing A Generic Render

In the form.eachCaptured section, we hinted that you could write a generic JavaScript function (which can be defined in the Global HTML Form JavaScript editor). This could dynamically render any encountered question and hence reducing the per-form HTML and JavaScript. Another advantage is that all your hard work on a slick UI for a particular question type could be used for all instances of that type throughout your application (and any future changes limited to a single place).

Worked Example

Let's look at a full example of how to develop and use a custom renderer which allows the select of a List Type Question via a fomantic.ui dropdown control. (n.b. the Fomantic.ui library is included by default for your HTML forms to utilize. See the online documentation for more information)

For this example we are not going to use any Per-Form HTML. Let's look at the HTML Form JavaScript...

switch(form.mode){
    case "startup":
        form.eachCaptured(function(def) {
            switch (def.type) {
                case "list":
                    myRender_createDropdown(form, def);
                    break;
            }
        });
        break;
  	case "next":
        let allStored = true;
        form.eachCaptured(function(def) {
            switch (def.type) {
                case "list":
                    let stored = myRender_storeDropdownValue(form, def);
                    if (!stored) {
                        allStored = false;
                    }
                    break;
            }
        });
    	return allStored;
}

So the key component of this technique is the use of the form.eachCaptured method. In the "startup" section, we iterate through each specified Captured Question and (if it's a List Type Question) we call a custom Global JavaScript function myRender_createDropdown passing the form object and the captured question's definition. We'll look at myRender_createDropdown later in this section.

In the "next" section, we again iterate through the Captured Questions and call a custom Global JavaScript function myRender_storeDropdownValue passing the form object and the captured question's definition. This function returns true if the value is successfully stored (e.g. the user has supplied a value for it) or false if there is an issue.

The slight complexity of this code (all the allStored stuff) is that you need to return false in response to the "next" mode if ANY inputs do not store correctly.

We'll just dump out Global HTML form and JavaScript here for your enjoyment...

function myRender_createDropdown(form, def) {
    // markup required for the semantic-ui dropdown
    let $form = $('<form class="ui form myrender-form" att-name="' + def.name + '">' +
    	'<div class="field">' +
      		'<label>name goes here</label>' +
      		'<div class="ui search selection dropdown">' +
      			'<input type="hidden">' +
      			'<i class="dropdown icon"></i>' +
      			'<div class="default text">Select a value...</div>' +
      			'<div class="menu">' +
      			'</div>' +
      		'</div>' +
      	'</div>' +
    '</form>').appendTo(form.$container);
  
    // populate the field's input label with the question name
	$form.find("label").html(def.name);
  
    let $dropdown = $form.find(".ui.dropdown");
    $dropdown.dropdown({}); * semantic-ui dropdown initialization    https:*semantic-ui.com/modules/dropdown.html
  
    // populate the dropdown with the Question's values
    let ddv = [];
    for (let c = 0; c < def.values.length; c++) {
        ddv.push({
            name: def.values[c].display ? def.values[c].display : def.values[c].name,
            value: def.values[c].name,
            disabled: !def.values[c].valid
        });
    }
    $dropdown.dropdown("change values", ddv);
  
    // possible default question value
    let currentVal = form.getValue(def.name);
    if (currentVal !== null) {
        $dropdown.dropdown("set selected", currentVal);
    }
}

function myRender_storeDropdownValue(form, def) {
    // lookup the semantic-ui form element based on the att-name attribute of the captured question
    let $form = form.$container.find(".myrender-form[att-name=" + def.name + "]");
    // find the dropdown on the semantic-ui form
    let $dropdown = $form.find(".ui.dropdown");
    // get the current selected value
    let selValue = $dropdown.dropdown("get value");
    // check that a value has actually been selected
    if (!selValue) {
        $dropdown.addClass("error");
        return false; // empty
    }
    // all is good, set the value back in the engine
    $dropdown.removeClass("error");
    form.setSelection(def.name, selValue);
    return true; // ok
}

Custom Question Properties and the baseclass Definition Property

When writing a dynamic (or generic) renderer, you often require more information than that which is encompassed within the default Viabl.ai question properties. This can be circumvented by utilizing Custom Base Classes and/or Custom Question properties.

Custom Base Class

The concept behind using Custom base classes is that the renderer can work off the specific base class (rather than the question type) to determine how to render the question, e.g. you could have a "Graphical_List" base class which is a list object but is displayed as images to the end user (rather than a textual list of options).

Custom Question Properties (via Object Structure Maintenance)

The "Object Structure Maintenance" Object Catalog menu option allows you to add custom Object Properties, and for list & boolean questions also List Value Properties. If you add a new property and check the "include in xpertrule.getValues" box then this property will be included in the captured question definition.

{
    "name": "Department",
    "display": "",
    "type": "list",
    "multiselect": false,
    "allowblank": false,
    "baseclass": "List_Question",
    "my_object_prop": "This is an example of a custom object property",
    "values": [
        {
            "name": "Engineering",
            "display": "Engineering",
            "valid": true,
            "my_list_value_prop": "This is an example of a custom list value property"
        },
        {
            "name": "Sales",
            "display": "Sales",
            "valid": true,
            "my_list_value_prop": "Custom property for the sales value"
        },
        {
            "name": "Marketing",
            "display": "Marketing",
            "valid": true,
            "my_list_value_prop": "Custom property for the marketing value"
        }
    ]
}

Editing the Main Chat Page

To take it to the next-level, you also have the ability to completely customize the entire chat front-end! This is achieved via the "Chat HTML" editor on the "Knowledge-Base Settings" dialog.

The down-side of this strategy is that any future enhancements to the chat front-end in the Viabl.ai Platform WILL NOT be reflected in your deployed knowledge-base!

On This Page