T3PO (TRU 3-Party Orchestrator)

  1. AngularJS
  2. Data binding supported by AngularJS
    • View-input elements to models (, i.e., internal variables), and then the models to view-output.
  3. Drawbacks of AngularJS
    • Slow initialization
      • Models are defined in view-input elements, and models are used in view-outputs (, i.e., expressions).
      • Models and expressions need to scanned through DOM at the initialization stage. It could take long time, especially when web programs are big.
    • One directional two way data binding
    • Poor modularization - mixed coding style
    • Not clear interaction between AngularJS and legacy JS - How to pass JS variables to AngularJS scope? How to pass AngularJS scope variables to JS?
    • Complex concept of service
    • No 3-way data binding
    • Simply put, too much complicated and poor performance
  4. Negative voices

  5. T3PO
    • 3-way data binding -
    • Simple description of data binding using ids of view-inputs and view-outputs. Not anything like ng-model, ng-bind, ng-init, ng-app, ng-controller, ... Here is an example of T3PO coding.
      window.addEventListener('load', function() {
          // Description of models
          var models = {  // All the properties are optional.
              't3pom1': { initial: 3 },
              't3pom2': { 
                   initial: 7,
                   tojs: testfromt3po  // To JS code; This will be called whenever this model is changed.
                                       // This function is a user defined function in the same file.
              },
              't3pom4': { initial: 'USA' },
              't3pom5': { initial: ['3540', '4620'] },  // Multiple value model; E.g., the inputs of checkbox type and the select element with multiple
              't3pom7': {},
              't3pom8': {},
              't3pom12': { initial: ['VW', 'Hyundai'] },
              't3pom13': { initial: 'like Object.observe()', tojs: testfromt3po13  }  // Not bound to any View-input
          };
          // Description of View-inputs
          var view_inputs = [  // event is optional. The default event is change.
              { id: 't3poi1', model: [ 't3pom1' ] },
              { id: 't3poi2', model: [ 't3pom2' ] },
              { id: 't3poi3', model: [ 't3pom1' ], event: 'keyup' },
              { id: 't3poi4', model: [ 't3pom4' ] },  // Radio buttons with the same name are all bound to one model
              { id: 't3poi5', model: [ 't3pom4' ] },
              { id: 't3poi6', model: [ 't3pom4' ] },
              { id: 't3poi9', model: [ 't3pom5' ] },  // Checkboxes with the same name are all bound to one model
              { id: 't3poi10', model: [ 't3pom5' ] },
              { id: 't3poi11', model: [ 't3pom5' ] },
              { id: 't3poi7', model: [ 't3pom7' ] },
              { id: 't3poi8', model: [ 't3pom8' ] },
              { id: 't3poi12', model: [ 't3pom12' ] }
          ];
          // Description of View-outputs
          var view_outputs = [
              { id: 't3poo1', model: [ 't3pom1', 't3pom2' ], expr: "'The result is ' + (Number(T3PO.t3pom1) + Number(T3PO.t3pom2)) + '.'" },  // For each model, 'T3PO.' should be attached.
              { id: 't3poo2', model: [ 't3pom7' ], expr: "T3PO.t3pom7 + ', external var y=' + y" }, 
              { id: 't3poo8', model: [ 't3pom8' ], expr: "T3PO.t3pom8" },
              { id: 't3poo9', model: [ 't3pom4' ], expr: "T3PO.t3pom4" },
              { id: 't3poo10', model: [ 't3pom5' ], expr: "T3PO.t3pom5" },
              { id: 't3poo12', model: [ 't3pom12' ], expr: "T3PO.t3pom12" },
              { id: 't3poo13', model: [ 't3pom13' ], expr: "T3PO.t3pom13" }
          ];
      
          // Create the Tru3PO object, and use it
          t3po = new Tru3PO(models, view_inputs, view_outputs);
          ...
          t3po.update('t3pom2', document.getElementById('fromjs').value);  // t3pom2 will be changed with other related inputs and outputs
          t3po.update('t3pom2', t3po.get('t3pom2') + document.getElementById('fromjs').value);
          ...
      });
      
      function testfromt3po() { ... };  // Invoked for t3pom2, when t3pom2 is changed.
      function testfromt3po13() { ... }; 
      
    • T3PO API
      • Library: //cs.tru.ca/~mlee/T3PO/t3po_rev3.lib.js
      • Tru3PO(models, view_inputs, view_outputs): Object constructor with three arguments
      • .update(model_name, new_value): Update the model with the new value
      • .get(model_name): Return the value stored in the model
      • models - Object of models, i.e., internal variables
        • { ..., model_name: { initial:initial_value, tojs: functiontocall }, ... }, where initial_value can be a linear array and functiontocall is a user defined javascript function that will be called when the model is changed.
        • In the above, initial and tojs are optional.
      • view_inputs - Object that explains what input HTML elements use what models
        • { ..., {id: id_of_input_element, model: [ model_name, ... ], event: event_name}, ... }
        • In the above, event is optional and it can be a linear array of multiple event names.
      • view_outputs - Linear array that explains what HTML elements use what models
        • [ ..., { id: id_of_output_element, model: [ model_name, ... ], expr: "... (Number(T3PO.model_name) ... T3PO.model_name ..." }, ... ]
    • Features of T3PO
      • Separation of T3PO code from HTML
      • Descriptive data binding
      • No lagged initialization
      • Use of any event for triggering View-inputs
      • 3-way data binding
      • Simple interaction between T3PO and JS, eventually server-side code
      • Easy use of JS variables and functions in T3PO
      • Easy API
      • Extendible
    • Here is a simple example.
      <script src = 'https://cs.tru.ca/~mlee/T3PO/t3po_rev3.lib.js'></script>
      <p>
          Number: <input id='ix'></input><br>
          Number: <input id='iy'></input><br>
          Sum: <span id='ox'></span>
      </p>
      <button id='tr1-button'>Add 5 to the first number</button>
      
      <script>
          var models = {'x':{initial:3}, 'y':{tojs:updated}};
          var inputs = [{id:'ix', model:['x'], event:['keyup']}, {id:'iy', model:['y']}];
          var outputs = [{id:'ox', model:['x','y'], expr:"'The sum is ' + (Number(T3PO.x) + Number(T3PO.y)) + '.'"}]; 
          var t3po = new Tru3PO(models, inputs, outputs);
          // for testing
          $('#tr1-button').click(function() {
              t3po.update('x', Number(t3po.get('x')) + 5);  // from-data-to-view binding
          });
          function updated() { alert('The model, iy, is updated.'); }  // from-view-to-data binding
      </script>
      
      Trial 1: Let's try the above example, with the followings.


    • Here is a more complex example. Try!