Custom Values in the 4D Ajax Framework

This example demonstrates how to use custom values in the 4D Ajax Framework. The custom value data is sent from the web page (frontend) and recieved in the 4D Database (backend). The 4D Database (backend) then performs a query and sends custom values back to the web page (frontend) which then formats the data and displays it on the web page. The example is in the form of a Car Dealership's internal quoting system; it is designed to let the user configure a vehicle with the options they want, and displays the total price on the page.


Brands

The Brands grid displays a list of car makers in the database.


Models

The Models grid displays a list of car models in the database. The grid is initially loaded with a bogus query to limit the display to zero results. Each time a brand is clicked, the database is queried and the Models grid is updated to display only the models found for the selected brand.


Options

The Options div displays a list of options (and their associated price) found in the database for the car model selected. The div is dynamically altered each time the user clicks on the Brands or Models grid.


Populating Data

The grids are populated with the following configuration:

Brands grid setup:

JavaScript Code:
   var BrandsGrid = new dax_dataGrid('Brands',$('CarBrandDiv'), 0, 0, false);
   BrandsGrid.go(); // go
   BrandsGrid.disableAutoRefresh(); // disable auto refreshing the grid
   BrandsGrid.hideColumn(0); // ID column
   BrandsGrid.setColumnWidth(1,80); // Name
   BrandsGrid.onDataRowClick = BrandClicked; // call BrandClicked on row click
End JavaScript Code

Models grid setup:

JavaScript Code:
   var ModelsGrid = new dax_dataGrid('Models',$('CarModelsDiv'), 0, 0, false);
   ModelsGrid.go(); // go
   ModelsGrid.disableAutoRefresh(); // disable auto refreshing the grid
   ModelsGrid.setColumnWidth(0,110); // Model Name
   ModelsGrid.hideColumn(1); // model ID
   ModelsGrid.newQuery(); //start a new query
   ModelsGrid.addQuery('brandID', '=', 0); // query to get 0 results
   ModelsGrid.runQuery(); // run the query
   ModelsGrid.onDataRowClick = ModelClicked; // set on clicked event
End JavaScript Code

Click Events

The following functions are triggered when the Brands and Models data grid are clicked:

BrandClicked() function definition:

JavaScript Code:
   function BrandClicked(row, column, recordId, fieldReference){
      $totalPrice = 0;
      $lastColorPrice = 0;
      $('CarOptionsDiv').innerHTML = "Please select a model";
      var BrandID = BrandsGrid.getCellValue(row, 0); // get field 0 value of selected row
      ModelsGrid.newQuery(); // start a new query
      ModelsGrid.addQuery('brandID', '=', BrandID); // Query based on BrandID
      ModelsGrid.runQuery(); // run the query
   } // end BrandClicked
End JavaScript Code

ModelClicked() function definition:

JavaScript Code:
   function ModelClicked(row, column, recordId, fieldReference){
      $totalPrice = 0;
      $lastColorPrice = 0;
      $('CarOptionsDiv').innerHTML = "Getting options from backend";
      var ModelID = ModelsGrid.getCellValue(row, 1); // get cell value from field 3 of the selected row
      prepOptions(ModelID);
   } //end ModelClicked
End JavaScript Code

Sending Custom Values to the backend

The ModelClicked() function calls the prepOptions() function that will send the custom values to the backend:

PrepOptions() function definition:

JavaScript Code:
   function prepOptions(modelID){
      myQuery = new dax_query('Models');
      myQuery.clearCustomValues();
      myQuery.addCustomValue('modelID', modelID);
      myQuery.handler = getOptionValues;
      myQuery.runQuery();
   } // end prepOptions
End JavaScript Code

Recieving the Custom Values in the backend

The DAX_DevHook_OnQuery project method will be called upon 4D receiving the custom values:

DAX_DevHook_OnQuery method (custom modification in BOLD):

4D Code:
C_LONGINT($1;$selectionNumber_l;$size_l;$i)
C_POINTER($2;$3;$4;$5;$6;$tableNumbers_p;$fieldNumbers_p;$queryValues_p;$queryComparators_p;$queryLineLinks_p)
C_BOOLEAN($7;$0;$queryInSelection_b;$queryDone_b)
C_TEXT($tableName_t;$selectionName_t;$operator_t;$value_t)
C_POINTER($table_p;$field_p)
` variable declarations for modelID custom value query
C_TEXT($modelID_t;$colorsSpan_t)
C_LONGINT($modelCount_li;$i;$colorCount_li)
C_REAL($basePrice_r)
` end variable declarations for modelID custom values query



$selectionNumber_l:=$1
$tableNumbers_p:=$2
$fieldNumbers_p:=$3
$queryValues_p:=$4
$queryComparators_p:=$5
$queryLineLinks_p:=$6
$queryInSelection_b:=$7


$queryDone_b:=False ` Change to True if you handle the Query


` *** This is an advanced feature - you'll need to be comfortable with building complex
` queries using pointers and text values


` *** To handle the queries yourself
` Convert the passed parameters to tables, fields, and values, and perform the queries


$modelID_t:=DAX_Dev_GetWebVar ("modelID")


If ($modelID_t#"")
  $queryDone_b:=True


  ALL RECORDS([Options])
  QUERY([Options];[Options]modelID=$modelID_t)
  $modelCount_li:=Records in selection([Options])
  ARRAY TEXT(customVarName_at;($modelCount_li+2))
  ARRAY TEXT(customVarValue_at;($modelCount_li+2))


  ALL RECORDS([Models])
  QUERY([Models];[Models]ID=$modelID_t)
  $basePrice_r:=[Models]basePrice


  customVarName_at{1}:="Base Price"
  customVarValue_at{1}:=String($basePrice_r)


  FIRST RECORD([Options])
  For ($i;1;$modelCount_li)
    customVarName_at{($i+1)}:=[Options]name
    customVarValue_at{($i+1)}:=String([Options]price)
    NEXT RECORD([Options])
  End for


  ` compose HTML for colors and send as name="Colors" and value="composedHTML"
  ALL RECORDS([Colors])
  QUERY([Colors];[Colors]modelID=$modelID_t)
  $colorCount_li:=Records in selection([Colors])
  FIRST RECORD([Colors])
  $colorsSpan_t:="<table border=0 cellpadding=0 cellspacing=0 border=0 width=100%>\r"
  $colorsSpan_t:=$colorsSpan_t+"<tr><td rowspan="+String($colorCount_li+1)+" valign=top align=left>Colors:</td></tr>"
  For ($x;1;$colorCount_li)
    If ([Colors]Color="White")
      $colorsSpan_t:=$colorsSpan_t+"<tr><td align=left><input type=\"radio\" id=\"White\" checked=true onclick=\"calcColor(this);\" value=\""+String([Colors]Price)+"\" name=\"color\">"+[Colors]Color+"</td><td align=right>$"+String([Colors]Price)+"</td></tr>\r"
    Else
      $colorsSpan_t:=$colorsSpan_t+"<tr><td align=left><input type=\"radio\" onclick=\"calcColor(this);\" value=\""+String([Colors]Price)+"\" name=\"color\">"+[Colors]Color+"</td><td align=right>$"+String([Colors]Price)+"</td></tr>\r"
    End if
    NEXT RECORD([Colors])
  End for
  $colorsSpan_t:=$colorsSpan_t+"</table>"


  customVarName_at{($modelCount_li+2)}:="Colors"
  customVarValue_at{($modelCount_li+2)}:=$colorsSpan_t


  DAX_Dev_SetCustomVariables (->customVarName_at;->customVarValue_at)


End if


+If (False)
End if


` *** Return True if you have performed the query
` Return False to have 4DAF perform the query
$0:=$queryDone_b
End 4D Code

receiving the Custom Values callback from the backend

The getOptionsValues() function was previously set as the handler for the myQuery object that was used to send the custom values to the backend; this is the fucntion that is called when the data comes back from 4D to the frontend:

getOptionValues() function definition:

JavaScript Code:
function getOptionValues(){
  myCustomValues = myQuery.getCustomValuesFrom4D();
  var len = myCustomValues.length;
  if(len!=0){
    $optionsHTML = "<form><table border=0 cellspacing=0 cellpadding=0 width=100%>\r";
    for(var i = 0; i<len;i++){
      if(myCustomValues[i].name == "Base Price"){
        $basePrice=myCustomValues[i].value;
        $optionsHTML = $optionsHTML + "<tr><td style=\"padding: 3px\">Base Price:</td>\r<td align=right style=\"padding: 3px\">$"+ $basePrice +"</td></tr>\r";
        $optionsHTML = $optionsHTML + "<tr height=1 bgcolor=#000000><td colspan=2></td></tr>\r";
      }else{ // end if / start else
        $name = myCustomValues[i].name;
        $value = myCustomValues[i].value;
        if($name=="Colors"){
          $spanForColors = $value;
        }else{ // end COLOR
          $optionsHTML = $optionsHTML + "<tr>\r";
          $optionsHTML = $optionsHTML + "<td><input type=checkbox value=\""+ $value +"\" ID=\"DaxCarOption\" name=\""+ $name +"\" onclick=\"calcTotal(this);\">"+ $name +"</td>\r";
          $optionsHTML = $optionsHTML + "<td align=right style=\"padding: 3px\">$"+ $value +"</td>\r";
          $optionsHTML = $optionsHTML + "</tr>\r";
        } // end COLOR else
      } // end BASE PRICE else
    } // end for loop
    $optionsHTML = $optionsHTML + "<tr height=1 bgcolor=#000000><td colspan=2></td></tr>\r";
    $optionsHTML = $optionsHTML + "<tr><td valign=top colspan=2 style=\"padding: 3px\"><span ID=\"ColorsDropDown\"></span></td></tr>\r";
    $optionsHTML = $optionsHTML + "<tr height=1 bgcolor=#000000><td colspan=2></td></tr>\r";
    $optionsHTML = $optionsHTML + "<tr><td><b style=\"padding: 3px\">Total Price:</b></td>\r<td align=right style=\"padding: 3px\"><b><div ID=\"TotalPrice\"></div></b></td></tr>\r";
    $optionsHTML = $optionsHTML + "</table>\r</form>";
    $('CarOptionsDiv').innerHTML = $optionsHTML;
    $('ColorsDropDown').innerHTML = $spanForColors;
    $totalPrice = $basePrice;
    $('TotalPrice').innerHTML = "$"+$totalPrice;
  $('White').click();
  } // end IF len # 0
} // end getOptionValues
End JavaScript Code

Calculating the total cost

Each time the user clicks on any of the options, the following functions are used to calculate the total cost:

calcTotal function definition:

JavaScript Code:
function calcTotal(item){
  if(item.checked==true){
    $totalPrice = parseInt($totalPrice) + parseInt(item.value);
    $('TotalPrice').innerHTML = "$"+$totalPrice;
  }else{
    $totalPrice = parseInt($totalPrice) - parseInt(item.value);
    $('TotalPrice').innerHTML = "$"+$totalPrice;
  }
}
End JavaScript Code

calcColor function definition:

JavaScript Code:
function calcColor(item){
  $totalPrice = parseInt($totalPrice) + parseInt(item.value) - parseInt($lastColorPrice);
  $('TotalPrice').innerHTML = "$"+$totalPrice;
  $lastColorPrice = parseInt(item.value); // update the price of the last color used for the next calculation
}
End JavaScript Code