Creating JavaScript Form Controls (Advanced)
  • 23 Dec 2020
  • 4 Minutes to read
  • Dark
    Light
  This documentation version is deprecated, please click here for the latest version.

Creating JavaScript Form Controls (Advanced)

  • Dark
    Light

Article summary

Overview

This article demonstrates how to create a Form Control that receives an input and how to add CSS to the Control. For a basic overview of this topic, please visit the Creating Javascript Form Controls article.

Example

In this example, the control will consist of two buttons: the main button, which increases the click count, and a reset button, which sets the click count back to zero. It'll output two pieces of data: The current click count (since the reset button was clicked), and the total click count (which isn't affected by the reset button).

This control will also have two inputs: the ability to set the text on the main button, and also provide a starting count (in case you want to start counting at a number other than zero)

Take another look at the JS data control interface that will be implemented:

 interface JSBasedComponentInterface {
    initialize(host: JQuery, component: any): void;
    resize(height: number, width: number): void;
    setValue(data: any): void;
    getValue(): any;
  }

Start with a blank text document and name the control class 'MyResetButtonControl'. The initialize function looks like this:

 MyResetButtonControl.prototype.initialize = function(host, component) {
    // Keep track of the host; we'll need it later.
    this.host = host;
    // Initialize our count data:
    this.currentCount = 0;
    this.totalCount = 0;
    // This one is important when we have input data. See 'consumeData' for details.
    this.lastConsumed = null;

    // We're going to put both buttons in a div, then append the div to the host:
    var control = document.createElement('div');

    var button = document.createElement('button');
    button.classList.add('myControlButton');
    button.type = 'button';
    button.textContent = 'Click me';

    var thisControl = this; // To avoid 'this' problems inside the onclick event.

    button.onclick = function(e){
      thisControl.currentCount++;
      thisControl.totalCount++;
    };

    control.appendChild(button);

    var resetButton = document.createElement('button');
    resetButton.classList.add('myControlButton');
    resetButton.type = 'button';
    resetButton.textContent = 'Reset';

    resetButton.onclick = function(e){
      if(thisControl.currentCount > 0){
        thisControl.currentCount = 0;
      }
    };

    control.appendChild(resetButton);

    host.append(control);

    // The two buttons appear too close together unless we add some css:
    var style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = '.myControlButton{ margin: 5px; }';
    document.head.appendChild(style);
  };

The resize function is unchanged, so once again we copy & paste it:

 MyResetButtonControl.prototype.resize = function(height, width) {
    if (this.host && height && width && (height > 0) && (width > 0)) {
      this.host.css({ width: width, height: height });
    }
  };

Next is setValue. Input data is sent into the control with this function - it's called during form initialization and every time the input data changes.

Let's say that the inputs are 'StartingCount' and 'ButtonText':

 MyResetButtonControl.prototype.setValue = function(data) {
    // The lastConsumed variable, declared in 'initialize', is used to track changes in
    //   input data. If data exists, we make sure it has changed since last time:
    if(data && JSON.stringify(data) !== JSON.stringify(this.lastConsumed)){
      // Proceed only if the data is different than last time:
      this.lastConsumed = data;
      // Here, we treat StartingCount as optional, by using a value of 0 if no value
      //   was given:
      var startingCount = data.StartingCount || 0;
      this.currentCount = startingCount;
      this.totalCount = startingCount;
      // We've decided that ButtonText should also be an optional input,
      // so check whether it exists before using it. (If the ButtonText input is
      // set to Ignore, we'll keep the default button text, 'Click me'.)
      if(data.ButtonText){
        // Use JQuery's find function to find the first button on our control:
        this.host.find('button')[0].textContent = data.ButtonText;
      }
    }
  };

Finally, getValue now returns 2 values in its output data:

 MyResetButtonControl.prototype.getValue = function() {
    return {
      CurrentClickCount: this.currentCount,
      TotalClickCount: this.totalCount
    };
  };

Putting it all together:

 function MyResetButtonControl() { };

  MyResetButtonControl.prototype.initialize = function(host, component) {
    // Keep track of the host; we'll need it later.
    this.host = host;
    // Initialize our count data:
    this.currentCount = 0;
    this.totalCount = 0;
    // This one is important when we have input data. See 'consumeData' for details.
    this.lastConsumed = null;

    // We're going to put both buttons in a div, then append the div to the host:
    var control = document.createElement('div');

    var button = document.createElement('button');
    button.classList.add('myControlButton');
    button.type = 'button';
    button.textContent = 'Click me';

    var thisControl = this; // To avoid 'this' problems inside the onclick event.

    button.onclick = function(e){
      thisControl.currentCount++;
      thisControl.totalCount++;
    };

    control.appendChild(button);

    var resetButton = document.createElement('button');
    resetButton.classList.add('myControlButton');
    resetButton.type = 'button';
    resetButton.textContent = 'Reset';

    resetButton.onclick = function(e){
      if(thisControl.currentCount > 0){
        thisControl.currentCount = 0;
      }
    };

    control.appendChild(resetButton);

    host.append(control);

    // The two buttons appear too close together unless we add some css:
    var style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = '.myControlButton{ margin: 5px; }';
    document.head.appendChild(style);
  };
  
  MyResetButtonControl.prototype.resize = function(height, width) {
    if (this.host && height && width && (height > 0) && (width > 0)) {
      this.host.css({ width: width, height: height });
    }
  };
  
  MyResetButtonControl.prototype.setValue = function(data) {
    // The lastConsumed variable, declared in 'initialize', is used to track changes in
    //   input data. If data exists, we make sure it has changed since last time:
    if(data && JSON.stringify(data) !== JSON.stringify(this.lastConsumed)){
      // Proceed only if the data is different than last time:
      this.lastConsumed = data;
      // Here, we treat StartingCount as optional, by using a value of 0 if no value
      //   was given:
      var startingCount = data.StartingCount || 0;
      this.currentCount = startingCount;
      this.totalCount = startingCount;
      // We've decided that ButtonText should also be an optional input,
      // so check whether it exists before using it. (If the ButtonText input is
      // set to Ignore, we'll keep the default button text, 'Click me'.)
      if(data.ButtonText){
        // Use JQuery's find function to find the first button on our control:
        this.host.find('button')[0].textContent = data.ButtonText;
      }
    }
  };
  
  MyResetButtonControl.prototype.getValue = function() {
    return {
      CurrentClickCount: this.currentCount,
      TotalClickCount: this.totalCount
    };
  };

Save as MyJsResetControl.js, and it's ready to be added as a new form control.

Just make sure that the Input Data section has the correct info, in addition to the Output Data section.



Was this article helpful?