ActiveWAFL Crash Course

The WAFL Cones Ice Cream Shop, Version 1.0

5.2 Create a client-side Controller

Now we'll create the client-side controllers. Just like we did with the server-side, we'll start with the main landing page.

For the main landing page, we're actually going to delete some code from the default client-side controller created by Utensils.
Our main landing page doesn't need to do anything in the client-side controller.

Edit the file WaflCones/Controllers/LandingPage.js and replace it's contents with the folllowing.

WaflCones/Controllers/LandingPage.js: client-side controller for the main landing page.
  1. Namespace("WaflCones.Controllers");
  2.  
  3. //all client-side controllers inherit from ControllerBase
  4. WaflCones.Controllers.LandingPage = DblEj.Mvc.ControllerBase.extend({
  5.     //in ActiveWafl, traditional-oo style classes in javascript all have an init() function which is like the constructor
  6.     init: function()
  7.     {
  8.     },
  9.    
  10.     //for every action in the server-side controller, there can be a corresponding client-side action
  11.    DefaultAction: function()
  12.    {
  13.     }
  14. });

We'll go in the same order that we created the server-side controllers in.
So, next we'll do the code for the new sale client-side controller and the administration menu client-side controller.

Their code follows.

WaflCones/Controllers/Sale.js: client-side controller for the new sale page.
  1. Namespace("WaflCones.Controllers");
  2.  
  3. /**
  4.  * all client-side controllers inherit from ControllerBase
  5.  */
  6. WaflCones.Controllers.Sale = DblEj.Mvc.ControllerBase.extend({
  7.     /**
  8.      * called on construction
  9.      * @returns {undefined}
  10.      */
  11.     init: function()
  12.     {
  13.         /**
  14.          * private method to recalculate values based on the current form input
  15.          * @returns {boolean} true on success
  16.          */
  17.         this._recalculatePrices = function()
  18.         {
  19.             var totalPrice = 0;
  20.            
  21.             //go through each ItemWeight input, find the corresponding inputs
  22.             //and use their values to set the price per ounce and total price for that line.
  23.             //also accumulate the price of each line item to determine the total.
  24.             //Note: we'll recheck the values on the server-side.
  25.             $$class("ItemWeight").OnEach
  26.             (
  27.                 function(elem)
  28.                 {
  29.                     var saleItemNumber = parseInt(elem.GetData("sale-item-number"));
  30.                     var weight = parseFloat(elem.Get_Value());
  31.                     if (isNaN(weight))
  32.                     {
  33.                         weight=0;
  34.                     }
  35.                     var pricePerOunce = 0;
  36.                     $$q(".FlavorButton[data-sale-item-number='"+saleItemNumber+"']").OnEach
  37.                     (
  38.                         function(radioButton)
  39.                         {
  40.                             if (radioButton.checked)
  41.                             {
  42.                                 pricePerOunce = parseFloat(radioButton.GetData("price-per-ounce"));
  43.                             }
  44.                         }
  45.                     );
  46.                     var price = weight * pricePerOunce;
  47.                     totalPrice += price;
  48.                     $("PricePerOunce_"+saleItemNumber).SetText(pricePerOunce.toFixed(2));
  49.                     $("SaleItemPrice_"+saleItemNumber).SetText(price.toFixed(2));
  50.                 }
  51.             );
  52.             $("TotalPrice").SetText(totalPrice.toFixed(2));
  53.             return true;
  54.         };
  55.     },
  56.    
  57.    DefaultAction: function()
  58.    {
  59.         //add a line item when someone clicks the "Add Item" button.
  60.         //This utilizes client-side templating.
  61.         //If you look in the Sale.tpl you'll notice an HTML element with a <template> tag.
  62.         $("AddItemButton").AddClickHandler(
  63.             function(event)
  64.             {
  65.                 var saleItemIdx = $$q(".SaleItem").length+1;
  66.                
  67.                 //Render(): an element is rendered from the template then it is added to the DOM
  68.                 $("SaleItemTemplate").Render($("SaleItems"),{"SaleItemNumber": saleItemIdx});
  69.                
  70.                 //setup event listeners for the new line item's Ounces inputs
  71.                 $("Ounces_"+saleItemIdx)
  72.                     .AddChangeHandler(
  73.                         function(event)
  74.                         {
  75.                             this._recalculatePrices();
  76.                         }.Bind(this))
  77.                     .AddKeyUpHandler(
  78.                         function(event)
  79.                         {
  80.                             this._recalculatePrices();
  81.                         }.Bind(this));
  82.  
  83.                 //setup event listeners for the new line item's flavor radio buttons
  84.                 $$q(".FlavorButtonLabel[data-sale-item-number='"+saleItemIdx+"']").AddClickHandler(
  85.                     function()
  86.                     {
  87.                         this._recalculatePrices();
  88.                     }.Bind(this));
  89.             }.Bind(this));
  90.  
  91.         //setup event listeners for the amount tendered text box
  92.         $("AmountTendered").AddChangeHandler
  93.         (
  94.             function(event)
  95.             {
  96.                 if (parseFloat($("AmountTendered").GetValue()) < parseFloat($("TotalPrice").GetText()))
  97.                 {
  98.                     $("AmountTendered").SetValidationError("Cannot be less than the grand total");
  99.                 } else {
  100.                     $("AmountTendered").ClearValidationError();
  101.                 }
  102.             }
  103.         );
  104.  
  105.         //Submit the form to an API handler via ajax.
  106.         //You'll need to add the ProcessSale call to ApiCalls.syrp.
  107.         $("SaleForm").AddSubmitHandler
  108.         (
  109.             function(event)
  110.             {
  111.                 DblEj.EventHandling.Events.PreventDefaultEvent(event);
  112.  
  113.                 //async form submit
  114.                 ____("ProcessSale", $("SaleForm"),
  115.                     function(saleResult,sendToken)
  116.                     {
  117.                         $("ChangeDue").SetText(saleResult.ChangeDue);
  118.                         $("SaleCompletedButton").Enable();
  119.                         $("PostSalePopup").ShowDialog("Sale Completed");
  120.                     });
  121.                    
  122.                 //disable sale form elements so it doesn't get submitted again
  123.                 $$q("input, button, a").OnEach
  124.                 (
  125.                     function(elem)
  126.                     {
  127.                         elem.Disable();
  128.                     }
  129.                 );
  130.             }
  131.         );
  132.     }
  133. });

Almost finished!
We need to do a couple of things to finish the application.

  • Define and create the ProcessSale API Handler
  • Create the Presentation Templates, Presentation Stylesheets, and Controllers for the remaining pages.
    There are 11 pages total.
    Now that you have grasped the basic concepts, rather than make you copy and paste the code for remaining pages, we'll provide them in an easy single download at the end of the course.

Next: 6.1: Create the API Handler to process a sale