Example 2: Simple text input

Introduction

This topic describes the different steps to implement a simple gICAPI-based web component.

In this example, we will implement a simple text editor based on a textarea HTML element.

The dialog code implements a couple of triggers to show how the WEBCOMPONENT field interacts with the program.

The HTML file is described in detail, and complete code example with program and form file is available at the end of this topic.

HTML code description

As any HTML source code, the file starts with the typical HTML tags:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html" charset="utf-8" />
<meta name='viewport' content='initial-scale=1.0, maximum-scale=1.0' />
Note: The "viewport" meta is provided to adjust the viewport size for mobile devices.
A bunch of CSS is added to
<style>
html,body {
  height:100%;
  padding:0;
  margin:0;
  border:0;
  overflow:hidden;
}

textarea#value {
  font-weight: bold;
}

textarea {
  display: block;
  font-family: fixed;
  font-size: 10px;
  padding:0;
  margin:0;
  width: 99%;
}
</style>
The HTML head is then ended with the typical ending tag:
</head>
The body of the HTML file defines a <textarea> element and references the external JavaScript files in <script> elements:
<body>
<textarea id="value"></textarea>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/wc_simple.js"></script>
</body>
Finally, we end the HTML page with the final tag:
</html>

The wc_simple.js file

The JavaScript code implementing the gICAPI web component starts with some global variables. These variables will hold information that must be persistent during the web component life:

var has_focus;

The global function onICHostReady() will be called by the front-end, when the web component interface is ready. The version passed as parameter allows you to check that your component code is compatible with the current gICAPI framework, and to define and assign the gICAPI.on* callback methods (these will be defined in the body of the onICHostReady() function:

var onICHostReady = function(version) {

    if ( version != "1.0" ) {
        alert('Invalid API version');
    }

    ... see below for gICAPI interface functions ...

}

At this point, the gICAPI interface is ready and the gICAPI object can be used.

The gICAPI.onData() function must be implemented to detect web component value changes done in the program, and to acknowledge SetData() calls:

    gICAPI.onData = function(content) {
        $('textarea#value').val(content);
    }
The onFocus() function is used to detect that the web component has got or lost the focus. If the focus is gained, we need to explicitly set the focus to the expected web component element:
    gICAPI.onFocus = function(polarity) {
        has_focus = polarity;
        if (has_focus) {
            $('textarea#value').focus();
        }
    }
Note: The only way to detect that the focus was gained by the web compoment field, is when onFocus(true) is called.
We bind a function to the window.resize() event in order to reset the size of the textarea element:
var wc_resize = function() {
    var h = document.body.clientHeight;
    try {
        var log_height = $('textarea#log').height();
        $('textarea#value').height(h - log_height - 10);
    } catch (err) {
    }
}

...

    $(window).resize(function() {
        wc_resize();
    });
When the textarea element gets the focus, we ask the focus to the runtime system:
    $("textarea#value").on("focus", function() {
        gICAPI.SetFocus();
    });
Implement the gICAPI.onFlushData() function, to provide textarea content, when the front-end needs to send the field value to the runtime system:
    gICAPI.onFlushData = function() {
        gICAPI.SetData( $('textarea#value').val() );
    };
Setup the web component when the field state changes by implementing the gICAPI.onStateChanged() function:
    gICAPI.onStateChanged = function(ps) {
        var params = JSON.parse(ps);
        if ( params.active ) {
            $('textarea#value').attr('disabled', false);
        } else {
            $('textarea#value').attr('disabled', true);
        }
    };
At the end of the onICHostReady() function, we perform an initial resize:
    wc_resize(); // Force adjustment when starting ...
}

Complete source code

File webcomp.per:

LAYOUT (TEXT="Simple web component")
GRID
{
Id:                       [id               ]
[wc                                         ]
[                                           ]
[                                           ]
[                                           ]
[                                           ]
[tx                                         ]
[                                           ]
[                                           ]
[                                           ]
}
END
END

ATTRIBUTES
EDIT id = FORMONLY.id;
WEBCOMPONENT wc = FORMONLY.wc,
  COMPONENTTYPE = "wc_simple",
  SCROLLBARS = NONE,
  STRETCH = BOTH;
TEXTEDIT tx = FORMONLY.info;
END

File webcomp.4gl:

MAIN
    DEFINE rec
        RECORD
            id INTEGER,
            wc STRING,
            info STRING
        END RECORD

    OPTIONS INPUT WRAP, FIELD ORDER FORM

    OPEN FORM f1 FROM "webcomp"
    DISPLAY FORM f1

    LET rec.id = 123
    LET rec.wc = "Hello, world!"

    INPUT BY NAME rec.* ATTRIBUTES(UNBUFFERED, WITHOUT DEFAULTS)
        BEFORE FIELD wc
           LET rec.info = "BEFORE FIELD wc=\n", rec.wc
        ON CHANGE wc
           LET rec.info = "ON CHANGE wc =\n", rec.wc
        AFTER FIELD wc
           LET rec.info = "AFTER FIELD wc =\n", rec.wc
        ON ACTION show
           LET rec.info = "ON ACTION show wc=\n", rec.wc
        ON ACTION disable
           CALL DIALOG.setFieldActive("wc", FALSE)
        ON ACTION enable
           CALL DIALOG.setFieldActive("wc", TRUE)
        ON IDLE 2
           LET rec.info = "ON IDLE wc=\n ", rec.wc
    END INPUT

END MAIN

File wc_simple.html:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html" charset="utf-8" />
<meta name='viewport' content='initial-scale=1.0, maximum-scale=1.0' />

<style>
html,body {
  height:100%;
  padding:0;
  margin:0;
  border:0;
  overflow:hidden;
}

textarea#value {
  font-weight: bold;
}

textarea {
  display: block;
  font-family: fixed;
  font-size: 10px;
  padding:0;
  margin:0;
  width: 99%;
}
</style>
</head>

<body>
<textarea id="value"></textarea>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/wc_simple.js"></script>
</body>

</html>

File wc_simple.js:

var has_focus = false;

var wc_resize = function() {
    var h = document.body.clientHeight;
    try {
        var log_height = $('textarea#log').height();
        $('textarea#value').height(h - log_height - 10);
    } catch (err) {
    }
}

var onICHostReady = function(version) {

    if ( version != "1.0" ) {
        alert('Invalid API version');
    }

    gICAPI.onData = function(content) {
        $('textarea#value').val(content);
    };

    gICAPI.onFocus = function(polarity) {
        has_focus = polarity;
        if (has_focus) {
            $('textarea#value').focus();
        }
    };

    $(window).resize(function() {
        wc_resize();
    });

    $("textarea#value").on("focus", function() {
        gICAPI.SetFocus();
    });

//  $("textarea#value").on("change keyup paste cut", function() {
//      gICAPI.SetData( $(this).val() );
//  });

    gICAPI.onFlushData = function() {
        gICAPI.SetFlushedData( $('textarea#value').val() );
    };

    gICAPI.onStateChanged = function(params) {
        var params = JSON.parse(ps);
        if ( params.active ) {
            $('textarea#value').disabled = false;
        } else {
            $('textarea#value').disabled = true;
        }
    };

    wc_resize(); // Force adjustment when starting ...
}